andlabs-ui/comctl_windows.go

127 lines
3.9 KiB
Go
Raw Normal View History

// 25 february 2014
package ui
import (
"fmt"
"syscall"
"unsafe"
"io/ioutil"
)
// pretty much every constant here except _WM_USER is from commctrl.h, except where noted
var (
comctlManifestCookie uintptr
)
var (
_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) {
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))
// make this context the process default, just to be safe
actctx.dwFlags = _ACTCTX_FLAG_SET_PROCESS_DEFAULT
actctx.lpSource = toUTF16(filename)
r1, _, err := _createActCtx.Call(uintptr(unsafe.Pointer(&actctx)))
// don't negConst() INVALID_HANDLE_VALUE; windowsconstgen was given a pointer by windows.h, and pointers are unsigned, so converting it back to signed doesn't work
if r1 == _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
comctl32 = syscall.NewLazyDLL("comctl32.dll")
r1, _, err := comctl32.NewProc("InitCommonControlsEx").Call(uintptr(unsafe.Pointer(&icc)))
if r1 == _FALSE { // failure
return fmt.Errorf("error initializing Common Controls (comctl32.dll); Windows last error: %v", err)
}
return nil
}
// Common Controls class names.
const (
// x (lowercase) prefix to avoid being caught by the constants generator
x_PROGRESS_CLASS = "msctls_progress32"
)
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>
`)