summaryrefslogtreecommitdiff
path: root/comctl_windows.go
blob: b11c892f05690b862a0070b4265452b16ccfbe22 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// 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))
	// TODO set ACTCTX_FLAG_SET_PROCESS_DEFAULT? I can't find a reference to figure out what this means
	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>
`)