Migrated all Common Controls version 6 initialization on the Windows backend to C.

This commit is contained in:
Pietro Gagliardi 2014-08-12 12:05:23 -04:00
parent 1873b72d49
commit 995fbb2b8f
4 changed files with 58 additions and 77 deletions

View File

@ -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";

View 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>
`)

View File

@ -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)

View File

@ -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);