From 150b999c0a8a19b5f841a32f1add4c663c533f22 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 2 Apr 2014 13:47:11 -0400 Subject: [PATCH] Forced Common Controls version 6 on Windows by embedding the needed manifest in. --- comctl_windows.go | 100 ++++++++++++++++++++++++++++++++++++++++++++-- common_windows.go | 2 +- 2 files changed, 97 insertions(+), 5 deletions(-) diff --git a/comctl_windows.go b/comctl_windows.go index 8e3522a..7c336be 100644 --- a/comctl_windows.go +++ b/comctl_windows.go @@ -4,14 +4,18 @@ package ui import ( "fmt" -// "syscall" + "syscall" "unsafe" + "io/ioutil" ) // pretty much every constant here except _WM_USER is from commctrl.h // TODO for all: filter out constants not available in Windows XP -// TODO ensure that comctl32.dll version 6 or newer is loaded as it provides marquee progress bars +var ( + // TODO deinitialize at program end? + comctlManifestCookie uintptr +) // InitCommonControlsEx constants. const ( @@ -35,18 +39,82 @@ const ( ) var ( - _initCommonControlsEx = comctl32.NewProc("InitCommonControlsEx") + _activateActCtx = kernel32.NewProc("ActivateActCtx") + _createActCtx = kernel32.NewProc("CreateActCtxW") ) +/* +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 +*/ +func forceCommonControls6() (err error) { + var ( + // from winbase.h; var because Go won't let me convert this constant + _INVALID_HANDLE_VALUE = -1 + ) + + 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 actctx struct { + cbSize uint32 + dwFlags uint32 + lpSource *uint16 + wProcessorArchitecture uint16 + wLangId uint16 // originally LANGID + lpAssemblyDirectory uintptr // originally LPCWSTR + lpResourceName uintptr // originally LPCWSTR + lpApplicationName uintptr // originally LPCWSTR + hModule _HANDLE // originally HMODULE + } + + actctx.cbSize = uint32(unsafe.Sizeof(actctx)) + // TODO set ACTCTX_FLAG_SET_PROCESS_DEFAULT? I can't find a reference to figure out what this means + actctx.lpSource = syscall.StringToUTF16Ptr(filename) + + r1, _, err := _createActCtx.Call(uintptr(unsafe.Pointer(&actctx))) + if r1 == uintptr(_INVALID_HANDLE_VALUE) { // failure + return fmt.Errorf("error creating activation context for synthesized manifest file: %v", err) + } + r1, _, err = _activateActCtx.Call( + r1, + uintptr(unsafe.Pointer(&comctlManifestCookie))) + if r1 == uintptr(_FALSE) { // failure + return fmt.Errorf("error activating activation context for synthesized manifest file: %v", err) + } + return nil +} + func initCommonControls() (err error) { var icc struct { dwSize uint32 dwICC uint32 } + err = forceCommonControls6() + if err != nil { + return fmt.Errorf("error forcing Common Controls version 6 (or newer): %v", err) + } + icc.dwSize = uint32(unsafe.Sizeof(icc)) icc.dwICC = _ICC_PROGRESS_CLASS - r1, _, err := _initCommonControlsEx.Call(uintptr(unsafe.Pointer(&icc))) + + comctl32 = syscall.NewLazyDLL("comctl32.dll") + r1, _, err := comctl32.NewProc("InitCommonControlsEx").Call(uintptr(unsafe.Pointer(&icc))) if r1 == _FALSE { // failure // TODO does it set GetLastError()? return fmt.Errorf("error initializing Common Controls (comctl32.dll): %v", err) @@ -87,3 +155,27 @@ const ( _PBM_SETBKCOLOR = _CCM_SETBKCOLOR _PBM_SETMARQUEE = (_WM_USER + 10) ) + +var manifest = []byte(` + + +Your application description here. + + + + + + +`) diff --git a/common_windows.go b/common_windows.go index acc3b2e..522b65a 100644 --- a/common_windows.go +++ b/common_windows.go @@ -11,7 +11,7 @@ var ( user32 = syscall.NewLazyDLL("user32.dll") kernel32 = syscall.NewLazyDLL("kernel32.dll") gdi32 = syscall.NewLazyDLL("gdi32.dll") - comctl32 = syscall.NewLazyDLL("comctl32.dll") + comctl32 *syscall.LazyDLL // comctl32 not defined here; see comctl_windows.go gdiplus = syscall.NewLazyDLL("gdiplus.dll") )