Migrated all Common Controls version 6 initialization on the Windows backend to C.
This commit is contained in:
parent
1873b72d49
commit
995fbb2b8f
|
@ -16,8 +16,24 @@ LRESULT (*WINAPI fv_DefSubclassProc)(HWND, UINT, WPARAM, LPARAM);
|
||||||
ICC_LISTVIEW_CLASSES | /* list views */ \
|
ICC_LISTVIEW_CLASSES | /* list views */ \
|
||||||
0)
|
0)
|
||||||
|
|
||||||
DWORD initCommonControls(LPWSTR manifest, char **errmsg)
|
/* note that this is an 8-bit character string we're writing; see the encoding clause */
|
||||||
|
static const char manifest[] = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\n<assemblyIdentity\n version=\"1.0.0.0\"\n processorArchitecture=\"*\"\n name=\"CompanyName.ProductName.YourApplication\"\n type=\"win32\"\n/>\n<description>Your application description here.</description>\n<dependency>\n <dependentAssembly>\n <assemblyIdentity\n type=\"win32\"\n name=\"Microsoft.Windows.Common-Controls\"\n version=\"6.0.0.0\"\n processorArchitecture=\"*\"\n publicKeyToken=\"6595b64144ccf1df\"\n language=\"*\"\n />\n </dependentAssembly>\n</dependency>\n</assembly>\n";
|
||||||
|
|
||||||
|
/*
|
||||||
|
Windows requires a manifest file to enable Common Controls version 6.
|
||||||
|
The only way to not require an external manifest is to synthesize the manifest ourselves.
|
||||||
|
We can use the activation context API to load it at runtime.
|
||||||
|
References:
|
||||||
|
- http://stackoverflow.com/questions/4308503/how-to-enable-visual-styles-without-a-manifest
|
||||||
|
- http://support.microsoft.com/kb/830033
|
||||||
|
Because neither Go nor MinGW have ways to compile in resources like this (as far as I know), we have to do the work ourselves.
|
||||||
|
*/
|
||||||
|
DWORD initCommonControls(char **errmsg)
|
||||||
{
|
{
|
||||||
|
WCHAR temppath[MAX_PATH + 1];
|
||||||
|
WCHAR filename[MAX_PATH + 1];
|
||||||
|
HANDLE file;
|
||||||
|
DWORD nExpected, nGot;
|
||||||
ACTCTX actctx;
|
ACTCTX actctx;
|
||||||
HANDLE ac;
|
HANDLE ac;
|
||||||
INITCOMMONCONTROLSEX icc;
|
INITCOMMONCONTROLSEX icc;
|
||||||
|
@ -25,10 +41,45 @@ DWORD initCommonControls(LPWSTR manifest, char **errmsg)
|
||||||
/* this is listed as WINAPI in both Microsoft's and MinGW's headers, but not on MSDN for some reason */
|
/* this is listed as WINAPI in both Microsoft's and MinGW's headers, but not on MSDN for some reason */
|
||||||
BOOL (*WINAPI ficc)(const LPINITCOMMONCONTROLSEX);
|
BOOL (*WINAPI ficc)(const LPINITCOMMONCONTROLSEX);
|
||||||
|
|
||||||
|
if (GetTempPathW(MAX_PATH + 1, temppath) == 0) {
|
||||||
|
*errmsg = "error getting temporary path for writing manifest file";
|
||||||
|
return GetLastError();
|
||||||
|
}
|
||||||
|
if (GetTempFileNameW(temppath, L"manifest", 0, filename) == 0) {
|
||||||
|
*errmsg = "error getting temporary filename for writing manifest file";
|
||||||
|
return GetLastError();
|
||||||
|
}
|
||||||
|
file = CreateFileW(filename, GENERIC_WRITE,
|
||||||
|
0, /* don't share while writing */
|
||||||
|
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
if (file == NULL) {
|
||||||
|
*errmsg = "error creating manifest file";
|
||||||
|
return GetLastError();
|
||||||
|
}
|
||||||
|
nExpected = strlen(manifest); /* TODO make static */
|
||||||
|
SetLastError(0); /* catch errorless short writes */
|
||||||
|
if (WriteFile(file, manifest, nExpected, &nGot, NULL) == 0) {
|
||||||
|
*errmsg = "error writing manifest file";
|
||||||
|
return GetLastError();
|
||||||
|
}
|
||||||
|
if (nGot != nExpected) {
|
||||||
|
DWORD lasterr;
|
||||||
|
|
||||||
|
lasterr = GetLastError();
|
||||||
|
*errmsg = "short write to manifest file";
|
||||||
|
if (lasterr == 0)
|
||||||
|
*errmsg = "short write to manifest file without error code";
|
||||||
|
return lasterr;
|
||||||
|
}
|
||||||
|
if (CloseHandle(file) == 0) {
|
||||||
|
*errmsg = "error closing manifest file (this IS an error here because not doing so will prevent Windows from being able to use the manifest file in an activation context)";
|
||||||
|
return GetLastError();
|
||||||
|
}
|
||||||
|
|
||||||
ZeroMemory(&actctx, sizeof (ACTCTX));
|
ZeroMemory(&actctx, sizeof (ACTCTX));
|
||||||
actctx.cbSize = sizeof (ACTCTX);
|
actctx.cbSize = sizeof (ACTCTX);
|
||||||
actctx.dwFlags = ACTCTX_FLAG_SET_PROCESS_DEFAULT;
|
actctx.dwFlags = ACTCTX_FLAG_SET_PROCESS_DEFAULT;
|
||||||
actctx.lpSource = manifest;
|
actctx.lpSource = filename;
|
||||||
ac = CreateActCtx(&actctx);
|
ac = CreateActCtx(&actctx);
|
||||||
if (ac == INVALID_HANDLE_VALUE) {
|
if (ac == INVALID_HANDLE_VALUE) {
|
||||||
*errmsg = "error creating activation context for synthesized manifest file";
|
*errmsg = "error creating activation context for synthesized manifest file";
|
||||||
|
|
|
@ -1,72 +0,0 @@
|
||||||
// 25 february 2014
|
|
||||||
|
|
||||||
package ui
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
// #include "winapi_windows.h"
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
// TODO possibly rewrite the whole file access bits in C
|
|
||||||
|
|
||||||
// pretty much every constant here except _WM_USER is from commctrl.h, except where noted
|
|
||||||
|
|
||||||
/*
|
|
||||||
Windows requires a manifest file to enable Common Controls version 6.
|
|
||||||
The only way to not require an external manifest is to synthesize the manifest ourselves.
|
|
||||||
We can use the activation context API to load it at runtime.
|
|
||||||
References:
|
|
||||||
- http://stackoverflow.com/questions/4308503/how-to-enable-visual-styles-without-a-manifest
|
|
||||||
- http://support.microsoft.com/kb/830033
|
|
||||||
The activation context code itself is in comctl32_windows.c.
|
|
||||||
*/
|
|
||||||
func initCommonControls() (err error) {
|
|
||||||
manifestfile, err := ioutil.TempFile("", "gouicomctl32v6manifest")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error creating synthesized manifest file: %v", err)
|
|
||||||
}
|
|
||||||
_, err = manifestfile.Write(manifest)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error writing synthesized manifest file: %v", err)
|
|
||||||
}
|
|
||||||
filename := manifestfile.Name()
|
|
||||||
// we now have to close the file, otherwise ActivateActCtx() will complain that it's in use by another program
|
|
||||||
// if ioutil.TempFile() ever changes so that the file is deleted when it is closed, this will need to change
|
|
||||||
manifestfile.Close()
|
|
||||||
|
|
||||||
var errmsg *C.char
|
|
||||||
|
|
||||||
errcode := C.initCommonControls(toUTF16(filename), &errmsg)
|
|
||||||
if errcode != 0 || errmsg != nil {
|
|
||||||
return fmt.Errorf("error actually initializing comctl32.dll: %s: %v", C.GoString(errmsg), syscall.Errno(errcode))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var manifest = []byte(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
||||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
|
||||||
<assemblyIdentity
|
|
||||||
version="1.0.0.0"
|
|
||||||
processorArchitecture="*"
|
|
||||||
name="CompanyName.ProductName.YourApplication"
|
|
||||||
type="win32"
|
|
||||||
/>
|
|
||||||
<description>Your application description here.</description>
|
|
||||||
<dependency>
|
|
||||||
<dependentAssembly>
|
|
||||||
<assemblyIdentity
|
|
||||||
type="win32"
|
|
||||||
name="Microsoft.Windows.Common-Controls"
|
|
||||||
version="6.0.0.0"
|
|
||||||
processorArchitecture="*"
|
|
||||||
publicKeyToken="6595b64144ccf1df"
|
|
||||||
language="*"
|
|
||||||
/>
|
|
||||||
</dependentAssembly>
|
|
||||||
</dependency>
|
|
||||||
</assembly>
|
|
||||||
`)
|
|
|
@ -21,8 +21,10 @@ func uiinit() error {
|
||||||
if errcode != 0 || errmsg != nil {
|
if errcode != 0 || errmsg != nil {
|
||||||
return fmt.Errorf("error initializing package ui on Windows: %s: %v", C.GoString(errmsg), syscall.Errno(errcode))
|
return fmt.Errorf("error initializing package ui on Windows: %s: %v", C.GoString(errmsg), syscall.Errno(errcode))
|
||||||
}
|
}
|
||||||
if err := initCommonControls(); err != nil {
|
errmsg = nil
|
||||||
return fmt.Errorf("error initializing comctl32.dll version 6: %v", err)
|
errcode = C.initCommonControls(&errmsg)
|
||||||
|
if errcode != 0 || errmsg != nil {
|
||||||
|
return fmt.Errorf("error initializing comctl32.dll: %s: %v", C.GoString(errmsg), syscall.Errno(errcode))
|
||||||
}
|
}
|
||||||
if err := makemsgwin(); err != nil {
|
if err := makemsgwin(); err != nil {
|
||||||
return fmt.Errorf("error creating message-only window: %v", err)
|
return fmt.Errorf("error creating message-only window: %v", err)
|
||||||
|
|
|
@ -38,7 +38,7 @@ extern HWND msgwin;
|
||||||
extern DWORD makemsgwin(char **);
|
extern DWORD makemsgwin(char **);
|
||||||
|
|
||||||
/* comctl32_windows.c */
|
/* comctl32_windows.c */
|
||||||
extern DWORD initCommonControls(LPWSTR, char **);
|
extern DWORD initCommonControls(char **);
|
||||||
/* these are listed as WINAPI in both Microsoft's and MinGW's headers, but not on MSDN for some reason */
|
/* these are listed as WINAPI in both Microsoft's and MinGW's headers, but not on MSDN for some reason */
|
||||||
extern BOOL (*WINAPI fv_SetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR);
|
extern BOOL (*WINAPI fv_SetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR);
|
||||||
extern BOOL (*WINAPI fv_RemoveWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR);
|
extern BOOL (*WINAPI fv_RemoveWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR);
|
||||||
|
|
Loading…
Reference in New Issue