Forced Common Controls version 6 on Windows by embedding the needed manifest in.

This commit is contained in:
Pietro Gagliardi 2014-04-02 13:47:11 -04:00
parent 43311a668f
commit 150b999c0a
2 changed files with 97 additions and 5 deletions

View File

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

View File

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