summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--comctl_windows.go100
-rw-r--r--common_windows.go2
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(`<?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>
+`)
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")
)