182 lines
5.0 KiB
Go
182 lines
5.0 KiB
Go
// 25 february 2014
|
|
|
|
package ui
|
|
|
|
import (
|
|
"fmt"
|
|
"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
|
|
|
|
var (
|
|
// TODO deinitialize at program end?
|
|
comctlManifestCookie uintptr
|
|
)
|
|
|
|
// InitCommonControlsEx constants.
|
|
const (
|
|
_ICC_LISTVIEW_CLASSES = 0x00000001
|
|
_ICC_TREEVIEW_CLASSES = 0x00000002
|
|
_ICC_BAR_CLASSES = 0x00000004
|
|
_ICC_TAB_CLASSES = 0x00000008
|
|
_ICC_UPDOWN_CLASS = 0x00000010
|
|
_ICC_PROGRESS_CLASS = 0x00000020
|
|
_ICC_HOTKEY_CLASS = 0x00000040
|
|
_ICC_ANIMATE_CLASS = 0x00000080
|
|
_ICC_WIN95_CLASSES = 0x000000FF
|
|
_ICC_DATE_CLASSES = 0x00000100
|
|
_ICC_USEREX_CLASSES = 0x00000200
|
|
_ICC_COOL_CLASSES = 0x00000400
|
|
_ICC_INTERNET_CLASSES = 0x00000800
|
|
_ICC_PAGESCROLLER_CLASS = 0x00001000
|
|
_ICC_NATIVEFNTCTL_CLASS = 0x00002000
|
|
_ICC_STANDARD_CLASSES = 0x00004000
|
|
_ICC_LINK_CLASS = 0x00008000
|
|
)
|
|
|
|
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) {
|
|
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
|
|
|
|
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)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Common Controls class names.
|
|
const (
|
|
_PROGRESS_CLASS = "msctls_progress32"
|
|
)
|
|
|
|
// Shared Common Controls styles.
|
|
const (
|
|
_WM_USER = 0x0400
|
|
_CCM_FIRST = 0x2000
|
|
_CCM_SETBKCOLOR = (_CCM_FIRST + 1)
|
|
)
|
|
|
|
// Progress Bar styles.
|
|
const (
|
|
_PBS_SMOOTH = 0x01
|
|
_PBS_VERTICAL = 0x04
|
|
_PBS_MARQUEE = 0x08
|
|
)
|
|
|
|
// Progress Bar messages.
|
|
const (
|
|
_PBM_SETRANGE = (_WM_USER + 1)
|
|
_PBM_SETPOS = (_WM_USER + 2)
|
|
_PBM_DELTAPOS = (_WM_USER + 3)
|
|
_PBM_SETSTEP = (_WM_USER + 4)
|
|
_PBM_STEPIT = (_WM_USER + 5)
|
|
_PBM_SETRANGE32 = (_WM_USER + 6)
|
|
_PBM_GETRANGE = (_WM_USER + 7)
|
|
_PBM_GETPOS = (_WM_USER + 8)
|
|
_PBM_SETBARCOLOR = (_WM_USER + 9)
|
|
_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>
|
|
`)
|