diff --git a/redo/comctl32_windows.c b/redo/comctl32_windows.c index 09b9605..df1bdcd 100644 --- a/redo/comctl32_windows.c +++ b/redo/comctl32_windows.c @@ -16,8 +16,24 @@ LRESULT (*WINAPI fv_DefSubclassProc)(HWND, UINT, WPARAM, LPARAM); ICC_LISTVIEW_CLASSES | /* list views */ \ 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[] = "\n\n\nYour application description here.\n\n \n \n \n\n\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; HANDLE ac; 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 */ 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)); actctx.cbSize = sizeof (ACTCTX); actctx.dwFlags = ACTCTX_FLAG_SET_PROCESS_DEFAULT; - actctx.lpSource = manifest; + actctx.lpSource = filename; ac = CreateActCtx(&actctx); if (ac == INVALID_HANDLE_VALUE) { *errmsg = "error creating activation context for synthesized manifest file"; diff --git a/redo/comctl32_windows.go b/redo/comctl32_windows.go deleted file mode 100644 index f4a7620..0000000 --- a/redo/comctl32_windows.go +++ /dev/null @@ -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(` - - -Your application description here. - - - - - - -`) diff --git a/redo/uitask_windows.go b/redo/uitask_windows.go index cd2d7dc..288b33f 100644 --- a/redo/uitask_windows.go +++ b/redo/uitask_windows.go @@ -21,8 +21,10 @@ func uiinit() error { if errcode != 0 || errmsg != nil { return fmt.Errorf("error initializing package ui on Windows: %s: %v", C.GoString(errmsg), syscall.Errno(errcode)) } - if err := initCommonControls(); err != nil { - return fmt.Errorf("error initializing comctl32.dll version 6: %v", err) + errmsg = nil + 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 { return fmt.Errorf("error creating message-only window: %v", err) diff --git a/redo/winapi_windows.h b/redo/winapi_windows.h index 268caa3..ca27367 100644 --- a/redo/winapi_windows.h +++ b/redo/winapi_windows.h @@ -38,7 +38,7 @@ extern HWND msgwin; extern DWORD makemsgwin(char **); /* 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 */ extern BOOL (*WINAPI fv_SetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR); extern BOOL (*WINAPI fv_RemoveWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR);