diff --git a/redo/comctl32_windows.go b/redo/comctl32_windows.go new file mode 100644 index 0000000..6b3d6e5 --- /dev/null +++ b/redo/comctl32_windows.go @@ -0,0 +1,132 @@ +// 25 february 2014 + +package ui + +import ( + "fmt" + "io/ioutil" + "syscall" + "unsafe" +) + +// pretty much every constant here except _WM_USER is from commctrl.h, except where noted + +var ( + comctlManifestCookie t_ULONG_PTR +) + +/* +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() 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 s_ACTCTXW + + actctx.cbSize = uint32(unsafe.Sizeof(actctx)) + // make this context the process default, just to be safe + actctx.dwFlags = c_ACTCTX_FLAG_SET_PROCESS_DEFAULT + actctx.lpSource = syscall.StringToUTF16Ptr(filename) + + res, err := f_CreateActCtxW(&actctx) + if res == c_INVALID_HANDLE_VALUE { + return fmt.Errorf("error creating activation context for synthesized manifest file: %v", err) + } + res, err = f_ActivateActCtx(res, &comctlManifestCookie) + if res == c_FALSE { + return fmt.Errorf("error activating activation context for synthesized manifest file: %v", err) + } + return nil +} + +func initCommonControls() (err error) { + var icc s_INITCOMMONCONTROLSEX + + 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 = c_ICC_PROGRESS_CLASS + + // TODO call LoadLibraryW() directly + comctl32 := syscall.NewLazyDLL("comctl32.dll") + r1, _, err := comctl32.NewProc("InitCommonControlsEx").Call(uintptr(unsafe.Pointer(&icc))) + if r1 == c_FALSE { + return fmt.Errorf("error initializing Common Controls (comctl32.dll); Windows last error: %v", err) + } + fv_SetWindowSubclass = comctl32.NewProc("SetWindowSubclass") + fv_RemoveWindowSubclass = comctl32.NewProc("RemoveWindowSubclass") + fv_DefSubclassProc = comctl32.NewProc("DefSubclassProc") + return nil +} + +// Common Controls class names. +const ( + // x (lowercase) prefix to avoid being caught by the constants generator + x_PROGRESS_CLASS = "msctls_progress32" +) + +// this isn't generated by the constants generator because we need to load it from the correct version of comctl32.dll + +var ( + fv_SetWindowSubclass *syscall.LazyProc + fv_RemoveWindowSubclass *syscall.LazyProc + fv_DefSubclassProc *syscall.LazyProc +) + +func f_SetWindowSubclass(hwnd uintptr, subproc uintptr, id t_UINT_PTR, data t_DWORD_PTR) (uintptr, error) { + r1, _, err := fv_SetWindowSubclass.Call(hwnd, subproc, uintptr(id), uintptr(data)) + return r1, err +} + +func f_RemoveWindowSubclass(hwnd uintptr, subproc uintptr, id t_UINT_PTR) (uintptr, error) { + r1, _, err := fv_RemoveWindowSubclass.Call(hwnd, subproc, uintptr(id)) + return r1, err +} + +func f_DefSubclassProc(hwnd uintptr, uMsg t_UINT, wParam t_WPARAM, lParam t_LPARAM, id t_UINT_PTR, data t_DWORD_PTR) t_LRESULT { + r1, _, _ := fv_DefSubclassProc.Call(hwnd, uintptr(uMsg), uintptr(wParam), uintptr(lParam), uintptr(id), uintptr(data)) + return t_LRESULT(r1) +} + +var manifest = []byte(` + + +Your application description here. + + + + + + +`) diff --git a/redo/funcnames_windows.go b/redo/funcnames_windows.go index 005607f..35685db 100644 --- a/redo/funcnames_windows.go +++ b/redo/funcnames_windows.go @@ -39,3 +39,5 @@ package ui // wfunc gdi32 SelectObject uintptr uintptr uintptr // wfunc user32 ReleaseDC uintptr uintptr uintptr // wfunc user32 IsChild uintptr uintptr uintptr,noerr +// wfunc kernel32 CreateActCtxW *s_ACTCTXW uintptr +// wfunc kernel32 ActivateActCtx uintptr *t_ULONG_PTR uintptr diff --git a/redo/uitask_windows.go b/redo/uitask_windows.go index 8e8187a..15f4048 100644 --- a/redo/uitask_windows.go +++ b/redo/uitask_windows.go @@ -20,6 +20,9 @@ func uiinit() error { if err := initWindows(); err != nil { return fmt.Errorf("error initializing package ui on Windows: %v", err) } + if err := initCommonControls(); err != nil { + return fmt.Errorf("error initializing comctl32.dll version 6: %v", err) + } if err := makemsgwin(); err != nil { return fmt.Errorf("error creating message-only window: %v", err) } diff --git a/redo/zwinconstgen.go b/redo/zwinconstgen.go index c0e2171..07a113c 100644 --- a/redo/zwinconstgen.go +++ b/redo/zwinconstgen.go @@ -278,6 +278,8 @@ func main() { fmt.Fprintf(buf, "type t_LRESULT %s\n", winName(reflect.TypeOf(C.LRESULT(0)))) fmt.Fprintf(buf, "type t_UINT_PTR %s\n", winName(reflect.TypeOf(C.UINT_PTR(0)))) fmt.Fprintf(buf, "type t_DWORD_PTR %s\n", winName(reflect.TypeOf(C.DWORD_PTR(0)))) + // and one for initCommonControls() + fmt.Fprintf(buf, "type t_ULONG_PTR %s\n", winName(reflect.TypeOf(C.ULONG_PTR(0)))) // and one for GetMessageW() fmt.Fprintf(buf, "type t_BOOL %s\n", winName(reflect.TypeOf(C.BOOL(0))))