├── go.mod ├── example.gif ├── _resources ├── gopher.ico ├── minimal.syso ├── README.md └── win10.exe.manifest ├── wstr ├── xdoc.go ├── x_decoders_test.go ├── x_encoders_test.go ├── x_utils_test.go ├── decoders.go ├── BufEncoder.go ├── BufDecoder.go └── encoders.go ├── win ├── uxtheme_structs.go ├── user_hbrush.go ├── advapi_structs.go ├── shell_hwnd.go ├── psapi_funcs.go ├── kernel_handle.go ├── user_hprocess.go ├── oleaut_ITypeLib.go ├── kernel_hfile_386.go ├── kernel_hfile_amd64.go ├── advapi_funcs.go ├── gdi_funcs.go ├── gdi_hpalette.go ├── uxtheme_funcs.go ├── uxtheme_hwnd.go ├── shell_IModalWindow.go ├── xdoc.go ├── kernel_funcs_amd64.go ├── ole_common.go ├── ole_hwnd.go ├── user_hcursor.go ├── shell_ITaskbarList4.go ├── kernel_funcs_386.go ├── shell_ITaskbarList2.go ├── user_hwnd_386.go ├── user_hwnd_amd64.go ├── version_structs.go ├── shell_IOleWindow.go ├── user_hmonitor.go ├── gdi_hbrush.go ├── ole_ISequentialStream.go ├── user_haccel.go ├── psapi_structs.go ├── user_hdwp.go ├── gdi_hbitmap.go ├── comctl_unions.go ├── gdi_hgdiobject.go ├── oleaut_bstr.go ├── ole_OleReleaser.go ├── ole_htaskmem.go ├── kernel_hfind.go ├── ole_IDataObject.go ├── dwmapi_funcs.go ├── comctl_hwnd.go ├── oleaut_IPropertyStore.go ├── gdi_hfont.go ├── user_hicon.go ├── oleaut_funcs.go ├── shell_hdrop.go ├── gdi_hpen.go ├── kernel_hinstance.go ├── util_Version.go ├── shell_ITaskbarList.go ├── kernel_hfilemap.go ├── kernel_hactctx.go ├── shell_IFileOpenDialog.go ├── ole_IEnumString.go ├── shell_IShellView.go ├── version_funcs.go └── shell_IFileSaveDialog.go ├── co ├── xdoc.go ├── co_user_amd64.go ├── co_user_386.go ├── co_version.go └── guid.go ├── internal ├── utl │ ├── bool.go │ ├── PtrCache.go │ ├── str.go │ ├── consts.go │ ├── errors.go │ ├── bitops.go │ └── ole.go └── dll │ └── dll.go ├── ui ├── xdoc.go ├── wnd_DlgModal.go ├── ctl_ToolbarButtons.go ├── interfaces.go ├── ctl_ListViewCols.go ├── ctl_TabItems.go ├── wnd_EventsWindowLib.go ├── wnd_DlgMain.go ├── ctl_StatusBarPart.go ├── wnd_DlgControl.go ├── ctl_TreeViewItems.go ├── ctl_TabItem.go ├── wndu_Modal.go ├── ctl_HeaderItems.go ├── globalsPub.go └── ctl_StatusBar.go └── LICENSE.md /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/rodrigocfd/windigo 2 | 3 | go 1.20 4 | -------------------------------------------------------------------------------- /example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rodrigocfd/windigo/HEAD/example.gif -------------------------------------------------------------------------------- /_resources/gopher.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rodrigocfd/windigo/HEAD/_resources/gopher.ico -------------------------------------------------------------------------------- /_resources/minimal.syso: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rodrigocfd/windigo/HEAD/_resources/minimal.syso -------------------------------------------------------------------------------- /wstr/xdoc.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | // This package provides support to convert between Go strings and native, 4 | // null-terminated Windows UTF-16 strings. It's mainly used within the library, 5 | // but it's available if you need this kind of encoding/decoding. 6 | package wstr 7 | -------------------------------------------------------------------------------- /win/uxtheme_structs.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | // [MARGINS] struct. 6 | // 7 | // [MARGINS]: https://learn.microsoft.com/en-us/windows/win32/api/uxtheme/ns-uxtheme-margins 8 | type MARGINS struct { 9 | CxLeftWidth int32 10 | CxRightWidth int32 11 | CyTopHeight int32 12 | CyBottomHeight int32 13 | } 14 | -------------------------------------------------------------------------------- /co/xdoc.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | // This package contains native Win32 constants. All constants have their own 4 | // type, in order to avoid improper mixing. 5 | // 6 | // They are named as close as possible to the original C/C++ declarations, so 7 | // you can use the abundant online documentation. In addition to that, each 8 | // entity has a link to its [official docs], so you can lookup the correct 9 | // usage. 10 | // 11 | // [official docs]: https://learn.microsoft.com/en-us/windows/win32/api/ 12 | package co 13 | -------------------------------------------------------------------------------- /internal/utl/bool.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package utl 4 | 5 | // Syntactic sugar; converts bool to 0 or 1. 6 | func BoolToInt32(b bool) int32 { 7 | if b { 8 | return 1 9 | } 10 | return 0 11 | } 12 | 13 | // Syntactic sugar; converts bool to 0 or 1. 14 | func BoolToUint32(b bool) uint32 { 15 | if b { 16 | return 1 17 | } 18 | return 0 19 | } 20 | 21 | // Syntactic sugar; converts bool to 0 or 1. 22 | func BoolToUintptr(b bool) uintptr { 23 | if b { 24 | return 1 25 | } 26 | return 0 27 | } 28 | -------------------------------------------------------------------------------- /ui/xdoc.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | // This package contains high-level abstractions for GUI windows and controls. 4 | // They are built on top of win and co packages, and attempt to provide a more 5 | // ergonomic way to build GUI applications. 6 | // 7 | // The windows themselves can be built programmatically, or by loading dialog 8 | // resources, which can be manipulated with a WYSIWYG editor like 9 | // [Visual Studio] or [Resource Hacker]. 10 | // 11 | // [Visual Studio]: https://visualstudio.microsoft.com/vs 12 | // [Resource Hacker]: https://www.angusj.com/resourcehacker 13 | package ui 14 | -------------------------------------------------------------------------------- /wstr/x_decoders_test.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package wstr_test 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/rodrigocfd/windigo/wstr" 9 | ) 10 | 11 | func ExampleDecodeArrPtr() { 12 | wideStr := []uint16{'a', 'b', 0, 'c', 'd', 0, 0} 13 | goStr := wstr.DecodeArrPtr(&wideStr[0]) 14 | fmt.Println(goStr) 15 | // Output: [ab cd] 16 | } 17 | 18 | func ExampleDecodePtr() { 19 | wideStr := []uint16{'a', 'b', 0} 20 | goStr := wstr.DecodePtr(&wideStr[0]) 21 | fmt.Println(goStr) 22 | // Output: ab 23 | } 24 | 25 | func ExampleDecodeSlice() { 26 | wideStr := []uint16{'a', 'b', 0} 27 | goStr := wstr.DecodeSlice(wideStr) 28 | fmt.Println(goStr) 29 | // Output: ab 30 | } 31 | -------------------------------------------------------------------------------- /win/user_hbrush.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | 8 | "github.com/rodrigocfd/windigo/co" 9 | "github.com/rodrigocfd/windigo/internal/dll" 10 | ) 11 | 12 | // [GetSysColorBrush] function. 13 | // 14 | // [GetSysColorBrush]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsyscolorbrush 15 | func GetSysColorBrush(index co.COLOR) (HBRUSH, error) { 16 | ret, _, _ := syscall.SyscallN( 17 | dll.Load(dll.USER32, &_GetSysColorBrush, "GetSysColorBrush"), 18 | uintptr(index)) 19 | if ret == 0 { 20 | return HBRUSH(0), co.ERROR_INVALID_PARAMETER 21 | } 22 | return HBRUSH(ret), nil 23 | } 24 | 25 | var _GetSysColorBrush *syscall.Proc 26 | -------------------------------------------------------------------------------- /win/advapi_structs.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "unsafe" 7 | 8 | "github.com/rodrigocfd/windigo/co" 9 | ) 10 | 11 | // [VALENT] struct. 12 | // 13 | // [VALENT]: https://learn.microsoft.com/en-us/windows/win32/api/winreg/ns-winreg-valentw 14 | type _VALENT struct { 15 | ValueName *uint16 16 | ValueLen uint32 17 | ValuePtr uintptr 18 | Type co.REG 19 | } 20 | 21 | // Returns a projection over src, delimited by ValuePtr and ValueLen fields. 22 | func (v *_VALENT) bufProjection(src []byte) []byte { 23 | srcPtrVal := uintptr(unsafe.Pointer(&src[0])) 24 | offsetIdx := v.ValuePtr - srcPtrVal 25 | pastIdx := offsetIdx + uintptr(v.ValueLen) 26 | return src[offsetIdx:pastIdx] 27 | } 28 | -------------------------------------------------------------------------------- /win/shell_hwnd.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | 8 | "github.com/rodrigocfd/windigo/internal/dll" 9 | "github.com/rodrigocfd/windigo/internal/utl" 10 | "github.com/rodrigocfd/windigo/wstr" 11 | ) 12 | 13 | // [ShellAbout] function. 14 | // 15 | // [ShellAbout]: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shellaboutw 16 | func (hWnd HWND) ShellAbout(app, otherStuff string, hIcon HICON) error { 17 | var wApp, wOtherStuff wstr.BufEncoder 18 | ret, _, _ := syscall.SyscallN( 19 | dll.Load(dll.SHELL32, &_ShellAboutW, "ShellAboutW"), 20 | uintptr(hWnd), 21 | uintptr(wApp.AllowEmpty(app)), 22 | uintptr(wOtherStuff.EmptyIsNil(otherStuff)), 23 | uintptr(hIcon)) 24 | return utl.ZeroAsSysInvalidParm(ret) 25 | } 26 | 27 | var _ShellAboutW *syscall.Proc 28 | -------------------------------------------------------------------------------- /win/psapi_funcs.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/dll" 11 | ) 12 | 13 | // [GetPerformanceInfo] function. 14 | // 15 | // [GetPerformanceInfo]: https://learn.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getperformanceinfo 16 | func GetPerformanceInfo() (PERFORMANCE_INFORMATION, error) { 17 | var pi PERFORMANCE_INFORMATION 18 | pi.SetCb() 19 | 20 | ret, _, err := syscall.SyscallN( 21 | dll.Load(dll.PSAPI, &_K32GetPerformanceInfo, "K32GetPerformanceInfo"), 22 | uintptr(unsafe.Pointer(&pi)), 23 | uintptr(unsafe.Sizeof(pi))) 24 | if ret == 0 { 25 | return PERFORMANCE_INFORMATION{}, co.ERROR(err) 26 | } 27 | return pi, nil 28 | } 29 | 30 | var _K32GetPerformanceInfo *syscall.Proc 31 | -------------------------------------------------------------------------------- /win/kernel_handle.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | 8 | "github.com/rodrigocfd/windigo/internal/dll" 9 | "github.com/rodrigocfd/windigo/internal/utl" 10 | ) 11 | 12 | // Handle to an [internal object]. This generic handle is used throughout the 13 | // whole API, with different meanings. 14 | // 15 | // [internal object]: https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#handle 16 | type HANDLE syscall.Handle 17 | 18 | // [CloseHandle] function. 19 | // 20 | // [CloseHandle]: https://learn.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle 21 | func (h HANDLE) CloseHandle() error { 22 | ret, _, err := syscall.SyscallN( 23 | dll.Load(dll.KERNEL32, &_CloseHandle, "CloseHandle"), 24 | uintptr(h)) 25 | return utl.ZeroAsGetLastError(ret, err) 26 | } 27 | 28 | var _CloseHandle *syscall.Proc 29 | -------------------------------------------------------------------------------- /_resources/README.md: -------------------------------------------------------------------------------- 1 | # Resources 2 | 3 | This folder contains several [resources](https://learn.microsoft.com/en-us/windows/win32/menurc/about-resource-files) that can be used to build your native Win32 application: 4 | 5 | | Resource | Description | 6 | | - | - | 7 | | `gopher.ico` | An icon that can be used as default. | 8 | | `win10.exe.manifest` | A basic manifest file that enables your application to be recognized as a Windows 10 one. | 9 | | `minimal.syso` | A syso file, ready to use, that contains the icon and the manifest. Just place it at the root folder of your project. You can load the icon using the resource ID 101. | 10 | 11 | If you wish, you can build your own syso: 12 | 13 | * with the [rsrc](https://github.com/akavel/rsrc) tool; 14 | * creating a `.rc` file from scratch and using a resource compiler, like [MSVC/RC](https://learn.microsoft.com/en-us/windows/win32/menurc/resource-compiler). 15 | -------------------------------------------------------------------------------- /internal/utl/PtrCache.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package utl 4 | 5 | import ( 6 | "sync" 7 | "unsafe" 8 | ) 9 | 10 | type _PtrCache struct { 11 | mutex sync.Mutex 12 | cache map[unsafe.Pointer]struct{} 13 | } 14 | 15 | // A global synchronized cache for Go pointers which must not be collected by 16 | // the GC. 17 | var PtrCache _PtrCache 18 | 19 | // Synchronously adds a new Go pointer to the cache, preventing GC collection. 20 | func (me *_PtrCache) Add(ptr unsafe.Pointer) { 21 | me.mutex.Lock() 22 | defer me.mutex.Unlock() 23 | 24 | if me.cache == nil { 25 | me.cache = make(map[unsafe.Pointer]struct{}) 26 | } 27 | me.cache[ptr] = struct{}{} 28 | } 29 | 30 | // Synchronously deletes the Go pointer from the cache, allowing GC collection. 31 | func (me *_PtrCache) Delete(ptr unsafe.Pointer) { 32 | me.mutex.Lock() 33 | defer me.mutex.Unlock() 34 | 35 | delete(me.cache, ptr) 36 | } 37 | -------------------------------------------------------------------------------- /win/user_hprocess.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/dll" 11 | "github.com/rodrigocfd/windigo/internal/utl" 12 | ) 13 | 14 | // [SetUserObjectInformation] function. 15 | // 16 | // [SetUserObjectInformation]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setuserobjectinformationw 17 | func (hProcess HPROCESS) SetUserObjectInformation( 18 | index co.UOI, 19 | info unsafe.Pointer, 20 | infoLen uintptr, 21 | ) error { 22 | ret, _, err := syscall.SyscallN( 23 | dll.Load(dll.USER32, &_SetUserObjectInformationW, "SetUserObjectInformationW"), 24 | uintptr(hProcess), 25 | uintptr(index), 26 | uintptr(info), 27 | uintptr(infoLen)) 28 | return utl.ZeroAsGetLastError(ret, err) 29 | } 30 | 31 | var _SetUserObjectInformationW *syscall.Proc 32 | -------------------------------------------------------------------------------- /win/oleaut_ITypeLib.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "github.com/rodrigocfd/windigo/co" 7 | ) 8 | 9 | // [ITypeLib] COM interface. 10 | // 11 | // Implements [OleObj] and [OleResource]. 12 | // 13 | // [ITypeLib]: https://learn.microsoft.com/en-us/windows/win32/api/oaidl/nn-oaidl-itypelib 14 | type ITypeLib struct{ IUnknown } 15 | 16 | // Returns the unique COM [interface ID]. 17 | // 18 | // [interface ID]: https://learn.microsoft.com/en-us/office/client-developer/outlook/mapi/iid 19 | func (*ITypeLib) IID() co.IID { 20 | return co.IID_ITypeLib 21 | } 22 | 23 | type _ITypeLibVt struct { 24 | GetTypeInfoCount uintptr 25 | GetTypeInfo uintptr 26 | GetTypeInfoType uintptr 27 | GetTypeInfoOfGuid uintptr 28 | GetLibAttr uintptr 29 | GetTypeComp uintptr 30 | GetDocumentation uintptr 31 | IsName uintptr 32 | FindName uintptr 33 | ReleaseTLibAttr uintptr 34 | } 35 | -------------------------------------------------------------------------------- /co/co_user_amd64.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package co 4 | 5 | // [GetWindowLongPtr] and [SetWindowLongPtr] nIndex. Also includes constants 6 | // with GWL prefix. 7 | // 8 | // [GetWindowLongPtr]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowlongptrw 9 | // [SetWindowLongPtr]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowlongptrw 10 | type GWLP int32 11 | 12 | const ( 13 | GWLP_WNDPROC GWLP = -4 14 | GWLP_HINSTANCE GWLP = -6 15 | GWLP_HWNDPARENT GWLP = -8 16 | GWLP_ID GWLP = -12 17 | GWLP_STYLE GWLP = -16 // Originally has GWL prefix. 18 | GWLP_EXSTYLE GWLP = -20 // Originally has GWL prefix. 19 | GWLP_USERDATA GWLP = -21 20 | 21 | GWLP_DWLP_MSGRESULT GWLP = 0 // Originally has DWLP prefix. 22 | GWLP_DWLP_DLGPROC = GWLP_DWLP_MSGRESULT + 8 // Originally has DWLP prefix. 23 | GWLP_DWLP_USER = GWLP_DWLP_DLGPROC + 8 // Originally has DWLP prefix. 24 | ) 25 | -------------------------------------------------------------------------------- /win/kernel_hfile_386.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/dll" 11 | ) 12 | 13 | // [SetFilePointer] function. 14 | // 15 | // [SetFilePointer]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfilepointer 16 | func (hFile HFILE) SetFilePointerEx( 17 | distanceToMove int, 18 | moveMethod co.FILE_FROM, 19 | ) (newPointerOffset int, wErr error) { 20 | var newOff32 int32 21 | ret, _, err := syscall.SyscallN( 22 | dll.Load(dll.KERNEL32, &_SetFilePointer, "SetFilePointer"), 23 | uintptr(hFile), 24 | uintptr(int32(distanceToMove)), 25 | uintptr(unsafe.Pointer(&newOff32)), 26 | uintptr(moveMethod)) 27 | 28 | if wErr = co.ERROR(err); ret == 0 && wErr != co.ERROR_SUCCESS { 29 | return 0, wErr 30 | } else { 31 | return int(newOff32), nil 32 | } 33 | return 34 | } 35 | 36 | var _SetFilePointer *syscall.Proc 37 | -------------------------------------------------------------------------------- /win/kernel_hfile_amd64.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/dll" 11 | ) 12 | 13 | // [SetFilePointerEx] function. 14 | // 15 | // [SetFilePointerEx]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfilepointerex 16 | func (hFile HFILE) SetFilePointerEx( 17 | distanceToMove int, 18 | moveMethod co.FILE_FROM, 19 | ) (newPointerOffset int, wErr error) { 20 | var newOff64 int64 21 | ret, _, err := syscall.SyscallN( 22 | dll.Load(dll.KERNEL32, &_SetFilePointerEx, "SetFilePointerEx"), 23 | uintptr(hFile), 24 | uintptr(int64(distanceToMove)), 25 | uintptr(unsafe.Pointer(&newOff64)), 26 | uintptr(moveMethod)) 27 | 28 | if wErr = co.ERROR(err); ret == 0 && wErr != co.ERROR_SUCCESS { 29 | return 0, wErr 30 | } else { 31 | return int(newOff64), nil 32 | } 33 | } 34 | 35 | var _SetFilePointerEx *syscall.Proc 36 | -------------------------------------------------------------------------------- /co/co_user_386.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package co 4 | 5 | // [GetWindowLongPtr] and [SetWindowLongPtr] nIndex. Also includes constants 6 | // with GWL prefix. 7 | // 8 | // [GetWindowLongPtr]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowlongptrw 9 | // [SetWindowLongPtr]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowlongptrw 10 | type GWLP int32 11 | 12 | const ( 13 | GWLP_WNDPROC GWLP = -4 // Originally has GWL prefix. 14 | GWLP_HINSTANCE GWLP = -6 // Originally has GWL prefix. 15 | GWLP_HWNDPARENT GWLP = -8 // Originally has GWL prefix. 16 | GWLP_ID GWLP = -12 // Originally has GWL prefix. 17 | GWLP_STYLE GWLP = -16 // Originally has GWL prefix. 18 | GWLP_EXSTYLE GWLP = -20 // Originally has GWL prefix. 19 | GWLP_USERDATA GWLP = -21 // Originally has GWL prefix. 20 | 21 | GWLP_DWLP_MSGRESULT GWLP = 0 // Originally has DWL prefix. 22 | GWLP_DWLP_DLGPROC GWLP = 4 // Originally has DWL prefix. 23 | GWLP_DWLP_USER GWLP = 8 // Originally has DWL prefix. 24 | ) 25 | -------------------------------------------------------------------------------- /win/advapi_funcs.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | 8 | "github.com/rodrigocfd/windigo/internal/dll" 9 | "github.com/rodrigocfd/windigo/internal/utl" 10 | ) 11 | 12 | // [RegDisablePredefinedCache] function. 13 | // 14 | // [RegDisablePredefinedCache]: https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regdisablepredefinedcache 15 | func RegDisablePredefinedCache() error { 16 | ret, _, _ := syscall.SyscallN( 17 | dll.Load(dll.ADVAPI32, &_RegDisablePredefinedCache, "RegDisablePredefinedCache")) 18 | return utl.ZeroAsSysError(ret) 19 | } 20 | 21 | var _RegDisablePredefinedCache *syscall.Proc 22 | 23 | // [RegDisablePredefinedCacheEx] function. 24 | // 25 | // [RegDisablePredefinedCacheEx]: https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regdisablepredefinedcacheex 26 | func RegDisablePredefinedCacheEx() error { 27 | ret, _, _ := syscall.SyscallN( 28 | dll.Load(dll.ADVAPI32, &_RegDisablePredefinedCacheEx, "RegDisablePredefinedCacheEx")) 29 | return utl.ZeroAsSysError(ret) 30 | } 31 | 32 | var _RegDisablePredefinedCacheEx *syscall.Proc 33 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020-present, Rodrigo Cesar de Freitas Dias 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /win/gdi_funcs.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | 8 | "github.com/rodrigocfd/windigo/co" 9 | "github.com/rodrigocfd/windigo/internal/dll" 10 | "github.com/rodrigocfd/windigo/wstr" 11 | ) 12 | 13 | // [AddFontResourceEx] function. 14 | // 15 | // [AddFontResourceEx]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-addfontresourceexw 16 | func AddFontResourceEx(name string, fl co.FR) (int, error) { 17 | var wName wstr.BufEncoder 18 | ret, _, _ := syscall.SyscallN( 19 | dll.Load(dll.GDI32, &_AddFontResourceExW, "AddFontResourceExW"), 20 | uintptr(wName.AllowEmpty(name)), 21 | uintptr(fl), 22 | 0) 23 | if ret == 0 { 24 | return 0, co.ERROR_INVALID_PARAMETER 25 | } 26 | return int(int32(ret)), nil 27 | } 28 | 29 | var _AddFontResourceExW *syscall.Proc 30 | 31 | // [GdiFlush] function. 32 | // 33 | // [GdiFlush]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-gdiflush 34 | func GdiFlush() bool { 35 | ret, _, _ := syscall.SyscallN( 36 | dll.Load(dll.GDI32, &_GdiFlush, "GdiFlush")) 37 | return ret == 0 38 | } 39 | 40 | var _GdiFlush *syscall.Proc 41 | -------------------------------------------------------------------------------- /internal/utl/str.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package utl 4 | 5 | import ( 6 | "strings" 7 | ) 8 | 9 | // "&He && she" becomes "He & she". 10 | func RemoveAccelAmpersands(text string) string { 11 | runes := []rune(text) 12 | var buf strings.Builder 13 | buf.Grow(len(runes)) // prealloc for performance 14 | 15 | for i := 0; i < len(runes)-1; i++ { 16 | if runes[i] == '&' && runes[i+1] != '&' { 17 | continue 18 | } 19 | buf.WriteRune(runes[i]) 20 | } 21 | if runes[len(runes)-1] != '&' { 22 | buf.WriteRune(runes[len(runes)-1]) 23 | } 24 | return buf.String() 25 | } 26 | 27 | // Removes anchor markdowns from SysLink texts. Will fail if '<' char is present 28 | // without delimiting a tag. 29 | func RemoveHtmlAnchor(text string) string { 30 | var buf strings.Builder 31 | buf.Grow(len([]rune(text))) 32 | 33 | withinAnchor := false 34 | for _, ch := range text { 35 | if withinAnchor { 36 | if ch == '>' { 37 | withinAnchor = false 38 | continue 39 | } 40 | } 41 | if ch == '<' { 42 | withinAnchor = true 43 | continue 44 | } 45 | if !withinAnchor { 46 | buf.WriteRune(ch) 47 | } 48 | } 49 | return buf.String() 50 | } 51 | -------------------------------------------------------------------------------- /win/gdi_hpalette.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/internal/dll" 10 | "github.com/rodrigocfd/windigo/internal/utl" 11 | ) 12 | 13 | // Handle to a 14 | // [palette](https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#hpalette). 15 | type HPALETTE HANDLE 16 | 17 | // [AnimatePalette] function. 18 | // 19 | // [AnimatePalette]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-animatepalette 20 | func (hPal HPALETTE) AnimatePalette(startIndex int, entries []PALETTEENTRY) error { 21 | ret, _, _ := syscall.SyscallN( 22 | dll.Load(dll.GDI32, &_AnimatePalette, "AnimatePalette"), 23 | uintptr(hPal), 24 | uintptr(uint32(startIndex)), 25 | uintptr(uint32(len(entries))), 26 | uintptr(unsafe.Pointer(&entries[0]))) 27 | return utl.ZeroAsSysInvalidParm(ret) 28 | } 29 | 30 | var _AnimatePalette *syscall.Proc 31 | 32 | // [DeleteObject] function. 33 | // 34 | // [DeleteObject]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-deleteobject 35 | func (hPal HPALETTE) DeleteObject() error { 36 | return HGDIOBJ(hPal).DeleteObject() 37 | } 38 | -------------------------------------------------------------------------------- /_resources/win10.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 23 | 24 | 25 | 26 | 27 | true 28 | 29 | 30 | -------------------------------------------------------------------------------- /win/uxtheme_funcs.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | 8 | "github.com/rodrigocfd/windigo/internal/dll" 9 | ) 10 | 11 | // [IsAppThemed] function. 12 | // 13 | // [IsAppThemed]: https://learn.microsoft.com/en-us/windows/win32/api/uxtheme/nf-uxtheme-isappthemed 14 | func IsAppThemed() bool { 15 | ret, _, _ := syscall.SyscallN( 16 | dll.Load(dll.UXTHEME, &_IsAppThemed, "IsAppThemed")) 17 | return ret != 0 18 | } 19 | 20 | var _IsAppThemed *syscall.Proc 21 | 22 | // [IsCompositionActive] function. 23 | // 24 | // [IsCompositionActive]: https://learn.microsoft.com/en-us/windows/win32/api/uxtheme/nf-uxtheme-iscompositionactive 25 | func IsCompositionActive() bool { 26 | ret, _, _ := syscall.SyscallN( 27 | dll.Load(dll.UXTHEME, &_IsCompositionActive, "IsCompositionActive")) 28 | return ret != 0 29 | } 30 | 31 | var _IsCompositionActive *syscall.Proc 32 | 33 | // [IsThemeActive] function. 34 | // 35 | // [IsThemeActive]: https://learn.microsoft.com/en-us/windows/win32/api/uxtheme/nf-uxtheme-isthemeactive 36 | func IsThemeActive() bool { 37 | ret, _, _ := syscall.SyscallN( 38 | dll.Load(dll.UXTHEME, &_IsThemeActive, "IsThemeActive")) 39 | return ret != 0 40 | } 41 | 42 | var _IsThemeActive *syscall.Proc 43 | -------------------------------------------------------------------------------- /ui/wnd_DlgModal.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ui 4 | 5 | import ( 6 | "github.com/rodrigocfd/windigo/co" 7 | "github.com/rodrigocfd/windigo/win" 8 | ) 9 | 10 | // Dialog-based modal window. 11 | type _DlgModal struct { 12 | _DlgBase 13 | parent Parent 14 | } 15 | 16 | func newModalDlg(parent Parent, dlgId uint16) *_DlgModal { 17 | me := &_DlgModal{ 18 | _DlgBase: newBaseDlg(dlgId), 19 | parent: parent, 20 | } 21 | me.defaultMessageHandlers() 22 | return me 23 | } 24 | 25 | func (me *_DlgModal) showModal() { 26 | hInst, _ := me.parent.Hwnd().HInstance() 27 | me.dialogBoxParam(hInst, me.parent.Hwnd()) 28 | } 29 | 30 | func (me *_DlgModal) defaultMessageHandlers() { 31 | me._DlgBase._BaseContainer.defaultMessageHandlers() 32 | 33 | me.beforeUserEvents.wmCreateOrInitdialog(func() { 34 | rcModal, _ := me.hWnd.GetWindowRect() 35 | rcParent, _ := me.parent.Hwnd().GetWindowRect() 36 | 37 | x := rcParent.Left + ((rcParent.Right - rcParent.Left) / 2) - (rcModal.Right-rcModal.Left)/2 38 | y := rcParent.Top + ((rcParent.Bottom - rcParent.Top) / 2) - (rcModal.Bottom-rcModal.Top)/2 39 | 40 | me.hWnd.SetWindowPos(win.HWND(0), int(x), int(y), 0, 0, co.SWP_NOSIZE|co.SWP_NOZORDER) 41 | }) 42 | 43 | me.userEvents.WmClose(func() { 44 | me.hWnd.EndDialog(0) 45 | }) 46 | } 47 | -------------------------------------------------------------------------------- /win/uxtheme_hwnd.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | 8 | "github.com/rodrigocfd/windigo/co" 9 | "github.com/rodrigocfd/windigo/internal/dll" 10 | "github.com/rodrigocfd/windigo/wstr" 11 | ) 12 | 13 | // [IsThemeDialogTextureEnabled] function. 14 | // 15 | // [IsThemeDialogTextureEnabled]: https://learn.microsoft.com/en-us/windows/win32/api/uxtheme/nf-uxtheme-isthemedialogtextureenabled 16 | func (hWnd HWND) IsThemeDialogTextureEnabled() bool { 17 | ret, _, _ := syscall.SyscallN( 18 | dll.Load(dll.UXTHEME, &_IsThemeDialogTextureEnabled, "IsThemeDialogTextureEnabled"), 19 | uintptr(hWnd)) 20 | return ret != 0 21 | } 22 | 23 | var _IsThemeDialogTextureEnabled *syscall.Proc 24 | 25 | // [OpenThemeData] function. 26 | // 27 | // ⚠️ You must defer [HTHEME.CloseThemeData]. 28 | // 29 | // [OpenThemeData]: https://learn.microsoft.com/en-us/windows/win32/api/uxtheme/nf-uxtheme-openthemedata 30 | func (hWnd HWND) OpenThemeData(classNames string) (HTHEME, error) { 31 | var wClassNames wstr.BufEncoder 32 | ret, _, _ := syscall.SyscallN( 33 | dll.Load(dll.UXTHEME, &_OpenThemeData, "OpenThemeData"), 34 | uintptr(hWnd), 35 | uintptr(wClassNames.EmptyIsNil(classNames))) 36 | if ret == 0 { 37 | return HTHEME(0), co.HRESULT_E_FAIL 38 | } 39 | return HTHEME(ret), nil 40 | } 41 | 42 | var _OpenThemeData *syscall.Proc 43 | -------------------------------------------------------------------------------- /wstr/x_encoders_test.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package wstr_test 4 | 5 | import ( 6 | "fmt" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/wstr" 10 | ) 11 | 12 | func ExampleEncodeArrToBuf() { 13 | destBuf := make([]uint16, 7) 14 | goStrs := []string{"ab", "cd"} 15 | wstr.EncodeArrToBuf(destBuf, goStrs...) 16 | fmt.Println(destBuf) 17 | // Output: [97 98 0 99 100 0 0] 18 | } 19 | 20 | func ExampleEncodeArrToPtr() { 21 | goStrs := []string{"ab", "cd"} 22 | pBuf := wstr.EncodeArrToPtr(goStrs...) 23 | sliceBuf := unsafe.Slice(pBuf, 7) 24 | fmt.Println(sliceBuf) 25 | // Output: [97 98 0 99 100 0 0] 26 | } 27 | 28 | func ExampleEncodeArrToSlice() { 29 | goStrs := []string{"ab", "cd"} 30 | buf := wstr.EncodeArrToSlice(goStrs...) 31 | fmt.Println(buf) 32 | // Output: [97 98 0 99 100 0 0] 33 | } 34 | 35 | func ExampleEncodeToBuf() { 36 | destBuf := make([]uint16, 3) 37 | goStr := "ab" 38 | wstr.EncodeToBuf(destBuf, goStr) 39 | fmt.Println(destBuf) 40 | // Output: [97 98 0] 41 | } 42 | 43 | func ExampleEncodeToPtr() { 44 | goStr := "ab" 45 | pBuf := wstr.EncodeArrToPtr(goStr) 46 | sliceBuf := unsafe.Slice(pBuf, 3) 47 | fmt.Println(sliceBuf) 48 | // Output: [97 98 0] 49 | } 50 | 51 | func ExampleEncodeToSlice() { 52 | goStr := "ab" 53 | buf := wstr.EncodeToSlice(goStr) 54 | fmt.Println(buf) 55 | // Output: [97 98 0] 56 | } 57 | -------------------------------------------------------------------------------- /win/shell_IModalWindow.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | ) 11 | 12 | // [IModalWindow] COM interface. 13 | // 14 | // Implements [OleObj] and [OleResource]. 15 | // 16 | // [IModalWindow]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-imodalwindow 17 | type IModalWindow struct{ IUnknown } 18 | 19 | // Returns the unique COM [interface ID]. 20 | // 21 | // [interface ID]: https://learn.microsoft.com/en-us/office/client-developer/outlook/mapi/iid 22 | func (*IModalWindow) IID() co.IID { 23 | return co.IID_IModalWindow 24 | } 25 | 26 | // [Show] method. 27 | // 28 | // Returns false if user cancelled. 29 | // 30 | // [Show]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-imodalwindow-show 31 | func (me *IModalWindow) Show(hwndOwner HWND) (bool, error) { 32 | ret, _, _ := syscall.SyscallN( 33 | (*_IModalWindowVt)(unsafe.Pointer(*me.Ppvt())).Show, 34 | uintptr(unsafe.Pointer(me.Ppvt())), 35 | uintptr(hwndOwner)) 36 | 37 | if wErr := co.ERROR(ret); wErr == co.ERROR_SUCCESS { 38 | return true, nil 39 | } else if wErr == co.ERROR_CANCELLED { 40 | return false, nil 41 | } else { 42 | return false, wErr.ToHresult() 43 | } 44 | } 45 | 46 | type _IModalWindowVt struct { 47 | _IUnknownVt 48 | Show uintptr 49 | } 50 | -------------------------------------------------------------------------------- /win/xdoc.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | // This package contains native Win32 functions, structs and handles. They are 4 | // implemented as close as possible to the original C/C++ declarations, so you 5 | // can use the abundant online documentation. In addition to that, each entity 6 | // has a link to its [official docs], so you can lookup the correct usage. 7 | // 8 | // Functions whose first parameter is a handle are implemented as a method of 9 | // that handle. For example, [MessageBox] function's first parameter is a HWND, 10 | // so MessageBox is a method of HWND: 11 | // 12 | // var hwnd win.HWND // initialized somewhere 13 | // 14 | // hwnd.MessageBox("Saying hello", "Hello", co.MB_ICONINFORMATION) 15 | // 16 | // All other functions are just regular free functions. 17 | // 18 | // Some structs, like [WNDCLASSEX], require the initialization of a cbSize field 19 | // (or other similar name) with the size of the struct itself. Each one of these 20 | // structs is properly documented, and they have a specific method for this, for 21 | // example: 22 | // 23 | // var wcx win.WNDCLASSEX 24 | // wcx.SetCbSize() 25 | // 26 | // All constants are declared in the co package. 27 | // 28 | // [MessageBox]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messageboxw 29 | // [WNDCLASSEX]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-wndclassexw 30 | // [official docs]: https://learn.microsoft.com/en-us/windows/win32/api/ 31 | package win 32 | -------------------------------------------------------------------------------- /internal/utl/consts.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package utl 4 | 5 | // Internal constants for comctl. 6 | const ( 7 | L_MAX_URL_LENGTH = 2048 + 32 + 4 8 | MAX_LINKID_TEXT = 48 9 | ) 10 | 11 | // Internal constants for gdi. 12 | const ( 13 | CBM_INIT = 0x04 14 | CCHFORMNAME = 32 15 | CLR_INVALID = 0xffff_ffff 16 | DM_SPECVERSION = 0x0401 17 | GDI_ERR = 0xffff_ffff 18 | HGDI_ERROR = 0xffff_ffff 19 | HIMETRIC_PER_INCH = 2540 20 | LF_FACESIZE = 32 21 | MAXLONG = 0x7fff_ffff 22 | REGION_ERROR = 0 23 | THREAD_PRIORITY_ERROR_RETURN = MAXLONG 24 | ) 25 | 26 | // Internal constants for kernel. 27 | const ( 28 | GMEM_INVALID_HANDLE = 0x8000 29 | HEAP_OPTIMIZE_RESOURCES_CURRENT_VERSION = 1 30 | INFINITE = 0xffff_ffff 31 | INVALID_HANDLE_VALUE = -1 32 | LMEM_INVALID_HANDLE = 0x8000 33 | MAX_MODULE_NAME32 = 255 34 | MAX_PATH = 260 35 | TIME_ZONE_INVALID = 0xffff_ffff 36 | ) 37 | 38 | // Internal constants for shell. 39 | const ( 40 | INFOTIPSIZE = 1024 41 | ) 42 | 43 | // Internal constants for user. 44 | const ( 45 | CB_ERR = int32(-1) 46 | CCHDEVICENAME = 32 47 | CCHILDREN_TITLEBAR = 5 48 | WC_DIALOG = uint16(0x8002) 49 | ) 50 | -------------------------------------------------------------------------------- /win/kernel_funcs_amd64.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/dll" 11 | ) 12 | 13 | // [VerifyVersionInfo] function. 14 | // 15 | // [VerifyVersionInfo]: https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-verifyversioninfow 16 | func VerifyVersionInfo(ovi *OSVERSIONINFOEX, typeMask co.VER, conditionMask uint64) (bool, error) { 17 | ovi.SetDwOsVersionInfoSize() // safety 18 | 19 | ret, _, err := syscall.SyscallN( 20 | dll.Load(dll.KERNEL32, &_VerifyVersionInfoW, "VerifyVersionInfoW"), 21 | uintptr(unsafe.Pointer(ovi)), 22 | uintptr(typeMask), 23 | uintptr(conditionMask)) 24 | 25 | if wErr := co.ERROR(err); ret == 0 && wErr == co.ERROR_OLD_WIN_VERSION { 26 | return false, nil 27 | } else if ret == 0 { 28 | return false, wErr // actual error 29 | } else { 30 | return true, nil 31 | } 32 | } 33 | 34 | var _VerifyVersionInfoW *syscall.Proc 35 | 36 | // [VerSetConditionMask] function. 37 | // 38 | // [VerSetConditionMask]: https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-versetconditionmask 39 | func VerSetConditionMask(conditionMask uint64, typeMask co.VER, condition co.VER_COND) uint64 { 40 | ret, _, _ := syscall.SyscallN( 41 | dll.Load(dll.KERNEL32, &_VerSetConditionMask, "VerSetConditionMask"), 42 | uintptr(conditionMask), 43 | uintptr(typeMask), 44 | uintptr(condition)) 45 | return uint64(ret) 46 | } 47 | 48 | var _VerSetConditionMask *syscall.Proc 49 | -------------------------------------------------------------------------------- /wstr/x_utils_test.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package wstr_test 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/rodrigocfd/windigo/wstr" 9 | ) 10 | 11 | func ExampleCmp() { 12 | compare1 := wstr.Cmp("aa", "bb") 13 | compare2 := wstr.Cmp("bb", "aa") 14 | compare3 := wstr.Cmp("aa", "aa") 15 | fmt.Println(compare1, compare2, compare3) 16 | // Output: -1 1 0 17 | } 18 | 19 | func ExampleCmpI() { 20 | compare1 := wstr.CmpI("aa", "BB") 21 | compare2 := wstr.CmpI("bb", "AA") 22 | compare3 := wstr.CmpI("aa", "AA") 23 | fmt.Println(compare1, compare2, compare3) 24 | // Output: -1 1 0 25 | } 26 | 27 | func ExampleCountRunes() { 28 | c1 := wstr.CountRunes("foo") 29 | c2 := wstr.CountRunes("🙂") 30 | fmt.Println(c1, c2) 31 | // Output: 3 1 32 | } 33 | 34 | func ExampleCountUtf16Len() { 35 | c1 := wstr.CountUtf16Len("foo") 36 | c2 := wstr.CountUtf16Len("🙂") 37 | fmt.Println(c1, c2) 38 | // Output: 3 2 39 | } 40 | 41 | func ExampleFmtBytes() { 42 | s := wstr.FmtBytes(2 * 1024 * 1024) 43 | fmt.Println(s) 44 | // Output: 2.00 MB 45 | } 46 | 47 | func ExampleFmtThousands() { 48 | s := wstr.FmtThousands(2000) 49 | fmt.Println(s) 50 | // Output: 2,000 51 | } 52 | 53 | func ExampleRemoveDiacritics() { 54 | s := wstr.RemoveDiacritics("Éçãos") 55 | fmt.Println(s) 56 | // Output: Ecaos 57 | } 58 | 59 | func ExampleSplitLines() { 60 | lines := wstr.SplitLines("ab\ncd") 61 | fmt.Println(lines) 62 | // Output: [ab cd] 63 | } 64 | 65 | func ExampleSubstrRunes() { 66 | s := wstr.SubstrRunes("ab🙂cd", 1, 3) 67 | fmt.Println(s) 68 | // Output: b🙂c 69 | } 70 | -------------------------------------------------------------------------------- /internal/utl/errors.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package utl 4 | 5 | import ( 6 | "fmt" 7 | "syscall" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | ) 11 | 12 | // Panics if any of the numbers is negative. 13 | func PanicNeg(nums ...int) { 14 | for _, n := range nums { 15 | if n < 0 { 16 | panic(fmt.Sprintf("Value cannot be negative: %d.", n)) 17 | } 18 | } 19 | } 20 | 21 | // Error handling syntactic sugar for syscalls which call GetLastError(). 22 | func ZeroAsGetLastError(ret uintptr, err syscall.Errno) error { 23 | if ret == 0 { 24 | return co.ERROR(err) 25 | } 26 | return nil 27 | } 28 | 29 | // Error handling syntactic sugar for syscalls directly returning a standard 30 | // Windows error. 31 | func ZeroAsSysError(ret uintptr) error { 32 | if wErr := co.ERROR(ret); wErr != co.ERROR_SUCCESS { 33 | return wErr 34 | } 35 | return nil 36 | } 37 | 38 | // Error handling syntactic sugar for syscalls returning the standard 39 | // co.ERROR_INVALID_PARAMETER. 40 | func ZeroAsSysInvalidParm(ret uintptr) error { 41 | if ret == 0 { 42 | return co.ERROR_INVALID_PARAMETER 43 | } 44 | return nil 45 | } 46 | 47 | // Error handling syntactic sugar for syscalls returning -1. 48 | func Minus1AsSysInvalidParm(ret uintptr) error { 49 | if int32(ret) == -1 { 50 | return co.ERROR_INVALID_PARAMETER 51 | } 52 | return nil 53 | } 54 | 55 | // Error handling syntactic sugar for syscalls directly returning an HRESULT. 56 | func ErrorAsHResult(ret uintptr) error { 57 | if hr := co.HRESULT(ret); hr != co.HRESULT_S_OK { 58 | return hr 59 | } 60 | return nil 61 | } 62 | -------------------------------------------------------------------------------- /win/ole_common.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "unsafe" 7 | 8 | "github.com/rodrigocfd/windigo/co" 9 | "github.com/rodrigocfd/windigo/internal/utl" 10 | ) 11 | 12 | // A [COM] object whose lifetime can be managed by an [OleReleaser], automating 13 | // the cleanup. 14 | // 15 | // [COM]: https://learn.microsoft.com/en-us/windows/win32/com/component-object-model--com--portal 16 | type OleResource interface { 17 | release() 18 | } 19 | 20 | // A [COM] object, derived from [IUnknown]. 21 | // 22 | // [COM]: https://learn.microsoft.com/en-us/windows/win32/com/component-object-model--com--portal 23 | // [IUnknown]: https://learn.microsoft.com/en-us/windows/win32/api/unknwn/nn-unknwn-iunknown 24 | type OleObj interface { 25 | OleResource 26 | 27 | // Returns the unique [COM] [interface ID]. 28 | // 29 | // [COM]: https://learn.microsoft.com/en-us/windows/win32/com/component-object-model--com--portal 30 | // [interface ID]: https://learn.microsoft.com/en-us/office/client-developer/outlook/mapi/iid 31 | IID() co.IID 32 | 33 | // Returns the [COM] virtual table pointer. 34 | // 35 | // This is a low-level method, used internally by the library. Incorrect usage 36 | // may lead to segmentation faults. 37 | // 38 | // [COM]: https://learn.microsoft.com/en-us/windows/win32/com/component-object-model--com--portal 39 | Ppvt() **_IUnknownVt 40 | } 41 | 42 | // Returns the virtual table pointer, performing a nil check. 43 | func ppvtOrNil(obj OleObj) unsafe.Pointer { 44 | if !utl.IsNil(obj) { 45 | return unsafe.Pointer(obj.Ppvt()) 46 | } 47 | return nil 48 | } 49 | -------------------------------------------------------------------------------- /win/ole_hwnd.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "errors" 7 | "syscall" 8 | "unsafe" 9 | 10 | "github.com/rodrigocfd/windigo/co" 11 | "github.com/rodrigocfd/windigo/internal/dll" 12 | "github.com/rodrigocfd/windigo/internal/utl" 13 | ) 14 | 15 | // [RegisterDragDrop] function. 16 | // 17 | // Paired with [HWND.RevokeDragDrop]. 18 | // 19 | // [RegisterDragDrop]: https://learn.microsoft.com/en-us/windows/win32/api/ole2/nf-ole2-registerdragdrop 20 | func (hWnd HWND) RegisterDragDrop(dropTarget *IDropTarget) error { 21 | exStyle, _ := hWnd.ExStyle() 22 | if (exStyle & co.WS_EX_ACCEPTFILES) != 0 { 23 | return errors.New("do not use WS_EX_ACCEPTFILES with RegisterDragDrop") 24 | } 25 | 26 | ret, _, _ := syscall.SyscallN( 27 | dll.Load(dll.OLE32, &_RegisterDragDrop, "RegisterDragDrop"), 28 | uintptr(hWnd), 29 | uintptr(unsafe.Pointer(dropTarget.Ppvt()))) 30 | if hr := co.HRESULT(ret); hr != co.HRESULT_S_OK { 31 | if hr == co.HRESULT_E_OUTOFMEMORY { 32 | return errors.New("RegisterDragDrop failed, did you call OleInitialize?") 33 | } 34 | return hr 35 | } 36 | return nil 37 | } 38 | 39 | var _RegisterDragDrop *syscall.Proc 40 | 41 | // [RevokeDragDrop] function. 42 | // 43 | // Paired with [HWND.RegisterDragDrop]. 44 | // 45 | // [RevokeDragDrop]: https://learn.microsoft.com/en-us/windows/win32/api/ole/nf-ole-revokedragdrop 46 | func (hWnd HWND) RevokeDragDrop() error { 47 | ret, _, _ := syscall.SyscallN( 48 | dll.Load(dll.OLE32, &_RevokeDragDrop, "RevokeDragDrop"), 49 | uintptr(hWnd)) 50 | return utl.ErrorAsHResult(ret) 51 | } 52 | 53 | var _RevokeDragDrop *syscall.Proc 54 | -------------------------------------------------------------------------------- /win/user_hcursor.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | 8 | "github.com/rodrigocfd/windigo/co" 9 | "github.com/rodrigocfd/windigo/internal/dll" 10 | "github.com/rodrigocfd/windigo/internal/utl" 11 | ) 12 | 13 | // Handle to a [cursor]. 14 | // 15 | // [cursor]: https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#hcursor 16 | type HCURSOR HANDLE 17 | 18 | // [CopyCursor] function. 19 | // 20 | // [CopyCursor]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-copycursor 21 | func (hCursor HCURSOR) CopyCursor() (HCURSOR, error) { 22 | hIcon, err := HICON(hCursor).CopyIcon() 23 | return HCURSOR(hIcon), err 24 | } 25 | 26 | // [DestroyCursor] function. 27 | // 28 | // [DestroyCursor]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-destroycursor 29 | func (hCursor HCURSOR) DestroyCursor() error { 30 | ret, _, err := syscall.SyscallN( 31 | dll.Load(dll.USER32, &_DestroyCursor, "DestroyCursor"), 32 | uintptr(hCursor)) 33 | return utl.ZeroAsGetLastError(ret, err) 34 | } 35 | 36 | var _DestroyCursor *syscall.Proc 37 | 38 | // [SetCursor] function. 39 | // 40 | // [SetCursor]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setcursor 41 | func (hCursor HCURSOR) SetCursor() (HCURSOR, error) { 42 | ret, _, err := syscall.SyscallN( 43 | dll.Load(dll.USER32, &_SetCursor, "SetCursor"), 44 | uintptr(hCursor)) 45 | if wErr := co.ERROR(err); wErr != co.ERROR_SUCCESS { 46 | return HCURSOR(0), err 47 | } else { 48 | return HCURSOR(ret), nil 49 | } 50 | } 51 | 52 | var _SetCursor *syscall.Proc 53 | -------------------------------------------------------------------------------- /ui/ctl_ToolbarButtons.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ui 4 | 5 | import ( 6 | "fmt" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/win" 11 | "github.com/rodrigocfd/windigo/wstr" 12 | ) 13 | 14 | // The buttons collection 15 | // 16 | // You cannot create this object directly, it will be created automatically 17 | // by the owning [Toolbar]. 18 | type CollectionToolbarButtons struct { 19 | owner *Toolbar 20 | } 21 | 22 | // Adds a new button with [TB_ADDBUTTONS]. 23 | // 24 | // The iconIndex is the zero-based index of the icon previously inserted into 25 | // the control's image list. 26 | // 27 | // [TB_ADDBUTTONS]: https://learn.microsoft.com/en-us/windows/win32/controls/tb-addbuttons 28 | func (me *CollectionToolbarButtons) Add(cmdId uint16, text string, iconIndex int) { 29 | var wText wstr.BufEncoder 30 | 31 | tbb := win.TBBUTTON{ 32 | IBitmap: int32(iconIndex), 33 | IdCommand: int32(cmdId), 34 | FsStyle: co.BTNS_AUTOSIZE, 35 | FsState: co.TBSTATE_ENABLED, 36 | IString: (*uint16)(wText.AllowEmpty(text)), 37 | } 38 | 39 | ret, _ := me.owner.hWnd.SendMessage(co.TB_ADDBUTTONS, 40 | 1, win.LPARAM(unsafe.Pointer(&tbb))) 41 | if ret == 0 { 42 | panic(fmt.Sprintf("TB_ADDBUTTONS \"%s\" failed.", text)) 43 | } 44 | 45 | me.owner.hWnd.SendMessage(co.TB_AUTOSIZE, 0, 0) 46 | } 47 | 48 | // Retrieves the number of buttons with [TB_BUTTONCOUNT]. 49 | // 50 | // [TB_BUTTONCOUNT]: https://learn.microsoft.com/en-us/windows/win32/controls/tb-buttoncount 51 | func (me *CollectionToolbarButtons) Count() int { 52 | ret, _ := me.owner.hWnd.SendMessage(co.TB_BUTTONCOUNT, 0, 0) 53 | return int(ret) 54 | } 55 | -------------------------------------------------------------------------------- /win/shell_ITaskbarList4.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/utl" 11 | ) 12 | 13 | // [ITaskbarList4] COM interface. 14 | // 15 | // Implements [OleObj] and [OleResource]. 16 | // 17 | // Example: 18 | // 19 | // _, _ = win.CoInitializeEx( 20 | // co.COINIT_APARTMENTTHREADED | co.COINIT_DISABLE_OLE1DDE) 21 | // defer win.CoUninitialize() 22 | // 23 | // rel := win.NewOleReleaser() 24 | // defer rel.Release() 25 | // 26 | // var taskbl *win.ITaskbarList4 27 | // _ = win.CoCreateInstance( 28 | // rel, 29 | // co.CLSID_TaskbarList, 30 | // nil, 31 | // co.CLSCTX_INPROC_SERVER, 32 | // &taskbl, 33 | // ) 34 | // 35 | // [ITaskbarList4]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-itaskbarlist4 36 | type ITaskbarList4 struct{ ITaskbarList3 } 37 | 38 | // Returns the unique COM [interface ID]. 39 | // 40 | // [interface ID]: https://learn.microsoft.com/en-us/office/client-developer/outlook/mapi/iid 41 | func (*ITaskbarList4) IID() co.IID { 42 | return co.IID_ITaskbarList4 43 | } 44 | 45 | // [SetProperties] method. 46 | // 47 | // [SetProperties]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-itaskbarlist4-settabproperties 48 | func (me *ITaskbarList4) SetProperties(hwndTab HWND, flags co.STPFLAG) error { 49 | ret, _, _ := syscall.SyscallN( 50 | (*_ITaskbarList4Vt)(unsafe.Pointer(*me.Ppvt())).SetTabProperties, 51 | uintptr(unsafe.Pointer(me.Ppvt())), 52 | uintptr(hwndTab), 53 | uintptr(flags)) 54 | return utl.ErrorAsHResult(ret) 55 | } 56 | 57 | type _ITaskbarList4Vt struct { 58 | _ITaskbarList3Vt 59 | SetTabProperties uintptr 60 | } 61 | -------------------------------------------------------------------------------- /win/kernel_funcs_386.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/dll" 11 | "github.com/rodrigocfd/windigo/internal/utl" 12 | ) 13 | 14 | // [VerifyVersionInfo] function. 15 | // 16 | // [VerifyVersionInfo]: https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-verifyversioninfow 17 | func VerifyVersionInfo(ovi *OSVERSIONINFOEX, typeMask co.VER, conditionMask uint64) (bool, error) { 18 | ovi.SetDwOsVersionInfoSize() // safety 19 | cMaskLo, cMaskHi := utl.Break64(conditionMask) 20 | 21 | ret, _, err := syscall.SyscallN( 22 | dll.Load(dll.KERNEL32, &_VerifyVersionInfoW, "VerifyVersionInfoW"), 23 | uintptr(unsafe.Pointer(ovi)), 24 | uintptr(typeMask), 25 | uintptr(cMaskLo), 26 | uintptr(cMaskHi)) 27 | 28 | if wErr := co.ERROR(err); ret == 0 && wErr == co.ERROR_OLD_WIN_VERSION { 29 | return false, nil 30 | } else if ret == 0 { 31 | return false, wErr // actual error 32 | } else { 33 | return true, nil 34 | } 35 | } 36 | 37 | var _VerifyVersionInfoW *syscall.Proc 38 | 39 | // [VerSetConditionMask] function. 40 | // 41 | // [VerSetConditionMask]: https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-versetconditionmask 42 | func VerSetConditionMask(conditionMask uint64, typeMask co.VER, condition co.VER_COND) uint64 { 43 | cMaskLo, cMaskHi := utl.Break64(conditionMask) 44 | retLo, retHi, _ := syscall.SyscallN( 45 | dll.Load(dll.KERNEL32, &_VerSetConditionMask, "VerSetConditionMask"), 46 | uintptr(cMaskLo), 47 | uintptr(cMaskHi), 48 | uintptr(typeMask), 49 | uintptr(condition)) 50 | return utl.Make64(uint32(retLo), uint32(retHi)) 51 | } 52 | 53 | var _VerSetConditionMask *syscall.Proc 54 | -------------------------------------------------------------------------------- /win/shell_ITaskbarList2.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/utl" 11 | ) 12 | 13 | // [ITaskbarList2] COM interface. 14 | // 15 | // Implements [OleObj] and [OleResource]. 16 | // 17 | // Example: 18 | // 19 | // _, _ = win.CoInitializeEx( 20 | // co.COINIT_APARTMENTTHREADED | co.COINIT_DISABLE_OLE1DDE) 21 | // defer win.CoUninitialize() 22 | // 23 | // rel := win.NewOleReleaser() 24 | // defer rel.Release() 25 | // 26 | // var taskbl *win.ITaskbarList2 27 | // _ = win.CoCreateInstance( 28 | // rel, 29 | // co.CLSID_TaskbarList, 30 | // nil, 31 | // co.CLSCTX_INPROC_SERVER, 32 | // &taskbl, 33 | // ) 34 | // 35 | // [ITaskbarList2]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-itaskbarlist2 36 | type ITaskbarList2 struct{ ITaskbarList } 37 | 38 | // Returns the unique COM [interface ID]. 39 | // 40 | // [interface ID]: https://learn.microsoft.com/en-us/office/client-developer/outlook/mapi/iid 41 | func (*ITaskbarList2) IID() co.IID { 42 | return co.IID_ITaskbarList2 43 | } 44 | 45 | // [MarkFullscreenWindow] method. 46 | // 47 | // [MarkFullscreenWindow]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-itaskbarlist2-markfullscreenwindow 48 | func (me *ITaskbarList2) MarkFullscreenWindow(hwnd HWND, fullScreen bool) error { 49 | ret, _, _ := syscall.SyscallN( 50 | (*_ITaskbarList2Vt)(unsafe.Pointer(*me.Ppvt())).MarkFullscreenWindow, 51 | uintptr(unsafe.Pointer(me.Ppvt())), 52 | uintptr(hwnd), 53 | utl.BoolToUintptr(fullScreen)) 54 | return utl.ErrorAsHResult(ret) 55 | } 56 | 57 | type _ITaskbarList2Vt struct { 58 | _ITaskbarListVt 59 | MarkFullscreenWindow uintptr 60 | } 61 | -------------------------------------------------------------------------------- /win/user_hwnd_386.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | 8 | "github.com/rodrigocfd/windigo/co" 9 | "github.com/rodrigocfd/windigo/internal/dll" 10 | ) 11 | 12 | // [GetClassLong] function. 13 | // 14 | // [GetClassLong]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getclasslongw 15 | func (hWnd HWND) GetClassLongPtr(index co.GCL) (uintptr, error) { 16 | ret, _, err := syscall.SyscallN( 17 | dll.Load(dll.USER32, &_GetClassLongW, "GetClassLongW"), 18 | uintptr(hWnd), 19 | uintptr(index)) 20 | if ret == 0 { 21 | return 0, co.ERROR(err) 22 | } 23 | return ret, nil 24 | } 25 | 26 | var _GetClassLongW *syscall.Proc 27 | 28 | // [GetWindowLong] function. 29 | // 30 | // [GetWindowLong]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowlongw 31 | func (hWnd HWND) GetWindowLongPtr(index co.GWLP) (uintptr, error) { 32 | ret, _, err := syscall.SyscallN( 33 | dll.Load(dll.USER32, &_GetWindowLongW, "GetWindowLongW"), 34 | uintptr(hWnd), 35 | uintptr(index)) 36 | if wErr := co.ERROR(err); ret == 0 && wErr != co.ERROR_SUCCESS { 37 | return 0, wErr 38 | } 39 | return ret, nil 40 | } 41 | 42 | var _GetWindowLongW *syscall.Proc 43 | 44 | // [SetWindowLong] function. 45 | // 46 | // [SetWindowLong]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowlongw 47 | func (hWnd HWND) SetWindowLongPtr(index co.GWLP, newLong uintptr) (uintptr, error) { 48 | ret, _, err := syscall.SyscallN( 49 | dll.Load(dll.USER32, &_SetWindowLongW, "SetWindowLongW"), 50 | uintptr(hWnd), 51 | uintptr(index), 52 | uintptr(int32(newLong))) 53 | if wErr := co.ERROR(err); ret == 0 && wErr != co.ERROR_SUCCESS { 54 | return 0, wErr 55 | } 56 | return ret, nil 57 | } 58 | 59 | var _SetWindowLongW *syscall.Proc 60 | -------------------------------------------------------------------------------- /internal/dll/dll.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package dll 4 | 5 | import ( 6 | "sync" 7 | "sync/atomic" 8 | "syscall" 9 | "unsafe" 10 | ) 11 | 12 | type DLL_INDEX int // Indexes globals dllCache and dllNames. 13 | 14 | const ( 15 | ADVAPI32 DLL_INDEX = iota 16 | COMCTL32 17 | DWMAPI 18 | GDI32 19 | KERNEL32 20 | OLE32 21 | OLEAUT32 22 | PSAPI 23 | SHELL32 24 | SHLWAPI 25 | USER32 26 | UXTHEME 27 | VERSION 28 | ) 29 | 30 | var ( 31 | dllCache [13]*syscall.DLL // Indexed by DLL_INDEX. 32 | dllMutex sync.Mutex 33 | dllNames = [13]string{ // Indexed by DLL_INDEX. 34 | "advapi32", 35 | "comctl32", 36 | "dwmapi", 37 | "gdi32", 38 | "kernel32", 39 | "ole32", 40 | "oleaut32", 41 | "psapi", 42 | "shell32", 43 | "shlwapi", 44 | "user32", 45 | "uxtheme", 46 | "version", 47 | } 48 | ) 49 | 50 | func loadDll(idx DLL_INDEX, name string) *syscall.DLL { 51 | if pDll := atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&dllCache[idx]))); pDll != nil { 52 | return (*syscall.DLL)(pDll) 53 | } 54 | 55 | dllMutex.Lock() 56 | defer dllMutex.Unlock() 57 | 58 | dllObj := syscall.MustLoadDLL(name) 59 | atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&dllCache[idx])), unsafe.Pointer(dllObj)) 60 | return dllObj 61 | } 62 | 63 | // Dynamically loads a procedure from a system DLL. 64 | func Load(dllIdx DLL_INDEX, pDestProc **syscall.Proc, procName string) uintptr { 65 | if pProc := atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(pDestProc))); pProc != nil { 66 | return (*syscall.Proc)(pProc).Addr() 67 | } 68 | 69 | dllObj := loadDll(dllIdx, dllNames[dllIdx]) 70 | 71 | dllMutex.Lock() 72 | defer dllMutex.Unlock() 73 | 74 | proc := dllObj.MustFindProc(procName) 75 | atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(pDestProc)), unsafe.Pointer(proc)) 76 | return proc.Addr() 77 | } 78 | -------------------------------------------------------------------------------- /internal/utl/bitops.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package utl 4 | 5 | import ( 6 | "encoding/binary" 7 | ) 8 | 9 | // Assembles an uint16 from two uint8. 10 | func Make16(lo, hi uint8) uint16 { 11 | return (uint16(lo) & 0xff) | ((uint16(hi) & 0xff) << 8) 12 | } 13 | 14 | // Assembles an uint32 from two uint16. 15 | func Make32(lo, hi uint16) uint32 { 16 | return (uint32(lo) & 0xffff) | ((uint32(hi) & 0xffff) << 16) 17 | } 18 | 19 | // Assembles an uint64 from two uint32. 20 | func Make64(lo, hi uint32) uint64 { 21 | return (uint64(lo) & 0xffff_ffff) | ((uint64(hi) & 0xffff_ffff) << 32) 22 | } 23 | 24 | // Breaks an uint16 into low and high uint8. 25 | func Break16(val uint16) (lo, hi uint8) { 26 | return uint8(val & 0xff), uint8(val >> 8 & 0xff) 27 | } 28 | 29 | // Breaks an uint32 into low and high uint16. 30 | func Break32(val uint32) (lo, hi uint16) { 31 | return uint16(val & 0xffff), uint16(val >> 16 & 0xffff) 32 | } 33 | 34 | // Breaks an uint64 into low and high uint32. 35 | func Break64(val uint64) (lo, hi uint32) { 36 | return uint32(val & 0xffff_ffff), uint32(val >> 32 & 0xffff_ffff) 37 | } 38 | 39 | // Tells whether the number has the nth bit set. 40 | // 41 | // bitPosition must be in the range 0-7. 42 | func BitIsSet(number, bitPosition uint8) bool { 43 | return (number & (1 << bitPosition)) > 0 44 | } 45 | 46 | // Returns a new number with the nth bit set or clear. 47 | // 48 | // bitPosition must be in the range 0-7. 49 | func BitSet(number, bitPosition uint8, doSet bool) uint8 { 50 | if doSet { 51 | return number | (1 << bitPosition) 52 | } else { 53 | return number &^ (1 << bitPosition) 54 | } 55 | } 56 | 57 | // Reverses the bytes, not the bits. 58 | func ReverseBytes64(n uint64) uint64 { 59 | var buf64 [8]byte 60 | binary.LittleEndian.PutUint64(buf64[:], n) 61 | return binary.BigEndian.Uint64(buf64[:]) 62 | } 63 | -------------------------------------------------------------------------------- /win/user_hwnd_amd64.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | 8 | "github.com/rodrigocfd/windigo/co" 9 | "github.com/rodrigocfd/windigo/internal/dll" 10 | ) 11 | 12 | // [GetClassLongPtr] function. 13 | // 14 | // [GetClassLongPtr]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getclasslongptrw 15 | func (hWnd HWND) GetClassLongPtr(index co.GCL) (uintptr, error) { 16 | ret, _, err := syscall.SyscallN( 17 | dll.Load(dll.USER32, &_GetClassLongPtrW, "GetClassLongPtrW"), 18 | uintptr(hWnd), 19 | uintptr(index)) 20 | if ret == 0 { 21 | return 0, co.ERROR(err) 22 | } 23 | return ret, nil 24 | } 25 | 26 | var _GetClassLongPtrW *syscall.Proc 27 | 28 | // [GetWindowLongPtr] function. 29 | // 30 | // [GetWindowLongPtr]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowlongptrw 31 | func (hWnd HWND) GetWindowLongPtr(index co.GWLP) (uintptr, error) { 32 | ret, _, err := syscall.SyscallN( 33 | dll.Load(dll.USER32, &_GetWindowLongPtrW, "GetWindowLongPtrW"), 34 | uintptr(hWnd), 35 | uintptr(index)) 36 | if wErr := co.ERROR(err); ret == 0 && wErr != co.ERROR_SUCCESS { 37 | return 0, wErr 38 | } 39 | return ret, nil 40 | } 41 | 42 | var _GetWindowLongPtrW *syscall.Proc 43 | 44 | // [SetWindowLongPtr] function. 45 | // 46 | // [SetWindowLongPtr]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowlongptrw 47 | func (hWnd HWND) SetWindowLongPtr(index co.GWLP, newLong uintptr) (uintptr, error) { 48 | ret, _, err := syscall.SyscallN( 49 | dll.Load(dll.USER32, &_SetWindowLongPtrW, "SetWindowLongPtrW"), 50 | uintptr(hWnd), 51 | uintptr(index), 52 | newLong) 53 | if wErr := co.ERROR(err); ret == 0 && wErr != co.ERROR_SUCCESS { 54 | return 0, wErr 55 | } 56 | return ret, nil 57 | } 58 | 59 | var _SetWindowLongPtrW *syscall.Proc 60 | -------------------------------------------------------------------------------- /win/version_structs.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "github.com/rodrigocfd/windigo/co" 7 | "github.com/rodrigocfd/windigo/internal/utl" 8 | ) 9 | 10 | // [VS_FIXEDFILEINFO] struct. 11 | // 12 | // [VS_FIXEDFILEINFO]: https://learn.microsoft.com/en-us/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo 13 | type VS_FIXEDFILEINFO struct { 14 | DwSignature uint32 15 | DwStrucVersion uint32 16 | dwFileVersionMS uint32 17 | dwFileVersionLS uint32 18 | dwProductVersionMS uint32 19 | dwProductVersionLS uint32 20 | DwFileFlagsMask co.VS_FF 21 | DwFileFlags co.VS_FF 22 | DwFileOS co.VOS 23 | DwFileType co.VFT 24 | DwFileSubtype co.VFT2 25 | dwFileDateMS uint32 26 | dwFileDateLS uint32 27 | } 28 | 29 | func (ffi *VS_FIXEDFILEINFO) FileVersion() (major, minor, patch, build uint16) { 30 | return HIWORD(ffi.dwFileVersionMS), LOWORD(ffi.dwFileVersionMS), 31 | HIWORD(ffi.dwFileVersionLS), LOWORD(ffi.dwFileVersionLS) 32 | } 33 | func (ffi *VS_FIXEDFILEINFO) SetFileVersion(major, minor, patch, build uint16) { 34 | ffi.dwFileVersionMS = MAKELONG(minor, major) 35 | ffi.dwFileVersionLS = MAKELONG(build, patch) 36 | } 37 | 38 | func (ffi *VS_FIXEDFILEINFO) ProductVersion() (major, minor, patch, build uint16) { 39 | return HIWORD(ffi.dwProductVersionMS), LOWORD(ffi.dwProductVersionMS), 40 | HIWORD(ffi.dwProductVersionLS), LOWORD(ffi.dwProductVersionLS) 41 | } 42 | func (ffi *VS_FIXEDFILEINFO) SetProductVersion(major, minor, patch, build uint16) { 43 | ffi.dwProductVersionMS = MAKELONG(minor, major) 44 | ffi.dwProductVersionLS = MAKELONG(build, patch) 45 | } 46 | 47 | func (ffi *VS_FIXEDFILEINFO) FileDate() uint64 { 48 | return utl.Make64(ffi.dwFileDateLS, ffi.dwFileDateMS) 49 | } 50 | func (ffi *VS_FIXEDFILEINFO) SetFileDate(val uint64) { 51 | ffi.dwFileDateLS, ffi.dwFileDateMS = utl.Break64(val) 52 | } 53 | -------------------------------------------------------------------------------- /win/shell_IOleWindow.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | ) 11 | 12 | // [IOleWindow] COM interface. 13 | // 14 | // Implements [OleObj] and [OleResource]. 15 | // 16 | // [IOleWindow]: https://learn.microsoft.com/en-us/windows/win32/api/oleidl/nn-oleidl-iolewindow 17 | type IOleWindow struct{ IUnknown } 18 | 19 | // Returns the unique COM [interface ID]. 20 | // 21 | // [interface ID]: https://learn.microsoft.com/en-us/office/client-developer/outlook/mapi/iid 22 | func (*IOleWindow) IID() co.IID { 23 | return co.IID_IOleWindow 24 | } 25 | 26 | // [ContextSensitiveHelp] method. 27 | // 28 | // [ContextSensitiveHelp]: https://learn.microsoft.com/en-us/windows/win32/api/oleidl/nf-oleidl-iolewindow-contextsensitivehelp 29 | func (me *IOleWindow) ContextSensitiveHelp() (bool, error) { 30 | var bVal int32 // BOOL 31 | ret, _, _ := syscall.SyscallN( 32 | (*_IOleWindowVt)(unsafe.Pointer(*me.Ppvt())).ContextSensitiveHelp, 33 | uintptr(unsafe.Pointer(me.Ppvt())), 34 | uintptr(unsafe.Pointer(&bVal))) 35 | 36 | if hr := co.HRESULT(ret); hr == co.HRESULT_S_OK { 37 | return bVal != 0, nil 38 | } else { 39 | return false, hr 40 | } 41 | } 42 | 43 | // [GetWindow] method. 44 | // 45 | // [GetWindow]: https://learn.microsoft.com/en-us/windows/win32/api/oleidl/nf-oleidl-iolewindow-getwindow 46 | func (me *IOleWindow) GetWindow() (HWND, error) { 47 | var hWnd HWND 48 | ret, _, _ := syscall.SyscallN( 49 | (*_IOleWindowVt)(unsafe.Pointer(*me.Ppvt())).GetWindow, 50 | uintptr(unsafe.Pointer(me.Ppvt())), 51 | uintptr(unsafe.Pointer(&hWnd))) 52 | 53 | if hr := co.HRESULT(ret); hr == co.HRESULT_S_OK { 54 | return hWnd, nil 55 | } else { 56 | return HWND(0), hr 57 | } 58 | } 59 | 60 | type _IOleWindowVt struct { 61 | _IUnknownVt 62 | GetWindow uintptr 63 | ContextSensitiveHelp uintptr 64 | } 65 | -------------------------------------------------------------------------------- /wstr/decoders.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package wstr 4 | 5 | import ( 6 | "unicode/utf16" 7 | "unsafe" 8 | ) 9 | 10 | // Converts a pointer to a multi null-terminated UTF-16 string into a Go 11 | // []string. 12 | // 13 | // Source string must have 2 terminating nulls. 14 | func DecodeArrPtr(p *uint16) []string { 15 | values := make([]string, 0, 1) 16 | if p == nil { 17 | return values 18 | } 19 | 20 | pRun := unsafe.Pointer(p) 21 | sLen := 0 22 | for { 23 | if *(*uint16)(pRun) == 0 { // terminating null found 24 | if sLen == 0 { 25 | break // two terminating nulls 26 | } 27 | 28 | slice := unsafe.Slice(p, sLen) // create slice without terminating null 29 | values = append(values, string(utf16.Decode(slice))) 30 | 31 | pRun = unsafe.Add(pRun, unsafe.Sizeof(*p)) // pRun++ 32 | p = (*uint16)(pRun) 33 | sLen = 0 34 | 35 | } else { 36 | pRun = unsafe.Add(pRun, unsafe.Sizeof(*p)) // pRun++ 37 | sLen++ 38 | } 39 | } 40 | 41 | return values 42 | } 43 | 44 | // Converts a pointer to a null-terminated UTF-16 string into a Go string. 45 | func DecodePtr(p *uint16) string { 46 | // Adapted from syscall_windows.go, utf16PtrToString() private function. 47 | if p == nil { 48 | return "" 49 | } 50 | 51 | // Find null terminator. 52 | pRun := unsafe.Pointer(p) 53 | sLen := 0 54 | for *(*uint16)(pRun) != 0 { 55 | pRun = unsafe.Add(pRun, unsafe.Sizeof(*p)) // pRun++ 56 | sLen++ 57 | } 58 | 59 | slice := unsafe.Slice(p, sLen) // create slice without terminating null 60 | return string(utf16.Decode(slice)) 61 | } 62 | 63 | // Converts a []uint16 with an UTF-16 string, null-terminated or not, into a Go 64 | // string. 65 | // 66 | // Wraps [utf16.Decode]. 67 | func DecodeSlice(str []uint16) string { 68 | for idx, ch := range str { 69 | if ch == 0x0000 { 70 | return string(utf16.Decode(str[:idx])) // stop before first terminating null 71 | } 72 | } 73 | return string(utf16.Decode(str)) 74 | } 75 | -------------------------------------------------------------------------------- /win/user_hmonitor.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/dll" 11 | "github.com/rodrigocfd/windigo/internal/utl" 12 | ) 13 | 14 | // Handle to a [display monitor]. 15 | // 16 | // [display monitor]: https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#hmonitor 17 | type HMONITOR HANDLE 18 | 19 | // [MonitorFromPoint] function. 20 | // 21 | // [MonitorFromPoint]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-monitorfrompoint 22 | func MonitorFromPoint(pt POINT, flags co.MONITOR) HMONITOR { 23 | ret, _, _ := syscall.SyscallN( 24 | dll.Load(dll.USER32, &_MonitorFromPoint, "MonitorFromPoint"), 25 | uintptr(utl.Make64(uint32(pt.X), uint32(pt.Y))), 26 | uintptr(flags)) 27 | return HMONITOR(ret) 28 | } 29 | 30 | var _MonitorFromPoint *syscall.Proc 31 | 32 | // [MonitorFromRect] function. 33 | // 34 | // [MonitorFromRect]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-monitorfromrect 35 | func MonitorFromRect(rc *RECT, flags co.MONITOR) HMONITOR { 36 | ret, _, _ := syscall.SyscallN( 37 | dll.Load(dll.USER32, &_MonitorFromRect, "MonitorFromRect"), 38 | uintptr(unsafe.Pointer(rc)), 39 | uintptr(flags)) 40 | return HMONITOR(ret) 41 | } 42 | 43 | var _MonitorFromRect *syscall.Proc 44 | 45 | // [GetMonitorInfo] function. 46 | // 47 | // [GetMonitorInfo]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmonitorinfow 48 | func (hMon HMONITOR) GetMonitorInfo() (MONITORINFOEX, error) { 49 | var mix MONITORINFOEX 50 | mix.SetCbSize() 51 | 52 | ret, _, _ := syscall.SyscallN( 53 | dll.Load(dll.USER32, &_GetMonitorInfoW, "GetMonitorInfoW"), 54 | uintptr(hMon), 55 | uintptr(unsafe.Pointer(&mix))) 56 | if ret == 0 { 57 | return MONITORINFOEX{}, co.ERROR_INVALID_PARAMETER 58 | } 59 | return mix, nil 60 | } 61 | 62 | var _GetMonitorInfoW *syscall.Proc 63 | -------------------------------------------------------------------------------- /win/gdi_hbrush.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/dll" 11 | ) 12 | 13 | // Handle to a [brush]. 14 | // 15 | // [brush]: https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#hbrush 16 | type HBRUSH HGDIOBJ 17 | 18 | // [CreateBrushIndirect] function. 19 | // 20 | // ⚠️ You must defer [HBRUSH.DeleteObject]. 21 | // 22 | // [CreateBrushIndirect]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createbrushindirect 23 | func CreateBrushIndirect(lb *LOGBRUSH) (HBRUSH, error) { 24 | ret, _, _ := syscall.SyscallN( 25 | dll.Load(dll.GDI32, &_CreateBrushIndirect, "CreateBrushIndirect"), 26 | uintptr(unsafe.Pointer(lb))) 27 | if ret == 0 { 28 | return HBRUSH(0), co.ERROR_INVALID_PARAMETER 29 | } 30 | return HBRUSH(ret), nil 31 | } 32 | 33 | var _CreateBrushIndirect *syscall.Proc 34 | 35 | // [CreatePatternBrush] function. 36 | // 37 | // ⚠️ You must defer [HBRUSH.DeleteObject]. 38 | // 39 | // [CreatePatternBrush]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createpatternbrush 40 | func CreatePatternBrush(hbm HBITMAP) (HBRUSH, error) { 41 | ret, _, _ := syscall.SyscallN( 42 | dll.Load(dll.GDI32, &_CreatePatternBrush, "CreatePatternBrush"), 43 | uintptr(hbm)) 44 | if ret == 0 { 45 | return HBRUSH(0), co.ERROR_INVALID_PARAMETER 46 | } 47 | return HBRUSH(ret), nil 48 | } 49 | 50 | var _CreatePatternBrush *syscall.Proc 51 | 52 | // [DeleteObject] function. 53 | // 54 | // [DeleteObject]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-deleteobject 55 | func (hBrush HBRUSH) DeleteObject() error { 56 | return HGDIOBJ(hBrush).DeleteObject() 57 | } 58 | 59 | // [GetObject] function. 60 | // 61 | // [GetObject]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-getobject 62 | func (hBrush HBRUSH) GetObject() (LOGBRUSH, error) { 63 | var lb LOGBRUSH 64 | if err := HGDIOBJ(hBrush).GetObject(unsafe.Sizeof(lb), unsafe.Pointer(&lb)); err != nil { 65 | return LOGBRUSH{}, err 66 | } else { 67 | return lb, nil 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /ui/interfaces.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ui 4 | 5 | import ( 6 | "github.com/rodrigocfd/windigo/win" 7 | ) 8 | 9 | // Any window. 10 | type Window interface { 11 | // Returns the underlying HWND handle of this window. 12 | // 13 | // Note that this handle is initially zero, existing only after window creation. 14 | Hwnd() win.HWND 15 | } 16 | 17 | // A child control window. 18 | type ChildControl interface { 19 | Window 20 | 21 | // Returns the control ID, unique within the same Parent. 22 | CtrlId() uint16 23 | 24 | // If parent is a dialog, sets the focus by sending [WM_NEXTDLGCTL]. This 25 | // draws the borders correctly in some undefined controls, like buttons. 26 | // 27 | // Otherwise, calls [SetFocus]. 28 | // 29 | // [WM_NEXTDLGCTL]: https://learn.microsoft.com/en-us/windows/win32/dlgbox/wm-nextdlgctl 30 | // [SetFocus]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setfocus 31 | Focus() 32 | } 33 | 34 | // A parent window. 35 | type Parent interface { 36 | Window 37 | 38 | // Exposes all the window notifications the can be handled. 39 | // 40 | // Panics if called after the window has been created. 41 | On() *EventsWindow 42 | 43 | // This method is analog to [SendMessage] (synchronous), but intended to be 44 | // called from another thread, so a callback function can, tunelled by 45 | // [WNDPROC], run in the original thread of the window, thus allowing GUI 46 | // updates. With this, the user doesn't have to deal with a custom WM_ 47 | // message. 48 | // 49 | // Example: 50 | // 51 | // var wnd ui.Parent // initialized somewhere 52 | // 53 | // wnd.On().WmCreate(func(_ WmCreate) int { 54 | // go func() { 55 | // // process to be done in a parallel goroutine... 56 | // 57 | // wnd.UiThread(func() { 58 | // // update the UI in the original UI thread... 59 | // }) 60 | // }() 61 | // return 0 62 | // }) 63 | // 64 | // [SendMessage]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendmessagew 65 | // [WNDPROC]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nc-winuser-wndproc 66 | UiThread(fun func()) 67 | 68 | base() *_BaseContainer 69 | } 70 | -------------------------------------------------------------------------------- /win/ole_ISequentialStream.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | ) 11 | 12 | // [ISequentialStream] COM interface. 13 | // 14 | // Implements [OleObj] and [OleResource]. 15 | // 16 | // [ISequentialStream]: https://learn.microsoft.com/en-us/windows/win32/api/objidl/nn-objidl-isequentialstream 17 | type ISequentialStream struct{ IUnknown } 18 | 19 | // Returns the unique COM [interface ID]. 20 | // 21 | // [interface ID]: https://learn.microsoft.com/en-us/office/client-developer/outlook/mapi/iid 22 | func (*ISequentialStream) IID() co.IID { 23 | return co.IID_ISequentialStream 24 | } 25 | 26 | // [Read] method. 27 | // 28 | // If returned numBytesRead is lower than requested buffer size, it means 29 | // the end of stream was reached. 30 | // 31 | // [Read]: https://learn.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-isequentialstream-read 32 | func (me *ISequentialStream) Read(buffer []byte) (numBytesRead int, hr error) { 33 | var read32 uint32 34 | ret, _, _ := syscall.SyscallN( 35 | (*_ISequentialStreamVt)(unsafe.Pointer(*me.Ppvt())).Read, 36 | uintptr(unsafe.Pointer(me.Ppvt())), 37 | uintptr(unsafe.Pointer(&buffer[0])), 38 | uintptr(uint32(len(buffer))), 39 | uintptr(unsafe.Pointer(&read32))) 40 | 41 | if hr = co.HRESULT(ret); hr == co.HRESULT_S_OK { 42 | return int(read32), nil 43 | } else { 44 | return 0, hr 45 | } 46 | } 47 | 48 | // [Write] method. 49 | // 50 | // [Write]: https://learn.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-isequentialstream-write 51 | func (me *ISequentialStream) Write(data []byte) (numBytesWritten int, hr error) { 52 | var written32 uint32 53 | ret, _, _ := syscall.SyscallN( 54 | (*_ISequentialStreamVt)(unsafe.Pointer(*me.Ppvt())).Write, 55 | uintptr(unsafe.Pointer(me.Ppvt())), 56 | uintptr(unsafe.Pointer(&data[0])), 57 | uintptr(uint32(len(data))), 58 | uintptr(unsafe.Pointer(&written32))) 59 | 60 | if hr = co.HRESULT(ret); hr == co.HRESULT_S_OK { 61 | return int(written32), nil 62 | } else { 63 | return 0, hr 64 | } 65 | return 66 | } 67 | 68 | type _ISequentialStreamVt struct { 69 | _IUnknownVt 70 | Read uintptr 71 | Write uintptr 72 | } 73 | -------------------------------------------------------------------------------- /internal/utl/ole.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package utl 4 | 5 | import ( 6 | "reflect" 7 | "unsafe" 8 | ) 9 | 10 | // Checks whether the interface carries an actual [nil value]. 11 | // 12 | // [nil value]: https://blog.devtrovert.com/p/go-secret-interface-nil-is-not-nil 13 | func IsNil(x interface{}) bool { 14 | if x == nil { 15 | return true 16 | } 17 | 18 | value := reflect.ValueOf(x) 19 | kind := value.Kind() 20 | 21 | switch kind { 22 | case reflect.Chan, 23 | reflect.Func, 24 | reflect.Map, 25 | reflect.Ptr, 26 | reflect.UnsafePointer, 27 | reflect.Interface, 28 | reflect.Slice: 29 | return value.IsNil() 30 | default: 31 | return false 32 | } 33 | } 34 | 35 | // Validates the pointer to pointer to COM object. Panics if fails. 36 | // 37 | // Returns the underlying pointed-to object. 38 | func OleValidateObj(ppOut interface{}) interface{} { 39 | ppTy := reflect.TypeOf(ppOut) // **IUnknown 40 | if ppTy.Kind() != reflect.Ptr { 41 | panic("You must a pass a pointer to a pointer COM object [**Ty failed].") 42 | } 43 | 44 | pTy := ppTy.Elem() // *IUnknown 45 | if pTy.Kind() != reflect.Ptr { 46 | panic("You must a pass a pointer to a pointer COM object [*Ty failed].") 47 | } 48 | 49 | // ty := pTy.Elem() // IUnknown 50 | 51 | pTarget := reflect.ValueOf(ppOut).Elem() // *IUnknown 52 | if !pTarget.CanSet() { 53 | panic("You must a pass a pointer to a pointer COM object [target CanSet() failed].") 54 | } 55 | var emptyVal reflect.Value 56 | if pTarget.MethodByName("IID") == emptyVal { 57 | panic("You must a pass a pointer to a pointer COM object [target IID() failed].") 58 | } 59 | 60 | return pTarget.Interface() 61 | } 62 | 63 | // Constructs a new COM object, assigns it to the pointer, and sets its 64 | // **IUnknownVt. 65 | // 66 | // Returns the underlying pointed-to object. 67 | func OleCreateObj(ppOut interface{}, ppIUnknownVt unsafe.Pointer) interface{} { 68 | pTarget := reflect.ValueOf(ppOut).Elem() // *IUnknown 69 | ty := reflect.TypeOf(ppOut).Elem().Elem() // IUnknown 70 | pTarget.Set(reflect.New(ty)) // instantiate new object on the heap and assign its pointer 71 | 72 | addrField0 := pTarget.Elem().Field(0).UnsafeAddr() 73 | *(*uintptr)(unsafe.Pointer(addrField0)) = uintptr(ppIUnknownVt) // assign ppvt field 74 | 75 | return pTarget.Interface() 76 | } 77 | -------------------------------------------------------------------------------- /wstr/BufEncoder.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package wstr 4 | 5 | import ( 6 | "unsafe" 7 | ) 8 | 9 | // Size of the stack buffer, equal to [MAX_PATH]. 10 | // 11 | // [MAX_PATH]: https://stackoverflow.com/a/1880453/6923555 12 | const BUF_MAX = 260 13 | 14 | // Encodes a Go string into a null-terminated UTF-16. If the string fits the 15 | // stack buffer, no allocation is performed. 16 | // 17 | // This buffer is used internally to speed up syscalls with string arguments. 18 | // 19 | // Example: 20 | // 21 | // var buf1, buf2 wstr.BufEncoder 22 | // p1 := buf1.AllowEmpty("abc") 23 | // p2 := buf2.EmptyIsNil("def") 24 | type BufEncoder struct { 25 | stack [BUF_MAX]uint16 26 | } 27 | 28 | // Encodes a Go string into a null-terminated UTF-16, returning a pointer to it. 29 | // If the string is empty, the buffer will contain just nulls. 30 | // 31 | // If the number of UTF-16 words fit the internal stack buffer, the returned 32 | // pointer will point to the internal stack buffer. Otherwise, the returned 33 | // pointer will point to a new heap-allocated slice. 34 | func (me *BufEncoder) AllowEmpty(s string) unsafe.Pointer { 35 | slice := me.Slice(s) 36 | return unsafe.Pointer(&slice[0]) 37 | } 38 | 39 | // Encodes a Go string into a null-terminated UTF-16, returning a pointer to it. 40 | // If the string is empty, returns nil. 41 | // 42 | // If the number of UTF-16 words fit the internal stack buffer, the returned 43 | // pointer will point to the internal stack buffer. Otherwise, the returned 44 | // pointer will point to a new heap-allocated slice. 45 | func (me *BufEncoder) EmptyIsNil(s string) unsafe.Pointer { 46 | if s == "" { 47 | return nil 48 | } 49 | return me.AllowEmpty(s) 50 | } 51 | 52 | // Encodes a Go string into a null-terminated UTF-16, returning a slice with it. 53 | // 54 | // If the number of UTF-16 words fit the internal stack buffer, the returned 55 | // slice will point to the internal stack buffer. Otherwise, the returned slice 56 | // will point to a new heap-allocated slice. 57 | func (me *BufEncoder) Slice(s string) []uint16 { 58 | numWords := CountUtf16Len(s) + 1 // plus terminating null 59 | if numWords > BUF_MAX { 60 | heap := make([]uint16, numWords) // overflows our stack buffer, use the heap 61 | EncodeToBuf(heap, s) 62 | return heap 63 | } else { 64 | EncodeToBuf(me.stack[:], s) 65 | return me.stack[:numWords] 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /win/user_haccel.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/dll" 11 | "github.com/rodrigocfd/windigo/internal/utl" 12 | ) 13 | 14 | // Handle to an [accelerator table]. 15 | // 16 | // [accelerator table]: https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#haccel 17 | type HACCEL HANDLE 18 | 19 | // [CreateAcceleratorTable] function. 20 | // 21 | // ⚠️ You must defer [HACCEL.DestroyAcceleratorTable]. 22 | // 23 | // [CreateAcceleratorTable]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createacceleratortablew 24 | func CreateAcceleratorTable(accelList []ACCEL) (HACCEL, error) { 25 | ret, _, err := syscall.SyscallN( 26 | dll.Load(dll.USER32, &_CreateAcceleratorTableW, "CreateAcceleratorTableW"), 27 | uintptr(unsafe.Pointer(&accelList[0])), 28 | uintptr(int32(len(accelList)))) 29 | if ret == 0 { 30 | return HACCEL(0), co.ERROR(err) 31 | } 32 | return HACCEL(ret), nil 33 | } 34 | 35 | var _CreateAcceleratorTableW *syscall.Proc 36 | 37 | // [CopyAcceleratorTable] function. 38 | // 39 | // [CopyAcceleratorTable]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-copyacceleratortablew 40 | func (hAccel HACCEL) CopyAcceleratorTable() []ACCEL { 41 | szRet, _, _ := syscall.SyscallN( 42 | dll.Load(dll.USER32, &_CopyAcceleratorTableW, "CopyAcceleratorTableW"), 43 | uintptr(hAccel), 44 | 0, 0) 45 | if szRet == 0 { 46 | return []ACCEL{} 47 | } 48 | accelList := make([]ACCEL, szRet) 49 | syscall.SyscallN( 50 | dll.Load(dll.USER32, &_CopyAcceleratorTableW, "CopyAcceleratorTableW"), 51 | uintptr(hAccel), 52 | uintptr(unsafe.Pointer(&accelList[0])), 53 | szRet) 54 | return accelList 55 | } 56 | 57 | var _CopyAcceleratorTableW *syscall.Proc 58 | 59 | // [DestroyAcceleratorTable] function. 60 | // 61 | // Paired with [CreateAcceleratorTable]. 62 | // 63 | // [DestroyAcceleratorTable]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-destroyacceleratortable 64 | func (hAccel HACCEL) DestroyAcceleratorTable() error { 65 | ret, _, err := syscall.SyscallN( 66 | dll.Load(dll.USER32, &_DestroyAcceleratorTable, "DestroyAcceleratorTable"), 67 | uintptr(hAccel)) 68 | return utl.ZeroAsGetLastError(ret, err) 69 | } 70 | 71 | var _DestroyAcceleratorTable *syscall.Proc 72 | -------------------------------------------------------------------------------- /win/psapi_structs.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "unsafe" 7 | ) 8 | 9 | // [MODULEINFO] struct. 10 | // 11 | // [MODULEINFO]: https://learn.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-moduleinfo 12 | type MODULEINFO struct { 13 | LpBaseOfDll uintptr 14 | SizeOfImage uint32 15 | EntryPoint uintptr 16 | } 17 | 18 | // [PERFORMANCE_INFORMATION] struct. 19 | // 20 | // ⚠️ You must call [PERFORMANCE_INFORMATION.SetCb] to initialize the struct. 21 | // 22 | // Example: 23 | // 24 | // var pi win.PERFORMANCE_INFORMATION 25 | // pi.SetCb() 26 | // 27 | // [PERFORMANCE_INFORMATION]: https://learn.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-performance_information 28 | type PERFORMANCE_INFORMATION struct { 29 | cb uint32 30 | CommitTotal uintptr 31 | CommitLimit uintptr 32 | CommitPeak uintptr 33 | PhysicalTotal uintptr 34 | PhysicalAvailable uintptr 35 | SystemCache uintptr 36 | KernelTotal uintptr 37 | KernelPaged uintptr 38 | KernelNonpaged uintptr 39 | PageSize uintptr 40 | HandleCount uint32 41 | ProcessCount uint32 42 | ThreadCount uint32 43 | } 44 | 45 | // Sets the cb field to the size of the struct, correctly initializing it. 46 | func (pi *PERFORMANCE_INFORMATION) SetCb() { 47 | pi.cb = uint32(unsafe.Sizeof(*pi)) 48 | } 49 | 50 | // [PROCESS_MEMORY_COUNTERS_EX] struct. 51 | // 52 | // ⚠️ You must call [PROCESS_MEMORY_COUNTERS_EX.SetCb] to initialize the struct. 53 | // 54 | // Example: 55 | // 56 | // var pmc win.PROCESS_MEMORY_COUNTERS_EX 57 | // pmc.SetCb() 58 | // 59 | // [PROCESS_MEMORY_COUNTERS_EX]: https://learn.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-process_memory_counters_ex 60 | type PROCESS_MEMORY_COUNTERS_EX struct { 61 | cb uint32 62 | PageFaultCount uint32 63 | PeakWorkingSetSize uintptr 64 | WorkingSetSize uintptr 65 | QuotaPeakPagedPoolUsage uintptr 66 | QuotaPagedPoolUsage uintptr 67 | QuotaPeakNonPagedPoolUsage uintptr 68 | QuotaNonPagedPoolUsage uintptr 69 | PagefileUsage uintptr 70 | PeakPagefileUsage uintptr 71 | PrivateUsage uintptr 72 | } 73 | 74 | // Sets the cb field to the size of the struct, correctly initializing it. 75 | func (pmc *PROCESS_MEMORY_COUNTERS_EX) SetCb() { 76 | pmc.cb = uint32(unsafe.Sizeof(*pmc)) 77 | } 78 | -------------------------------------------------------------------------------- /win/user_hdwp.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | 8 | "github.com/rodrigocfd/windigo/co" 9 | "github.com/rodrigocfd/windigo/internal/dll" 10 | "github.com/rodrigocfd/windigo/internal/utl" 11 | ) 12 | 13 | // Handle to a deferred window position [structure]. 14 | // 15 | // [structure]: https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#hdwp 16 | type HDWP HANDLE 17 | 18 | // [BeginDeferWindowPos] function. 19 | // 20 | // Panics if numWindows is negative. 21 | // 22 | // ⚠️ You must defer [HDWP.EndDeferWindowPos]. 23 | // 24 | // [BeginDeferWindowPos]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-begindeferwindowpos 25 | func BeginDeferWindowPos(numWindows int) (HDWP, error) { 26 | utl.PanicNeg(numWindows) 27 | ret, _, err := syscall.SyscallN( 28 | dll.Load(dll.USER32, &_BeginDeferWindowPos, "BeginDeferWindowPos"), 29 | uintptr(int32(numWindows))) 30 | if ret == 0 { 31 | return HDWP(0), co.ERROR(err) 32 | } 33 | return HDWP(ret), nil 34 | } 35 | 36 | var _BeginDeferWindowPos *syscall.Proc 37 | 38 | // [DeferWindowPos] function. 39 | // 40 | // [DeferWindowPos]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-deferwindowpos 41 | func (hDwp HDWP) DeferWindowPos( 42 | hWnd, hwndInsertAfter HWND, 43 | x, y, cx, cy int, 44 | uFlags co.SWP, 45 | ) (HDWP, error) { 46 | ret, _, err := syscall.SyscallN( 47 | dll.Load(dll.USER32, &_DeferWindowPos, "DeferWindowPos"), 48 | uintptr(hDwp), 49 | uintptr(hWnd), 50 | uintptr(hwndInsertAfter), 51 | uintptr(int32(x)), 52 | uintptr(int32(y)), 53 | uintptr(int32(cx)), 54 | uintptr(int32(cy)), 55 | uintptr(uFlags)) 56 | if ret == 0 { 57 | return HDWP(0), co.ERROR(err) 58 | } 59 | return HDWP(ret), nil 60 | } 61 | 62 | var _DeferWindowPos *syscall.Proc 63 | 64 | // [EndDeferWindowPos] function. 65 | // 66 | // Paired with [BeginDeferWindowPos]. 67 | // 68 | // [EndDeferWindowPos]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enddeferwindowpos 69 | // [BeginDeferWindowPos]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-begindeferwindowpos 70 | func (hDwp HDWP) EndDeferWindowPos() error { 71 | ret, _, err := syscall.SyscallN( 72 | dll.Load(dll.USER32, &_EndDeferWindowPos, "EndDeferWindowPos"), 73 | uintptr(hDwp)) 74 | return utl.ZeroAsGetLastError(ret, err) 75 | } 76 | 77 | var _EndDeferWindowPos *syscall.Proc 78 | -------------------------------------------------------------------------------- /ui/ctl_ListViewCols.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ui 4 | 5 | import ( 6 | "fmt" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/win" 11 | "github.com/rodrigocfd/windigo/wstr" 12 | ) 13 | 14 | // The columns collection. 15 | // 16 | // You cannot create this object directly, it will be created automatically 17 | // by the owning [ListView]. 18 | type CollectionListViewCols struct { 19 | owner *ListView 20 | } 21 | 22 | // Add a column with its width, using [LVM_INSERTCOLUMN], and returns the new 23 | // column. 24 | // 25 | // Panics on error. 26 | // 27 | // Example: 28 | // 29 | // var list ui.ListView // initialized somewhere 30 | // 31 | // list.Cols.Add("Title", ui.DpiX(80)) 32 | // 33 | // [LVM_INSERTCOLUMN]: https://learn.microsoft.com/en-us/windows/win32/controls/lvm-insertcolumn 34 | func (me *CollectionListViewCols) Add(title string, width int) ListViewCol { 35 | lvc := win.LVCOLUMN{ 36 | Mask: co.LVCF_TEXT | co.LVCF_WIDTH, 37 | Cx: int32(width), 38 | } 39 | 40 | var wTitle wstr.BufEncoder 41 | lvc.SetPszText(wTitle.Slice(title)) 42 | 43 | newIdxRet, err := me.owner.hWnd.SendMessage(co.LVM_INSERTCOLUMN, 44 | 0xffff, win.LPARAM(unsafe.Pointer(&lvc))) 45 | newIdx := int(newIdxRet) 46 | if err != nil || newIdx == -1 { 47 | panic(fmt.Sprintf("LVM_INSERTCOLUMN \"%s\" failed.", title)) 48 | } 49 | 50 | return me.Get(newIdx) 51 | } 52 | 53 | // Returns all columns. 54 | func (me *CollectionListViewCols) All() []ListViewCol { 55 | nCols := me.Count() 56 | cols := make([]ListViewCol, 0, nCols) 57 | for i := 0; i < nCols; i++ { 58 | cols = append(cols, me.Get(i)) 59 | } 60 | return cols 61 | } 62 | 63 | // Retrieves the number of columns with [HDM_GETITEMCOUNT]. 64 | // 65 | // Panics if the list view has no header. 66 | // 67 | // [HDM_GETITEMCOUNT]: https://learn.microsoft.com/en-us/windows/win32/controls/hdm-getitemcount 68 | func (me *CollectionListViewCols) Count() int { 69 | if me.owner.Header() == nil { 70 | panic("This ListView has no header.") 71 | } 72 | return me.owner.header.Items.Count() 73 | } 74 | 75 | // Returns the column at the given index. 76 | func (me *CollectionListViewCols) Get(index int) ListViewCol { 77 | return ListViewCol{ 78 | owner: me.owner, 79 | index: int32(index), 80 | } 81 | } 82 | 83 | // Returns the last column. 84 | func (me *CollectionListViewCols) Last() ListViewCol { 85 | return me.Get(me.Count() - 1) 86 | } 87 | -------------------------------------------------------------------------------- /ui/ctl_TabItems.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ui 4 | 5 | import ( 6 | "fmt" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/win" 11 | "github.com/rodrigocfd/windigo/wstr" 12 | ) 13 | 14 | // The items collection. 15 | // 16 | // You cannot create this object directly, it will be created automatically 17 | // by the owning [Tab]. 18 | type CollectionTabItems struct { 19 | owner *Tab 20 | } 21 | 22 | func (me *CollectionTabItems) add(title string) TabItem { 23 | tci := win.TCITEM{ 24 | Mask: co.TCIF_TEXT, 25 | } 26 | 27 | var wBuf wstr.BufEncoder 28 | tci.SetPszText(wBuf.Slice(title)) 29 | 30 | newIdxRet, err := me.owner.hWnd.SendMessage(co.TCM_INSERTITEM, 31 | 0x0fff_ffff, win.LPARAM(unsafe.Pointer(&tci))) 32 | newIdx := int(newIdxRet) 33 | if err != nil || newIdx == -1 { 34 | panic(fmt.Sprintf("TCM_INSERTITEM \"%s\" failed.", title)) 35 | } 36 | 37 | return me.Get(newIdx) 38 | } 39 | 40 | // Retrieves the number of items with [TCM_GETITEMCOUNT]. 41 | // 42 | // Panics on error. 43 | // 44 | // [TCM_GETITEMCOUNT]: https://learn.microsoft.com/en-us/windows/win32/controls/tcm-getitemcount 45 | func (me *CollectionTabItems) Count() int { 46 | countRet, err := me.owner.hWnd.SendMessage(co.TCM_GETITEMCOUNT, 0, 0) 47 | count := int(countRet) 48 | if err != nil || count == -1 { 49 | panic("TCM_GETITEMCOUNT failed.") 50 | } 51 | return count 52 | } 53 | 54 | // Retrieves the focused item with [TCM_GETCURFOCUS], if any 55 | // 56 | // [TCM_GETCURFOCUS]: https://learn.microsoft.com/en-us/windows/win32/controls/tcm-getcurfocus 57 | func (me *CollectionTabItems) Focused() (TabItem, bool) { 58 | idxRet, _ := me.owner.hWnd.SendMessage(co.TCM_GETCURFOCUS, 0, 0) 59 | idx := int(idxRet) 60 | if idx == -1 { 61 | return TabItem{}, false 62 | } 63 | return me.Get(idx), true 64 | } 65 | 66 | // Returns the item at the given index. 67 | func (me *CollectionTabItems) Get(index int) TabItem { 68 | return TabItem{ 69 | owner: me.owner, 70 | index: int32(index), 71 | } 72 | } 73 | 74 | // Retrieves the selected item with [TCM_GETCURSEL], if any 75 | // 76 | // [TCM_GETCURSEL]: https://learn.microsoft.com/en-us/windows/win32/controls/tcm-getcursel 77 | func (me *CollectionTabItems) Selected() (TabItem, bool) { 78 | idxRet, _ := me.owner.hWnd.SendMessage(co.TCM_GETCURSEL, 0, 0) 79 | idx := int(idxRet) 80 | if idx == -1 { 81 | return TabItem{}, false 82 | } 83 | return me.Get(idx), true 84 | } 85 | -------------------------------------------------------------------------------- /win/gdi_hbitmap.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/dll" 11 | "github.com/rodrigocfd/windigo/internal/utl" 12 | ) 13 | 14 | // Handle to a [bitmap]. 15 | // 16 | // [bitmap]: https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#hbitmap 17 | type HBITMAP HGDIOBJ 18 | 19 | // [CreateBitmap] function. 20 | // 21 | // Panics if numPlanes or bitCount is negative. 22 | // 23 | // ⚠️ You must defer [HBITMAP.DeleteObject]. 24 | // 25 | // [CreateBitmap]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createbitmap 26 | func CreateBitmap(width, height, numPlanes, bitCount int, bits []byte) (HBITMAP, error) { 27 | utl.PanicNeg(numPlanes, bitCount) 28 | ret, _, _ := syscall.SyscallN( 29 | dll.Load(dll.GDI32, &_CreateBitmap, "CreateBitmap"), 30 | uintptr(int32(width)), 31 | uintptr(int32(height)), 32 | uintptr(uint32(numPlanes)), 33 | uintptr(uint32(bitCount)), 34 | uintptr(unsafe.Pointer(&bits[0]))) 35 | if ret == 0 { 36 | return HBITMAP(0), co.ERROR_INVALID_PARAMETER 37 | } 38 | return HBITMAP(ret), nil 39 | } 40 | 41 | var _CreateBitmap *syscall.Proc 42 | 43 | // [CreateBitmapIndirect] function. 44 | // 45 | // ⚠️ You must defer [HBITMAP.DeleteObject]. 46 | // 47 | // [CreateBitmapIndirect]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createbitmapindirect 48 | func CreateBitmapIndirect(bm *BITMAP) (HBITMAP, error) { 49 | ret, _, _ := syscall.SyscallN( 50 | dll.Load(dll.GDI32, &_CreateBitmapIndirect, "CreateBitmapIndirect"), 51 | uintptr(unsafe.Pointer(bm))) 52 | if ret == 0 { 53 | return HBITMAP(0), co.ERROR_INVALID_PARAMETER 54 | } 55 | return HBITMAP(ret), nil 56 | } 57 | 58 | var _CreateBitmapIndirect *syscall.Proc 59 | 60 | // [DeleteObject] function. 61 | // 62 | // [DeleteObject]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-deleteobject 63 | func (hBmp HBITMAP) DeleteObject() error { 64 | return HGDIOBJ(hBmp).DeleteObject() 65 | } 66 | 67 | // [GetObject] function. 68 | // 69 | // [GetObject]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-getobject 70 | func (hBmp HBITMAP) GetObject() (BITMAP, error) { 71 | var bmp BITMAP 72 | if err := HGDIOBJ(hBmp).GetObject(unsafe.Sizeof(bmp), unsafe.Pointer(&bmp)); err != nil { 73 | return BITMAP{}, err 74 | } else { 75 | return bmp, nil 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /win/comctl_unions.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "github.com/rodrigocfd/windigo/co" 7 | ) 8 | 9 | // Tagged union for an icon identifier for [TASKDIALOGCONFIG], which can be: 10 | // - none 11 | // - [HICON] 12 | // - uint16 13 | // - [co.TDICON] 14 | // 15 | // Example: 16 | // 17 | // ico := TdcIconTdi(co.TD_ICON_ERROR) 18 | // 19 | // if tdi, ok := ico.Tdi(); ok { 20 | // println(tdi) 21 | // } 22 | type TdcIcon struct { 23 | tag _TdcIconTag 24 | data uint64 25 | } 26 | 27 | type _TdcIconTag uint8 28 | 29 | const ( 30 | _TdcIconTag_none _TdcIconTag = iota 31 | _TdcIconTag_hIcon 32 | _TdcIconTag_id 33 | _TdcIconTag_tdIcon 34 | ) 35 | 36 | // Constructs a new [TdcIcon] with an empty value. 37 | func TdcIconNone() TdcIcon { 38 | return TdcIcon{ 39 | tag: _TdcIconTag_none, 40 | } 41 | } 42 | 43 | // Returns true if there is no value. 44 | func (me *TdcIcon) IsNone() bool { 45 | return me.tag == _TdcIconTag_none 46 | } 47 | 48 | // Constructs a new [TdcIcon] with a [HICON] value. 49 | func TdcIconHicon(hIcon HICON) TdcIcon { 50 | return TdcIcon{ 51 | tag: _TdcIconTag_hIcon, 52 | data: uint64(hIcon), 53 | } 54 | } 55 | 56 | // If the value is a [HICON], returns it and true. 57 | func (me *TdcIcon) HIcon() (HICON, bool) { 58 | if me.tag == _TdcIconTag_hIcon { 59 | return HICON(me.data), true 60 | } 61 | return HICON(0), false 62 | } 63 | 64 | // Constructs a new [TdcIcon] with an ID value. 65 | func TdcIconId(id uint16) TdcIcon { 66 | return TdcIcon{ 67 | tag: _TdcIconTag_id, 68 | data: uint64(uint16(id)), 69 | } 70 | } 71 | 72 | // If the value is an ID, returns it and true. 73 | func (me *TdcIcon) Id() (uint16, bool) { 74 | if me.tag == _TdcIconTag_id { 75 | return uint16(me.data), true 76 | } 77 | return 0, false 78 | } 79 | 80 | // Constructs a new [TdcIcon] with a [co.TDICON] value. 81 | func TdcIconTdi(tdIcon co.TDICON) TdcIcon { 82 | return TdcIcon{ 83 | tag: _TdcIconTag_tdIcon, 84 | data: uint64(tdIcon), 85 | } 86 | } 87 | 88 | // If the value is a [co.TDICON], returns it and true. 89 | func (me *TdcIcon) Tdi() (co.TDICON, bool) { 90 | if me.tag == _TdcIconTag_tdIcon { 91 | return co.TDICON(me.data), true 92 | } 93 | return co.TDICON(0), false 94 | } 95 | 96 | // Returns the internal value as uint64. 97 | func (me *TdcIcon) raw() uint64 { 98 | return me.data 99 | } 100 | -------------------------------------------------------------------------------- /wstr/BufDecoder.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package wstr 4 | 5 | import ( 6 | "unsafe" 7 | ) 8 | 9 | // A buffer to receive UTF-16 strings and convert them to Go strings. 10 | // 11 | // This buffer is used internally to speed up syscalls that return strings, and 12 | // it's prone to buffer overruns. Be sure to allocate the needed space. 13 | // 14 | // This struct contains a buffer intended to be stack-allocated, so don't move 15 | // it. 16 | // 17 | // Example: 18 | // 19 | // var buf wstr.BufDecoder 20 | // buf.Alloc(20) 21 | // ptr := buf.Ptr() 22 | type BufDecoder struct { 23 | stack [BUF_MAX]uint16 24 | heap []uint16 25 | sz int 26 | } 27 | 28 | // Makes sure there is enough room for the given number of chars. If an 29 | // allocation is necessary, any previous content will be lost. 30 | // 31 | // Panics if numChars is negative. 32 | func (me *BufDecoder) Alloc(numChars int) { 33 | if numChars > BUF_MAX { 34 | me.heap = make([]uint16, numChars) 35 | } else { 36 | me.heap = nil 37 | } 38 | me.sz = numChars 39 | } 40 | 41 | // Makes sure there is enough room for the given number of chars. If an 42 | // allocation is necessary, any previous content will be lost. 43 | // 44 | // In addition, zeroes the whole buffer. 45 | // 46 | // Panics if numChars is negative. 47 | func (me *BufDecoder) AllocAndZero(numChars int) { 48 | me.Alloc(numChars) 49 | me.Zero() 50 | } 51 | 52 | // Returns a slice over the internal memory block, up to latest 53 | // [BufDecoder.Alloc] call. 54 | func (me *BufDecoder) HotSlice() []uint16 { 55 | if me.heap != nil { 56 | return me.heap 57 | } else { 58 | return me.stack[:me.sz] 59 | } 60 | } 61 | 62 | // Returns the size of the last call to [BufDecoder.Alloc]. 63 | func (me *BufDecoder) Len() int { 64 | return me.sz 65 | } 66 | 67 | // Returns a pointer to the internal memory block, either stack or heap. 68 | func (me *BufDecoder) Ptr() unsafe.Pointer { 69 | if me.heap != nil { 70 | return unsafe.Pointer(&me.heap[0]) 71 | } else { 72 | return unsafe.Pointer(&me.stack) 73 | } 74 | } 75 | 76 | // Converts the contents to a Go string. 77 | func (me *BufDecoder) String() string { 78 | return DecodeSlice(me.HotSlice()) 79 | } 80 | 81 | // Zeroes the whole buffer. 82 | func (me *BufDecoder) Zero() { 83 | if me.heap != nil { 84 | for i := 0; i < len(me.heap); i++ { 85 | me.heap[i] = 0x0000 86 | } 87 | } else { 88 | for i := 0; i < len(me.stack); i++ { 89 | me.stack[i] = 0x0000 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /ui/wnd_EventsWindowLib.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ui 4 | 5 | import ( 6 | "unsafe" 7 | 8 | "github.com/rodrigocfd/windigo/co" 9 | "github.com/rodrigocfd/windigo/win" 10 | ) 11 | 12 | type ( 13 | _StorageMsgLib struct { // ordinary WM messages 14 | id co.WM 15 | fun func(p Wm) 16 | } 17 | _StorageNfyLib struct { // WM_NOTIFY 18 | idFrom uint16 19 | code co.NM 20 | fun func(p unsafe.Pointer) 21 | } 22 | ) 23 | 24 | // Stores messages added internally by the library. 25 | type _EventsWindowLib struct { 26 | inits []func() // WM_CREATE and WM_INITDIALOG 27 | msgs []_StorageMsgLib // ordinary WM messages 28 | nfys []_StorageNfyLib // WM_NOTIFY 29 | } 30 | 31 | // Constructor. 32 | func newEventsWindowLib() _EventsWindowLib { 33 | return _EventsWindowLib{ 34 | inits: make([]func(), 0), 35 | msgs: make([]_StorageMsgLib, 0), 36 | nfys: make([]_StorageNfyLib, 0), 37 | } 38 | } 39 | 40 | // To be called after the first WM_CREATE/INITDIALOG processing. Releases the 41 | // memory in all these closures, which are never called again. 42 | func (me *_EventsWindowLib) removeWmCreateInitdialog() { 43 | me.inits = nil 44 | } 45 | 46 | // Releases the memory of all closures. 47 | func (me *_EventsWindowLib) clear() { 48 | me.removeWmCreateInitdialog() 49 | me.msgs = nil 50 | me.nfys = nil 51 | } 52 | 53 | // Runs all the internal closures for the given message. 54 | func (me *_EventsWindowLib) processAll(p Wm) (atLeastOne bool) { 55 | switch p.Msg { 56 | case co.WM_CREATE, co.WM_INITDIALOG: 57 | for _, fun := range me.inits { 58 | fun() 59 | atLeastOne = true 60 | } 61 | case co.WM_NOTIFY: 62 | pHdr := unsafe.Pointer(p.LParam) 63 | hdr := (*win.NMHDR)(pHdr) 64 | for _, obj := range me.nfys { 65 | if obj.idFrom == uint16(hdr.IdFrom) && obj.code == co.NM(hdr.Code) { 66 | obj.fun(pHdr) 67 | atLeastOne = true 68 | } 69 | } 70 | default: 71 | for _, obj := range me.msgs { 72 | if obj.id == p.Msg { 73 | obj.fun(p) 74 | atLeastOne = true 75 | } 76 | } 77 | } 78 | return 79 | } 80 | 81 | func (me *_EventsWindowLib) wmCreateOrInitdialog(fun func()) { 82 | me.inits = append(me.inits, fun) 83 | } 84 | 85 | func (me *_EventsWindowLib) wm(id co.WM, fun func(p Wm)) { 86 | me.msgs = append(me.msgs, _StorageMsgLib{id, fun}) 87 | } 88 | 89 | func (me *_EventsWindowLib) wmNotify(idFrom uint16, code co.NM, fun func(p unsafe.Pointer)) { 90 | me.nfys = append(me.nfys, _StorageNfyLib{idFrom, code, fun}) 91 | } 92 | -------------------------------------------------------------------------------- /win/gdi_hgdiobject.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/dll" 11 | "github.com/rodrigocfd/windigo/internal/utl" 12 | ) 13 | 14 | // Handle to a [GDI object]. 15 | // 16 | // This type is used as the base type for the specialized GDI objects, being 17 | // rarely used as itself. 18 | // 19 | // [GDI object]: https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#hgdiobj 20 | type HGDIOBJ HANDLE 21 | 22 | // [GetStockObject] function. 23 | // 24 | // ⚠️ The returned HGDIOBJ must be cast into the proper GDI object. 25 | // 26 | // [GetStockObject]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-getstockobject 27 | func GetStockObject(ty co.STOCK) (HGDIOBJ, error) { 28 | ret, _, _ := syscall.SyscallN( 29 | dll.Load(dll.GDI32, &_GetStockObject, "GetStockObject"), 30 | uintptr(ty)) 31 | if ret == 0 { 32 | return HGDIOBJ(0), co.ERROR_INVALID_PARAMETER 33 | } 34 | return HGDIOBJ(ret), nil 35 | } 36 | 37 | var _GetStockObject *syscall.Proc 38 | 39 | // [DeleteObject] function. 40 | // 41 | // [DeleteObject]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-deleteobject 42 | func (hGdiObj HGDIOBJ) DeleteObject() error { 43 | ret, _, _ := syscall.SyscallN( 44 | dll.Load(dll.GDI32, &_DeleteObject, "DeleteObject"), 45 | uintptr(hGdiObj)) 46 | return utl.ZeroAsSysInvalidParm(ret) 47 | } 48 | 49 | var _DeleteObject *syscall.Proc 50 | 51 | // [GetObject] function. 52 | // 53 | // [GetObject]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-getobject 54 | func (hGdiObj HGDIOBJ) GetObject(szBuf uintptr, buf unsafe.Pointer) error { 55 | ret, _, _ := syscall.SyscallN( 56 | dll.Load(dll.GDI32, &_GetObjectW, "GetObjectW"), 57 | uintptr(hGdiObj), 58 | uintptr(int32(szBuf)), 59 | uintptr(buf)) 60 | return utl.ZeroAsSysInvalidParm(ret) 61 | } 62 | 63 | var _GetObjectW *syscall.Proc 64 | 65 | // [SelectObject] function. 66 | // 67 | // [SelectObject]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-selectobject 68 | func (hdc HDC) SelectObject(hGdiObj HGDIOBJ) (HGDIOBJ, error) { 69 | ret, _, _ := syscall.SyscallN( 70 | dll.Load(dll.GDI32, &_SelectObject, "SelectObject"), 71 | uintptr(hdc), 72 | uintptr(hGdiObj)) 73 | if ret == 0 { 74 | return HGDIOBJ(0), co.ERROR_INVALID_PARAMETER 75 | } 76 | return HGDIOBJ(ret), nil 77 | } 78 | 79 | var _SelectObject *syscall.Proc 80 | -------------------------------------------------------------------------------- /win/oleaut_bstr.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/dll" 11 | "github.com/rodrigocfd/windigo/wstr" 12 | ) 13 | 14 | // [BSTR] is the string type used in COM Automation. 15 | // 16 | // [BSTR]: https://learn.microsoft.com/en-us/previous-versions/windows/desktop/automat/bstr 17 | type BSTR uintptr 18 | 19 | // [SysAllocString] function. 20 | // 21 | // ⚠️ You must defer [BSTR.SysFreeString]. 22 | // 23 | // Example: 24 | // 25 | // bstr, _ := win.SysAllocString("hello") 26 | // defer bstr.SysFreeString() 27 | // 28 | // [SysAllocString]: https://learn.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-sysallocstring 29 | func SysAllocString(s string) (BSTR, error) { 30 | var wS wstr.BufEncoder 31 | ret, _, _ := syscall.SyscallN( 32 | dll.Load(dll.OLEAUT32, &_SysAllocString, "SysAllocString"), 33 | uintptr(wS.AllowEmpty(s))) 34 | if ret == 0 { 35 | return BSTR(0), co.HRESULT_E_OUTOFMEMORY 36 | } 37 | return BSTR(ret), nil 38 | } 39 | 40 | var _SysAllocString *syscall.Proc 41 | 42 | // [SysFreeString] function. 43 | // 44 | // [SysFreeString]: https://learn.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-sysfreestring 45 | func (bstr BSTR) SysFreeString() { 46 | if bstr != 0 { 47 | syscall.SyscallN( 48 | dll.Load(dll.OLEAUT32, &_SysFreeString, "SysFreeString"), 49 | uintptr(bstr)) 50 | } 51 | } 52 | 53 | var _SysFreeString *syscall.Proc 54 | 55 | // [SysReAllocString] function. 56 | // 57 | // Be careful when using this function. It returns a new [BSTR] handle, which 58 | // invalidates the previous one – that is, you should not call 59 | // [BSTR.SysFreeString] on the previous one. This can become tricky if you 60 | // used defer. 61 | // 62 | // ⚠️ You must defer [BSTR.SysFreeString]. 63 | // 64 | // [SysReAllocString]: https://learn.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-sysreallocstring 65 | func (bstr BSTR) SysReAllocString(s string) (BSTR, error) { 66 | var wS wstr.BufEncoder 67 | ret, _, _ := syscall.SyscallN( 68 | dll.Load(dll.OLEAUT32, &_SysReAllocString, "SysReAllocString"), 69 | uintptr(bstr), 70 | uintptr(wS.AllowEmpty(s))) 71 | if ret == 0 { 72 | return BSTR(0), co.HRESULT_E_OUTOFMEMORY 73 | } 74 | return BSTR(ret), nil 75 | } 76 | 77 | var _SysReAllocString *syscall.Proc 78 | 79 | // Converts the BSTR pointer to a string. 80 | func (bstr BSTR) String() string { 81 | return wstr.DecodePtr((*uint16)(unsafe.Pointer(bstr))) 82 | } 83 | -------------------------------------------------------------------------------- /ui/wnd_DlgMain.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ui 4 | 5 | import ( 6 | "github.com/rodrigocfd/windigo/co" 7 | "github.com/rodrigocfd/windigo/win" 8 | ) 9 | 10 | // Dialog-based main application window. 11 | type _DlgMain struct { 12 | _DlgBase 13 | iconId uint16 14 | accelTableId uint16 15 | } 16 | 17 | // Constructor. 18 | func newMainDlg(opts *VarOptsMainDlg) *_DlgMain { 19 | me := &_DlgMain{ 20 | _DlgBase: newBaseDlg(opts.dlgId), 21 | iconId: opts.iconId, 22 | accelTableId: opts.accelTableId, 23 | } 24 | me.defaultMessageHandlers() 25 | return me 26 | } 27 | 28 | func (me *_DlgMain) runAsMain(hInst win.HINSTANCE) int { 29 | me.createDialogParam(hInst, win.HWND(0)) 30 | 31 | if me.iconId != 0 { 32 | me.setIcon(hInst, me.iconId) 33 | } 34 | 35 | var hAccel win.HACCEL 36 | if me.accelTableId != 0 { 37 | var err error 38 | hAccel, err = hInst.LoadAccelerators(win.ResIdInt(me.accelTableId)) 39 | if err != nil { 40 | panic(err) 41 | } 42 | } 43 | 44 | me.hWnd.ShowWindow(co.SW_SHOW) 45 | return me.runMainLoop(hAccel, true) 46 | } 47 | 48 | func (me *_DlgMain) defaultMessageHandlers() { 49 | me._DlgBase._BaseContainer.defaultMessageHandlers() 50 | 51 | me.userEvents.WmClose(func() { 52 | me.hWnd.DestroyWindow() 53 | }) 54 | 55 | me.userEvents.WmNcDestroy(func() { 56 | win.PostQuitMessage(0) 57 | }) 58 | } 59 | 60 | // Options for [NewMainDlg]; returned by [OptsMainDlg]. 61 | type VarOptsMainDlg struct { 62 | dlgId uint16 63 | iconId uint16 64 | accelTableId uint16 65 | } 66 | 67 | // Options for [NewMainDlg]. 68 | func OptsMainDlg() *VarOptsMainDlg { 69 | return &VarOptsMainDlg{} 70 | } 71 | 72 | // Dialog resource ID passed to [CreateDialogParam]. 73 | // 74 | // Panics if not informed. 75 | // 76 | // [CreateDialogParam]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createdialogparamw 77 | func (o *VarOptsMainDlg) DlgId(id uint16) *VarOptsMainDlg { o.dlgId = id; return o } 78 | 79 | // Dialog icon ID passed to [WM_SETICON]. 80 | // 81 | // Defaults to none. 82 | // 83 | // [WM_SETICON]: https://learn.microsoft.com/en-us/windows/win32/winmsg/wm-seticon 84 | func (o *VarOptsMainDlg) IconId(id uint16) *VarOptsMainDlg { o.iconId = id; return o } 85 | 86 | // Accelerator table ID passed to [LoadAccelerators]. 87 | // 88 | // Defaults to none. 89 | // 90 | // [LoadAccelerators]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-loadacceleratorsw 91 | func (o *VarOptsMainDlg) AccelTableId(id uint16) *VarOptsMainDlg { o.accelTableId = id; return o } 92 | -------------------------------------------------------------------------------- /ui/ctl_StatusBarPart.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ui 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/rodrigocfd/windigo/co" 9 | "github.com/rodrigocfd/windigo/win" 10 | "github.com/rodrigocfd/windigo/wstr" 11 | ) 12 | 13 | // A part from a [status bar]. 14 | // 15 | // [status bar]: https://learn.microsoft.com/en-us/windows/win32/controls/status-bars 16 | type StatusBarPart struct { 17 | owner *StatusBar 18 | index int32 19 | } 20 | 21 | // Retrieves the icon with [SB_GETICON]. 22 | // 23 | // The icon is shared, the StatusBar doesn't own it. 24 | // 25 | // [SB_GETICON]: https://learn.microsoft.com/en-us/windows/win32/controls/sb-geticon 26 | func (me StatusBarPart) Icon() win.HICON { 27 | h, _ := me.owner.hWnd.SendMessage(co.SB_GETICON, win.WPARAM(me.index), 0) 28 | return win.HICON(h) 29 | } 30 | 31 | // Returns the zero-based index of the column. 32 | func (me StatusBarPart) Index() int { 33 | return int(me.index) 34 | } 35 | 36 | // Sets the icon with [SB_SETICON]. 37 | // 38 | // The icon is shared, the [StatusBar] doesn't own it. 39 | // 40 | // Returns the same part, so further operations can be chained. 41 | // 42 | // [SB_SETICON]: https://learn.microsoft.com/en-us/windows/win32/controls/sb-seticon 43 | func (me StatusBarPart) SetIcon(hIcon win.HICON) StatusBarPart { 44 | me.owner.hWnd.SendMessage(co.SB_SETICON, 45 | win.WPARAM(int32(me.index)), win.LPARAM(hIcon)) 46 | return me 47 | } 48 | 49 | // Sets the text with [SB_SETTEXT]. 50 | // 51 | // Returns the same part, so further operations can be chained. 52 | // 53 | // Panics on error. 54 | // 55 | // [SB_SETTEXT]: https://learn.microsoft.com/en-us/windows/win32/controls/sb-settext 56 | func (me StatusBarPart) SetText(text string) StatusBarPart { 57 | var wText wstr.BufEncoder 58 | ret, _ := me.owner.hWnd.SendMessage(co.SB_SETTEXT, 59 | win.MAKEWPARAM(win.MAKEWORD(uint8(me.index), 0), 0), 60 | win.LPARAM(wText.AllowEmpty(text))) 61 | if ret == 0 { 62 | panic(fmt.Sprintf("SB_SETTEXT %d failed \"%s\".", me.index, text)) 63 | } 64 | 65 | return me 66 | } 67 | 68 | // Retrieves the text with [SB_GETTEXT]. 69 | // 70 | // [SB_GETTEXT]: https://learn.microsoft.com/en-us/windows/win32/controls/sb-gettext 71 | func (me StatusBarPart) Text() string { 72 | nLen, _ := me.owner.hWnd.SendMessage(co.SB_GETTEXTLENGTH, 73 | win.WPARAM(int32(me.index)), 0) 74 | if nLen == 0 { 75 | return "" 76 | } 77 | 78 | var wBuf wstr.BufDecoder 79 | wBuf.Alloc(wstr.BUF_MAX) 80 | 81 | me.owner.hWnd.SendMessage(co.SB_GETTEXT, 82 | win.WPARAM(int32(me.index)), win.LPARAM(wBuf.Ptr())) 83 | return wBuf.String() 84 | } 85 | -------------------------------------------------------------------------------- /win/ole_OleReleaser.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "github.com/rodrigocfd/windigo/internal/utl" 7 | ) 8 | 9 | // Stores multiple [COM] resources, releasing all them at once. 10 | // 11 | // Every function which returns a COM resource will require an [OleReleaser] 12 | // to manage the object's lifetime. 13 | // 14 | // Example: 15 | // 16 | // rel := win.NewOleReleaser() 17 | // defer rel.Release() 18 | // 19 | // [COM]: https://learn.microsoft.com/en-us/windows/win32/com/component-object-model--com--portal 20 | type OleReleaser struct { 21 | objs []OleResource 22 | } 23 | 24 | // Constructs a new [OleReleaser] to store multiple [COM] resources, releasing 25 | // them all at once. 26 | // 27 | // Every function which returns a COM resource will require an [OleReleaser] to 28 | // manage the object's lifetime. 29 | // 30 | // ⚠️ You must defer [OleReleaser.Release]. 31 | // 32 | // Example: 33 | // 34 | // rel := win.NewOleReleaser() 35 | // defer rel.Release() 36 | // 37 | // [COM]: https://learn.microsoft.com/en-us/windows/win32/com/component-object-model--com--portal 38 | func NewOleReleaser() *OleReleaser { 39 | return new(OleReleaser) 40 | } 41 | 42 | // Adds a new [COM] resource to have its lifetime managed by the [OleReleaser]. 43 | func (me *OleReleaser) Add(objs ...OleResource) { 44 | me.objs = append(me.objs, objs...) 45 | } 46 | 47 | // Releases all added [COM] resource, in the reverse order they were added. 48 | // 49 | // Example: 50 | // 51 | // rel := win.NewOleReleaser() 52 | // defer rel.Release() 53 | // 54 | // [COM]: https://learn.microsoft.com/en-us/windows/win32/com/component-object-model--com--portal 55 | func (me *OleReleaser) Release() { 56 | for i := len(me.objs) - 1; i >= 0; i-- { 57 | me.objs[i].release() 58 | } 59 | me.objs = nil 60 | } 61 | 62 | // Releases the specific [COM] resources, if present, immediately. 63 | // 64 | // These objects will be removed from the internal list, thus not being released 65 | // when [OleReleaser.Release] is further called. 66 | func (me *OleReleaser) ReleaseNow(objs ...OleResource) { 67 | NextHisObj: 68 | for _, hisObj := range objs { 69 | if utl.IsNil(hisObj) { 70 | continue // skip nil objects 71 | } 72 | 73 | for ourIdx, ourObj := range me.objs { 74 | if ourObj == hisObj { // we found the passed object in our array 75 | hisObj.release() 76 | copy(me.objs[ourIdx:len(me.objs)-1], me.objs[ourIdx+1:len(me.objs)]) // move subsequent elements into the gap 77 | me.objs[len(me.objs)-1] = nil 78 | me.objs = me.objs[:len(me.objs)-1] // shrink our slice over the same memory 79 | continue NextHisObj 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /ui/wnd_DlgControl.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ui 4 | 5 | import ( 6 | "github.com/rodrigocfd/windigo/co" 7 | "github.com/rodrigocfd/windigo/win" 8 | ) 9 | 10 | // Dialog-based control. 11 | type _DlgControl struct { 12 | _DlgBase 13 | ctrlId uint16 14 | } 15 | 16 | // Constructor. 17 | func newControlDlg(parent Parent, opts *VarOptsControlDlg) *_DlgControl { 18 | setUniqueCtrlId(&opts.ctrlId) 19 | me := &_DlgControl{ 20 | _DlgBase: newBaseDlg(opts.dlgId), 21 | ctrlId: opts.ctrlId, 22 | } 23 | 24 | parent.base().beforeUserEvents.wmCreateOrInitdialog(func() { 25 | hInst, _ := parent.Hwnd().HInstance() 26 | me.createDialogParam(hInst, parent.Hwnd()) 27 | me.hWnd.SetWindowLongPtr(co.GWLP_ID, uintptr(opts.ctrlId)) // give the control its ID 28 | me.hWnd.SetWindowPos(win.HWND(0), 29 | int(opts.position.X), int(opts.position.Y), 0, 0, 30 | co.SWP_NOZORDER|co.SWP_NOSIZE) 31 | parent.base().layout.Add(parent, me.hWnd, opts.layout) 32 | }) 33 | 34 | me.defaultMessageHandlers() 35 | return me 36 | } 37 | 38 | func (me *_DlgControl) defaultMessageHandlers() { 39 | me.userEvents.WmNcPaint(func(p WmNcPaint) { 40 | paintThemedBorders(me.hWnd, p) 41 | }) 42 | } 43 | 44 | // Options for [NewControlDlg]; returned by [OptsControlDlg]. 45 | type VarOptsControlDlg struct { 46 | dlgId uint16 47 | ctrlId uint16 48 | layout LAY 49 | position win.POINT 50 | } 51 | 52 | // Options for [NewControlDlg]. 53 | func OptsControlDlg() *VarOptsControlDlg { 54 | return &VarOptsControlDlg{} 55 | } 56 | 57 | // Dialog resource ID passed to [CreateDialogParam]. 58 | // 59 | // Panics if not informed. 60 | // 61 | // [CreateDialogParam]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createdialogparamw 62 | func (o *VarOptsControlDlg) DlgId(id uint16) *VarOptsControlDlg { o.dlgId = id; return o } 63 | 64 | // Control ID. Must be unique within a same parent window. 65 | // 66 | // Defaults to an auto-generated ID. 67 | func (o *VarOptsControlDlg) CtrlId(id uint16) *VarOptsControlDlg { o.ctrlId = id; return o } 68 | 69 | // Horizontal and vertical behavior for the control layout, when the parent 70 | // window is resized. 71 | // 72 | // Defaults to ui.LAY_HOLD_HOLD. 73 | func (o *VarOptsControlDlg) Layout(l LAY) *VarOptsControlDlg { o.layout = l; return o } 74 | 75 | // Position coordinates within parent window client area. 76 | // 77 | // Defaults to ui.Dpi(0, 0). 78 | // 79 | // [CreateWindowEx]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw 80 | func (o *VarOptsControlDlg) Position(x, y int) *VarOptsControlDlg { 81 | o.position.X = int32(x) 82 | o.position.Y = int32(y) 83 | return o 84 | } 85 | -------------------------------------------------------------------------------- /win/ole_htaskmem.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | 8 | "github.com/rodrigocfd/windigo/co" 9 | "github.com/rodrigocfd/windigo/internal/dll" 10 | "github.com/rodrigocfd/windigo/internal/utl" 11 | ) 12 | 13 | // Handle to an OLE [block of memory]. 14 | // 15 | // [block of memory]: https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cotaskmemalloc 16 | type HTASKMEM HANDLE 17 | 18 | // [CoTaskMemAlloc] function. 19 | // 20 | // Panics if numBytes is negative. 21 | // 22 | // ⚠️ You must defer [HTASKMEM.CoTaskMemFree]. 23 | // 24 | // Example: 25 | // 26 | // hMem, _ := win.CoTaskMemAlloc(int(unsafe.Sizeof(win.MSG{}))) 27 | // defer hMem.CoTaskMemFree() 28 | // 29 | // pMsg := (*win.MSG)(unsafe.Pointer(hMem)) 30 | // 31 | // [CoTaskMemAlloc]: https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cotaskmemalloc 32 | func CoTaskMemAlloc(numBytes int) (HTASKMEM, error) { 33 | utl.PanicNeg(numBytes) 34 | ret, _, _ := syscall.SyscallN( 35 | dll.Load(dll.OLE32, &_CoTaskMemAlloc, "CoTaskMemAlloc"), 36 | uintptr(uint64(numBytes))) 37 | if ret == 0 { 38 | return HTASKMEM(0), co.HRESULT_E_OUTOFMEMORY 39 | } 40 | return HTASKMEM(ret), nil 41 | } 42 | 43 | var _CoTaskMemAlloc *syscall.Proc 44 | 45 | // [CoTaskMemFree] function. 46 | // 47 | // This method is safe to be called if hMem is zero. 48 | // 49 | // Paired with [CoTaskMemAlloc] and [HTASKMEM.CoTaskMemRealloc]. 50 | // 51 | // [CoTaskMemFree]: https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cotaskmemfree 52 | func (hMem HTASKMEM) CoTaskMemFree() { 53 | if hMem != 0 { 54 | syscall.SyscallN( 55 | dll.Load(dll.OLE32, &_CoTaskMemFree, "CoTaskMemFree"), 56 | uintptr(hMem)) 57 | } 58 | } 59 | 60 | var _CoTaskMemFree *syscall.Proc 61 | 62 | // [CoTaskMemRealloc] function. 63 | // 64 | // Be careful when using this function. It returns a new [HTASKMEM] handle, 65 | // which invalidates the previous one – that is, you should not call 66 | // [HTASKMEM.CoTaskMemFree] on the previous one. This can become tricky if you 67 | // used defer. 68 | // 69 | // Panics if numBytes is negative. 70 | // 71 | // ⚠️ You must defer [HTASKMEM.CoTaskMemFree]. 72 | // 73 | // [CoTaskMemRealloc]: https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cotaskmemrealloc 74 | func (hMem HTASKMEM) CoTaskMemRealloc(numBytes int) (HTASKMEM, error) { 75 | utl.PanicNeg(numBytes) 76 | ret, _, _ := syscall.SyscallN( 77 | dll.Load(dll.OLE32, &_CoTaskMemRealloc, "CoTaskMemRealloc"), 78 | uintptr(hMem), 79 | uintptr(uint64(numBytes))) 80 | if ret == 0 { 81 | return HTASKMEM(0), co.HRESULT_E_OUTOFMEMORY 82 | } 83 | return HTASKMEM(ret), nil 84 | } 85 | 86 | var _CoTaskMemRealloc *syscall.Proc 87 | -------------------------------------------------------------------------------- /win/kernel_hfind.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/dll" 11 | "github.com/rodrigocfd/windigo/internal/utl" 12 | "github.com/rodrigocfd/windigo/wstr" 13 | ) 14 | 15 | // Handle to a [find object], used to find files. 16 | // 17 | // [find object]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findfirstfilew 18 | type HFIND HANDLE 19 | 20 | // [FindFirstFile] function. 21 | // 22 | // Returns true if a file was found. 23 | // 24 | // This is a low-level function, prefer using [PathEnum] or [PathEnumDeep]. 25 | // 26 | // ⚠️ You must defer [HFIND.FindClose]. 27 | // 28 | // [FindFirstFile]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findfirstfilew 29 | func FindFirstFile(fileName string, findFileData *WIN32_FIND_DATA) (HFIND, bool, error) { 30 | var wFileName wstr.BufEncoder 31 | ret, _, err := syscall.SyscallN( 32 | dll.Load(dll.KERNEL32, &_FindFirstFileW, "FindFirstFileW"), 33 | uintptr(wFileName.EmptyIsNil(fileName)), 34 | uintptr(unsafe.Pointer(findFileData))) 35 | 36 | if int(ret) == utl.INVALID_HANDLE_VALUE { 37 | if wErr := co.ERROR(err); wErr == co.ERROR_FILE_NOT_FOUND || wErr == co.ERROR_PATH_NOT_FOUND { 38 | return HFIND(0), false, nil // no matching files, not an error 39 | } else { 40 | return HFIND(0), false, wErr 41 | } 42 | } 43 | return HFIND(ret), true, nil // a file was found 44 | } 45 | 46 | var _FindFirstFileW *syscall.Proc 47 | 48 | // [FindClose] function. 49 | // 50 | // Paired with [FindFirstFile]. 51 | // 52 | // [FindClose]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findclose 53 | func (hFind HFIND) FindClose() error { 54 | ret, _, err := syscall.SyscallN( 55 | dll.Load(dll.KERNEL32, &_FindClose, "FindClose"), 56 | uintptr(hFind)) 57 | return utl.ZeroAsGetLastError(ret, err) 58 | } 59 | 60 | var _FindClose *syscall.Proc 61 | 62 | // [FindNextFile] function. 63 | // 64 | // Returns true if a file was found. 65 | // 66 | // This is a low-level function, prefer using [PathEnum] or [PathEnumDeep]. 67 | // 68 | // [FindNextFile]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findnextfilew 69 | func (hFind HFIND) FindNextFile(findFileData *WIN32_FIND_DATA) (bool, error) { 70 | ret, _, err := syscall.SyscallN( 71 | dll.Load(dll.KERNEL32, &_FindNextFileW, "FindNextFileW"), 72 | uintptr(hFind), 73 | uintptr(unsafe.Pointer(findFileData))) 74 | 75 | if ret == 0 { 76 | if wErr := co.ERROR(err); wErr == co.ERROR_NO_MORE_FILES { 77 | return false, nil // not an error, search ended 78 | } else { 79 | return false, wErr 80 | } 81 | } 82 | return true, nil // a file was found 83 | } 84 | 85 | var _FindNextFileW *syscall.Proc 86 | -------------------------------------------------------------------------------- /win/ole_IDataObject.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/utl" 11 | ) 12 | 13 | // [IDataObject] COM interface. 14 | // 15 | // Implements [OleObj] and [OleResource]. 16 | // 17 | // [IDataObject]: https://learn.microsoft.com/en-us/windows/win32/api/objidl/nn-objidl-idataobject 18 | type IDataObject struct{ IUnknown } 19 | 20 | // Returns the unique COM [interface ID]. 21 | // 22 | // [interface ID]: https://learn.microsoft.com/en-us/office/client-developer/outlook/mapi/iid 23 | func (*IDataObject) IID() co.IID { 24 | return co.IID_IDataObject 25 | } 26 | 27 | // [GetCanonicalFormatEtc] method. 28 | // 29 | // [GetCanonicalFormatEtc]: https://learn.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-idataobject-getcanonicalformatetc 30 | func (me *IDataObject) GetCanonicalFormatEtc(etcIn *FORMATETC) (FORMATETC, error) { 31 | var etcOut FORMATETC 32 | ret, _, _ := syscall.SyscallN( 33 | (*_IDataObjectVt)(unsafe.Pointer(*me.Ppvt())).GetCanonicalFormatEtc, 34 | uintptr(unsafe.Pointer(me.Ppvt())), 35 | uintptr(unsafe.Pointer(etcIn)), 36 | uintptr(unsafe.Pointer(&etcOut))) 37 | if hr := co.HRESULT(ret); hr == co.HRESULT_S_OK { 38 | return etcOut, nil 39 | } else { 40 | return FORMATETC{}, hr 41 | } 42 | } 43 | 44 | // [GetData] method. 45 | // 46 | // ⚠️ You must defer [ReleaseStgMedium] on the returned object. 47 | // 48 | // [GetData]: https://learn.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-idataobject-getdata 49 | func (me *IDataObject) GetData(etc *FORMATETC) (STGMEDIUM, error) { 50 | var stg STGMEDIUM 51 | ret, _, _ := syscall.SyscallN( 52 | (*_IDataObjectVt)(unsafe.Pointer(*me.Ppvt())).GetData, 53 | uintptr(unsafe.Pointer(me.Ppvt())), 54 | uintptr(unsafe.Pointer(etc)), 55 | uintptr(unsafe.Pointer(&stg))) 56 | if hr := co.HRESULT(ret); hr == co.HRESULT_S_OK { 57 | return stg, nil 58 | } else { 59 | return STGMEDIUM{}, hr 60 | } 61 | } 62 | 63 | // [QueryGetData] method. 64 | // 65 | // [QueryGetData]: https://learn.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-idataobject-querygetdata 66 | func (me *IDataObject) QueryGetData(etc *FORMATETC) error { 67 | ret, _, _ := syscall.SyscallN( 68 | (*_IDataObjectVt)(unsafe.Pointer(*me.Ppvt())).QueryGetData, 69 | uintptr(unsafe.Pointer(me.Ppvt())), 70 | uintptr(unsafe.Pointer(etc))) 71 | return utl.ErrorAsHResult(ret) 72 | } 73 | 74 | type _IDataObjectVt struct { 75 | _IUnknownVt 76 | GetData uintptr 77 | GetDataHere uintptr 78 | QueryGetData uintptr 79 | GetCanonicalFormatEtc uintptr 80 | SetData uintptr 81 | EnumFormatEtc uintptr 82 | DAdvise uintptr 83 | DUnadvise uintptr 84 | EnumDAdvise uintptr 85 | } 86 | -------------------------------------------------------------------------------- /co/co_version.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package co 4 | 5 | // [VS_FIXEDFILEINFO] DwFileType. 6 | // 7 | // [VS_FIXEDFILEINFO]: https://learn.microsoft.com/en-us/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo 8 | type VFT uint32 9 | 10 | const ( 11 | VFT_UNKNOWN VFT = 0x0000_0000 12 | VFT_APP VFT = 0x0000_0001 13 | VFT_DLL VFT = 0x0000_0002 14 | VFT_DRV VFT = 0x0000_0003 15 | VFT_FONT VFT = 0x0000_0004 16 | VFT_VXD VFT = 0x0000_0005 17 | VFT_STATIC_LIB VFT = 0x0000_0007 18 | ) 19 | 20 | // [VS_FIXEDFILEINFO] DwFileSubType. 21 | // 22 | // [VS_FIXEDFILEINFO]: https://learn.microsoft.com/en-us/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo 23 | type VFT2 uint32 24 | 25 | const ( 26 | VFT2_UNKNOWN VFT2 = 0x0000_0000 27 | VFT2_DRV_PRINTER VFT2 = 0x0000_0001 28 | VFT2_DRV_KEYBOARD VFT2 = 0x0000_0002 29 | VFT2_DRV_LANGUAGE VFT2 = 0x0000_0003 30 | VFT2_DRV_DISPLAY VFT2 = 0x0000_0004 31 | VFT2_DRV_MOUSE VFT2 = 0x0000_0005 32 | VFT2_DRV_NETWORK VFT2 = 0x0000_0006 33 | VFT2_DRV_SYSTEM VFT2 = 0x0000_0007 34 | VFT2_DRV_INSTALLABLE VFT2 = 0x0000_0008 35 | VFT2_DRV_SOUND VFT2 = 0x0000_0009 36 | VFT2_DRV_COMM VFT2 = 0x0000_000a 37 | VFT2_DRV_INPUTMETHOD VFT2 = 0x0000_000b 38 | VFT2_DRV_VERSIONED_PRINTER VFT2 = 0x0000_000c 39 | 40 | VFT2_FONT_RASTER VFT2 = 0x0000_0001 41 | VFT2_FONT_VECTOR VFT2 = 0x0000_0002 42 | VFT2_FONT_TRUETYPE VFT2 = 0x0000_0003 43 | ) 44 | 45 | // [VS_FIXEDFILEINFO] DwFileOS. 46 | // 47 | // [VS_FIXEDFILEINFO]: https://learn.microsoft.com/en-us/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo 48 | type VOS uint32 49 | 50 | const ( 51 | VOS_UNKNOWN VOS = 0x0000_0000 52 | VOS_DOS VOS = 0x0001_0000 53 | VOS_OS216 VOS = 0x0002_0000 54 | VOS_OS232 VOS = 0x0003_0000 55 | VOS_NT VOS = 0x0004_0000 56 | VOS_WINCE VOS = 0x0005_0000 57 | 58 | VOS_BASE VOS = 0x0000_0000 59 | VOS_WINDOWS16 VOS = 0x0000_0001 60 | VOS_PM16 VOS = 0x0000_0002 61 | VOS_PM32 VOS = 0x0000_0003 62 | VOS_WINDOWS32 VOS = 0x0000_0004 63 | 64 | VOS_DOS_WINDOWS16 VOS = 0x0001_0001 65 | VOS_DOS_WINDOWS32 VOS = 0x0001_0004 66 | VOS_OS216_PM16 VOS = 0x0002_0002 67 | VOS_OS232_PM32 VOS = 0x0003_0003 68 | VOS_NT_WINDOWS32 VOS = 0x0004_0004 69 | ) 70 | 71 | // [VS_FIXEDFILEINFO] DwFileFlagsMask and DwFileFlags. 72 | // 73 | // [VS_FIXEDFILEINFO]: https://learn.microsoft.com/en-us/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo 74 | type VS_FF uint32 75 | 76 | const ( 77 | VS_FF_DEBUG VS_FF = 0x0000_0001 78 | VS_FF_PRERELEASE VS_FF = 0x0000_0002 79 | VS_FF_PATCHED VS_FF = 0x0000_0004 80 | VS_FF_PRIVATEBUILD VS_FF = 0x0000_0008 81 | VS_FF_INFOINFERRED VS_FF = 0x0000_0010 82 | VS_FF_SPECIALBUILD VS_FF = 0x0000_0020 83 | ) 84 | -------------------------------------------------------------------------------- /win/dwmapi_funcs.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/dll" 11 | "github.com/rodrigocfd/windigo/internal/utl" 12 | ) 13 | 14 | // [DwmEnableMMCSS] function. 15 | // 16 | // [DwmEnableMMCSS]: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmenablemmcss 17 | func DwmEnableMMCSS(enable bool) error { 18 | ret, _, _ := syscall.SyscallN( 19 | dll.Load(dll.DWMAPI, &_DwmEnableMMCSS, "DwmEnableMMCSS"), 20 | utl.BoolToUintptr(enable)) 21 | return utl.ErrorAsHResult(ret) 22 | } 23 | 24 | var _DwmEnableMMCSS *syscall.Proc 25 | 26 | // [DwmFlush] function. 27 | // 28 | // [DwmFlush]: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmflush 29 | func DwmFlush() error { 30 | ret, _, _ := syscall.SyscallN( 31 | dll.Load(dll.DWMAPI, &_DwmFlush, "DwmFlush")) 32 | return utl.ErrorAsHResult(ret) 33 | } 34 | 35 | var _DwmFlush *syscall.Proc 36 | 37 | // [DwmGetColorizationColor] function. 38 | // 39 | // [DwmGetColorizationColor]: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmgetcolorizationcolor 40 | func DwmGetColorizationColor() (color COLORREF, isOpaqueBlend bool, hr error) { 41 | var clr COLORREF 42 | var bOpaque int32 // BOOL 43 | 44 | ret, _, _ := syscall.SyscallN( 45 | dll.Load(dll.DWMAPI, &_DwmGetColorizationColor, "DwmGetColorizationColor"), 46 | uintptr(unsafe.Pointer(&clr)), 47 | uintptr(unsafe.Pointer(&bOpaque))) 48 | if hr = co.HRESULT(ret); hr != co.HRESULT_S_OK { 49 | return COLORREF(0), false, hr 50 | } 51 | return clr, bOpaque != 0, nil 52 | } 53 | 54 | var _DwmGetColorizationColor *syscall.Proc 55 | 56 | // [DwmIsCompositionEnabled] function. 57 | // 58 | // [DwmIsCompositionEnabled]: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmiscompositionenabled 59 | func DwmIsCompositionEnabled() (bool, error) { 60 | var pfEnabled int32 // BOOL 61 | ret, _, _ := syscall.SyscallN( 62 | dll.Load(dll.DWMAPI, &_DwmIsCompositionEnabled, "DwmIsCompositionEnabled"), 63 | uintptr(unsafe.Pointer(&pfEnabled))) 64 | if hr := co.HRESULT(ret); hr != co.HRESULT_S_OK { 65 | panic(hr) 66 | } 67 | return pfEnabled != 0, nil 68 | } 69 | 70 | var _DwmIsCompositionEnabled *syscall.Proc 71 | 72 | // [DwmShowContact] function. 73 | // 74 | // Panics if pointerId is negative. 75 | // 76 | // [DwmShowContact]: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmshowcontact 77 | func DwmShowContact(pointerId int, showContact co.DWMSC) error { 78 | utl.PanicNeg(pointerId) 79 | ret, _, _ := syscall.SyscallN( 80 | dll.Load(dll.DWMAPI, &_DwmShowContact, "DwmShowContact"), 81 | uintptr(uint32(pointerId)), 82 | uintptr(showContact)) 83 | return utl.ErrorAsHResult(ret) 84 | } 85 | 86 | var _DwmShowContact *syscall.Proc 87 | -------------------------------------------------------------------------------- /win/comctl_hwnd.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/dll" 11 | "github.com/rodrigocfd/windigo/internal/utl" 12 | ) 13 | 14 | // [DefSubclassProc] function. 15 | // 16 | // [DefSubclassProc]: https://learn.microsoft.com/en-us/windows/win32/api/commctrl/nf-commctrl-defsubclassproc 17 | func (hWnd HWND) DefSubclassProc(msg co.WM, wParam WPARAM, lParam LPARAM) uintptr { 18 | ret, _, _ := syscall.SyscallN( 19 | dll.Load(dll.COMCTL32, &_DefSubclassProc, "DefSubclassProc"), 20 | uintptr(hWnd), 21 | uintptr(msg), 22 | uintptr(wParam), 23 | uintptr(lParam)) 24 | return ret 25 | } 26 | 27 | var _DefSubclassProc *syscall.Proc 28 | 29 | // [ImageList_DragEnter] function. 30 | // 31 | // [ImageList_DragEnter]: https://learn.microsoft.com/en-us/windows/win32/api/commctrl/nf-commctrl-imagelist_dragenter 32 | func (hWnd HWND) ImageListDragEnter(x, y int) error { 33 | ret, _, _ := syscall.SyscallN( 34 | dll.Load(dll.COMCTL32, &_ImageList_DragEnter, "ImageList_DragEnter"), 35 | uintptr(hWnd), 36 | uintptr(int32(x)), 37 | uintptr(int32(y))) 38 | return utl.ZeroAsSysInvalidParm(ret) 39 | } 40 | 41 | var _ImageList_DragEnter *syscall.Proc 42 | 43 | // [ImageList_DragLeave] function. 44 | // 45 | // [ImageList_DragLeave]: https://learn.microsoft.com/en-us/windows/win32/api/commctrl/nf-commctrl-imagelist_dragleave 46 | func (hWnd HWND) ImageListDragLeave() error { 47 | ret, _, _ := syscall.SyscallN( 48 | dll.Load(dll.COMCTL32, &_ImageList_DragLeave, "ImageList_DragLeave"), 49 | uintptr(hWnd)) 50 | return utl.ZeroAsSysInvalidParm(ret) 51 | } 52 | 53 | var _ImageList_DragLeave *syscall.Proc 54 | 55 | // [RemoveWindowSubclass] function. 56 | // 57 | // [RemoveWindowSubclass]: https://learn.microsoft.com/en-us/windows/win32/api/commctrl/nf-commctrl-removewindowsubclass 58 | func (hWnd HWND) RemoveWindowSubclass(subclassProc uintptr, idSubclass uint32) error { 59 | ret, _, _ := syscall.SyscallN( 60 | dll.Load(dll.COMCTL32, &_RemoveWindowSubclass, "RemoveWindowSubclass"), 61 | uintptr(hWnd), 62 | subclassProc, 63 | uintptr(idSubclass)) 64 | return utl.ZeroAsSysInvalidParm(ret) 65 | } 66 | 67 | var _RemoveWindowSubclass *syscall.Proc 68 | 69 | // [SetWindowSubclass] function. 70 | // 71 | // [SetWindowSubclass]: https://learn.microsoft.com/en-us/windows/win32/api/commctrl/nf-commctrl-setwindowsubclass 72 | func (hWnd HWND) SetWindowSubclass( 73 | subclassProc uintptr, 74 | idSubclass uint32, 75 | refData unsafe.Pointer, 76 | ) error { 77 | ret, _, _ := syscall.SyscallN( 78 | dll.Load(dll.COMCTL32, &_SetWindowSubclass, "SetWindowSubclass"), 79 | uintptr(hWnd), 80 | subclassProc, 81 | uintptr(idSubclass), 82 | uintptr(refData)) 83 | return utl.ZeroAsSysInvalidParm(ret) 84 | } 85 | 86 | var _SetWindowSubclass *syscall.Proc 87 | -------------------------------------------------------------------------------- /win/oleaut_IPropertyStore.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/utl" 11 | ) 12 | 13 | // [IPropertyStore] COM interface. 14 | // 15 | // Implements [OleObj] and [OleResource]. 16 | // 17 | // [IPropertyStore]: https://learn.microsoft.com/en-us/windows/win32/api/propsys/nn-propsys-ipropertystore 18 | type IPropertyStore struct{ IUnknown } 19 | 20 | // Returns the unique COM [interface ID]. 21 | // 22 | // [interface ID]: https://learn.microsoft.com/en-us/office/client-developer/outlook/mapi/iid 23 | func (*IPropertyStore) IID() co.IID { 24 | return co.IID_IPropertyStore 25 | } 26 | 27 | // [Commit] method. 28 | // 29 | // [Commit]: https://learn.microsoft.com/en-us/windows/win32/api/propsys/nf-propsys-ipropertystore-commit 30 | func (me *IPropertyStore) Commit() error { 31 | ret, _, _ := syscall.SyscallN( 32 | (*_IPropertyStoreVt)(unsafe.Pointer(*me.Ppvt())).Commit, 33 | uintptr(unsafe.Pointer(me.Ppvt()))) 34 | return utl.ErrorAsHResult(ret) 35 | } 36 | 37 | // Returns all [co.PKEY] values by calling [IPropertyStore.GetCount] and 38 | // [IPropertyStore.GetAt]. 39 | func (me *IPropertyStore) Enum() ([]co.PKEY, error) { 40 | count, hr := me.GetCount() 41 | if hr != nil { 42 | return nil, hr 43 | } 44 | 45 | pkeys := make([]co.PKEY, 0, count) 46 | for i := 0; i < count; i++ { 47 | pkey, hr := me.GetAt(i) 48 | if hr != nil { 49 | return nil, hr 50 | } 51 | pkeys = append(pkeys, pkey) 52 | } 53 | return pkeys, nil 54 | } 55 | 56 | // [GetAt] method. 57 | // 58 | // [GetAt]: https://learn.microsoft.com/en-us/windows/win32/api/propsys/nf-propsys-ipropertystore-getat 59 | func (me *IPropertyStore) GetAt(index int) (co.PKEY, error) { 60 | var guidPkey GUID 61 | ret, _, _ := syscall.SyscallN( 62 | (*_IPropertyStoreVt)(unsafe.Pointer(*me.Ppvt())).GetAt, 63 | uintptr(unsafe.Pointer(me.Ppvt())), 64 | uintptr(uint32(index)), 65 | uintptr(unsafe.Pointer(&guidPkey))) 66 | 67 | if hr := co.HRESULT(ret); hr == co.HRESULT_S_OK { 68 | return co.PKEY(guidPkey.String()), nil 69 | } else { 70 | return co.PKEY(""), hr 71 | } 72 | } 73 | 74 | // [GetCount] method. 75 | // 76 | // [GetCount]: https://learn.microsoft.com/en-us/windows/win32/api/propsys/nf-propsys-ipropertystore-getcount 77 | func (me *IPropertyStore) GetCount() (int, error) { 78 | var cProps uint32 79 | ret, _, _ := syscall.SyscallN( 80 | (*_IPropertyStoreVt)(unsafe.Pointer(*me.Ppvt())).GetCount, 81 | uintptr(unsafe.Pointer(me.Ppvt())), 82 | uintptr(unsafe.Pointer(&cProps))) 83 | 84 | if hr := co.HRESULT(ret); hr == co.HRESULT_S_OK { 85 | return int(cProps), nil 86 | } else { 87 | return 0, hr 88 | } 89 | } 90 | 91 | type _IPropertyStoreVt struct { 92 | _IUnknownVt 93 | GetCount uintptr 94 | GetAt uintptr 95 | GetValue uintptr 96 | SetValue uintptr 97 | Commit uintptr 98 | } 99 | -------------------------------------------------------------------------------- /win/gdi_hfont.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/dll" 11 | "github.com/rodrigocfd/windigo/internal/utl" 12 | "github.com/rodrigocfd/windigo/wstr" 13 | ) 14 | 15 | // Handle to a [font]. 16 | // 17 | // [font]: https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#hfont 18 | type HFONT HGDIOBJ 19 | 20 | // [CreateFont] function. 21 | // 22 | // ⚠️ You must defer [HFONT.DeleteObject]. 23 | // 24 | // [CreateFont]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createfontw 25 | func CreateFont( 26 | height, width int, 27 | escapement int, 28 | orientation int, 29 | weight int, 30 | italic, underline, strikeOut bool, 31 | charSet co.CHARSET, 32 | outPrecision co.OUT_PRECIS, 33 | clipPrecision co.CLIP_PRECIS, 34 | quality co.QUALITY, 35 | pitch co.PITCH, 36 | family co.FF, 37 | faceName string, 38 | ) (HFONT, error) { 39 | var wFaceName wstr.BufEncoder 40 | ret, _, _ := syscall.SyscallN( 41 | dll.Load(dll.GDI32, &_CreateFontW, "CreateFontW"), 42 | uintptr(int32(height)), 43 | uintptr(int32(width)), 44 | uintptr(int32(escapement)), 45 | uintptr(int32(orientation)), 46 | uintptr(int32(height)), 47 | utl.BoolToUintptr(italic), 48 | utl.BoolToUintptr(underline), 49 | utl.BoolToUintptr(strikeOut), 50 | uintptr(charSet), 51 | uintptr(outPrecision), 52 | uintptr(clipPrecision), 53 | uintptr(quality), 54 | uintptr(uint8(pitch)|uint8(family)), 55 | uintptr(wFaceName.EmptyIsNil(faceName))) 56 | if ret == 0 { 57 | return HFONT(0), co.ERROR_INVALID_PARAMETER 58 | } 59 | return HFONT(ret), nil 60 | } 61 | 62 | var _CreateFontW *syscall.Proc 63 | 64 | // [CreateFontIndirect] function. 65 | // 66 | // ⚠️ You must defer [HFONT.DeleteObject]. 67 | // 68 | // [CreateFontIndirect]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createfontindirectw 69 | func CreateFontIndirect(lf *LOGFONT) (HFONT, error) { 70 | ret, _, _ := syscall.SyscallN( 71 | dll.Load(dll.GDI32, &_CreateFontIndirectW, "CreateFontIndirectW"), 72 | uintptr(unsafe.Pointer(lf))) 73 | if ret == 0 { 74 | return HFONT(0), co.ERROR_INVALID_PARAMETER 75 | } 76 | return HFONT(ret), nil 77 | } 78 | 79 | var _CreateFontIndirectW *syscall.Proc 80 | 81 | // [DeleteObject] function. 82 | // 83 | // [DeleteObject]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-deleteobject 84 | func (hFont HFONT) DeleteObject() error { 85 | return HGDIOBJ(hFont).DeleteObject() 86 | } 87 | 88 | // [GetObject] function. 89 | // 90 | // [GetObject]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-getobject 91 | func (hFont HFONT) GetObject() (LOGFONT, error) { 92 | var lf LOGFONT 93 | if err := HGDIOBJ(hFont).GetObject(unsafe.Sizeof(lf), unsafe.Pointer(&lf)); err != nil { 94 | return LOGFONT{}, err 95 | } else { 96 | return lf, nil 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /ui/ctl_TreeViewItems.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ui 4 | 5 | import ( 6 | "github.com/rodrigocfd/windigo/co" 7 | "github.com/rodrigocfd/windigo/win" 8 | ) 9 | 10 | // The items collection. 11 | // 12 | // You cannot create this object directly, it will be created automatically 13 | // by the owning [TreeView]. 14 | type CollectionTreeViewItems struct { 15 | owner *TreeView 16 | } 17 | 18 | // Adds a new root item with [TVM_INSERTITEM], returning the new item. 19 | // 20 | // The iconIndex is the zero-based index of the icon previously inserted into 21 | // the control's image list, or -1 for no icon. 22 | // 23 | // [TVM_INSERTITEM]: https://learn.microsoft.com/en-us/windows/win32/controls/tvm-insertitem 24 | func (me *CollectionTreeViewItems) AddRoot(text string, iconIndex int) TreeViewItem { 25 | return me.Get(win.HTREEITEM(0)). 26 | AddChild(text, iconIndex) 27 | } 28 | 29 | // Retrieves the total number of items in the control, with [TVM_GETCOUNT]. 30 | // 31 | // [TVM_GETCOUNT]: https://learn.microsoft.com/en-us/windows/win32/controls/tvm-getcount 32 | func (me *CollectionTreeViewItems) Count() int { 33 | c, _ := me.owner.hWnd.SendMessage(co.TVM_GETCOUNT, 0, 0) 34 | return int(c) 35 | } 36 | 37 | // Deletes all items at once with [TVM_DELETEITEM]. 38 | // 39 | // Panics on error. 40 | // 41 | // [TVM_DELETEITEM]: https://learn.microsoft.com/en-us/windows/win32/controls/tvm-deleteitem 42 | func (me *CollectionTreeViewItems) DeleteAll() { 43 | ret, err := me.owner.hWnd.SendMessage(co.TVM_DELETEITEM, 44 | 0, win.LPARAM(win.HTREEITEM(0))) 45 | if ret == 0 || err != nil { 46 | panic("TVM_DELETEITEM for all items failed.") 47 | } 48 | } 49 | 50 | // Retrieves the first visible item, if any, with [TVM_GETNEXTITEM]. 51 | // 52 | // [TVM_GETNEXTITEM]: https://learn.microsoft.com/en-us/windows/win32/controls/tvm-getnextitem 53 | func (me *CollectionTreeViewItems) FirstVisible() (TreeViewItem, bool) { 54 | hVisible, _ := me.owner.hWnd.SendMessage(co.TVM_GETNEXTITEM, 55 | win.WPARAM(co.TVGN_FIRSTVISIBLE), win.LPARAM(win.HTREEITEM(0))) 56 | if hVisible != 0 { 57 | return TreeViewItem{me.owner, win.HTREEITEM(hVisible)}, true 58 | } 59 | return TreeViewItem{}, false 60 | } 61 | 62 | // Returns the item with the given handle. 63 | func (me *CollectionTreeViewItems) Get(hItem win.HTREEITEM) TreeViewItem { 64 | return TreeViewItem{me.owner, hItem} 65 | } 66 | 67 | // Returns the root items with [TVM_GETNEXTITEM]. 68 | // 69 | // [TVM_GETNEXTITEM]: https://learn.microsoft.com/en-us/windows/win32/controls/tvm-getnextitem 70 | func (me *CollectionTreeViewItems) Roots() []TreeViewItem { 71 | roof := TreeViewItem{me.owner, win.HTREEITEM(0)} 72 | return roof.Children() 73 | } 74 | 75 | // Retrieves the selected item, if any, with [TVM_GETNEXTITEM]. 76 | // 77 | // [TVM_GETNEXTITEM]: https://learn.microsoft.com/en-us/windows/win32/controls/tvm-getnextitem 78 | func (me *CollectionTreeViewItems) Selected() (TreeViewItem, bool) { 79 | hItem, _ := me.owner.hWnd.SendMessage(co.TVM_GETNEXTITEM, 80 | win.WPARAM(co.TVGN_CARET), win.LPARAM(win.HTREEITEM(0))) 81 | if hItem != 0 { 82 | return TreeViewItem{me.owner, win.HTREEITEM(hItem)}, true 83 | } 84 | return TreeViewItem{}, false 85 | } 86 | -------------------------------------------------------------------------------- /ui/ctl_TabItem.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ui 4 | 5 | import ( 6 | "fmt" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/win" 11 | "github.com/rodrigocfd/windigo/wstr" 12 | ) 13 | 14 | // An item from a [tab]. 15 | // 16 | // [tab]: https://learn.microsoft.com/en-us/windows/win32/controls/tab-controls 17 | type TabItem struct { 18 | owner *Tab 19 | index int32 20 | } 21 | 22 | func (me TabItem) displayContent() { 23 | if len(me.owner.children) == 0 { 24 | return 25 | } 26 | 27 | for idx, child := range me.owner.children { 28 | if idx != int(me.index) { 29 | child.Content.Hwnd().ShowWindow(co.SW_HIDE) // hide all others 30 | } 31 | } 32 | 33 | hParent, _ := me.owner.hWnd.GetParent() 34 | rcTab, _ := me.owner.hWnd.GetWindowRect() 35 | hParent.ScreenToClientRc(&rcTab) 36 | me.owner.hWnd.SendMessage(co.TCM_ADJUSTRECT, 0, win.LPARAM(unsafe.Pointer(&rcTab))) // ideal child size 37 | me.owner.children[me.index].Content.Hwnd(). 38 | SetWindowPos(win.HWND(0), int(rcTab.Left), int(rcTab.Top), // resize child to ideal size 39 | int(rcTab.Right-rcTab.Left), int(rcTab.Bottom-rcTab.Top), 40 | co.SWP_NOZORDER|co.SWP_SHOWWINDOW) 41 | } 42 | 43 | // Returns the zero-based index of the item. 44 | func (me TabItem) Index() int { 45 | return int(me.index) 46 | } 47 | 48 | // Selects this tab with [TCM_SETCURSEL]. 49 | // 50 | // Returns the same item, so further operations can be chained. 51 | // 52 | // [TCM_SETCURSEL]: https://learn.microsoft.com/en-us/windows/win32/controls/tcm-setcursel 53 | func (me TabItem) Select() TabItem { 54 | me.owner.hWnd.SendMessage(co.TCM_SETCURSEL, win.WPARAM(int32(me.index)), 0) 55 | me.displayContent() // because notification is not sent 56 | return me 57 | } 58 | 59 | // Sets the text with [TCM_SETITEM]. 60 | // 61 | // Returns the same item, so further operations can be chained. 62 | // 63 | // Panics on error. 64 | // 65 | // [TCM_SETITEM]: https://learn.microsoft.com/en-us/windows/win32/controls/tcm-setitem 66 | func (me TabItem) SetText(text string) TabItem { 67 | tci := win.TCITEM{ 68 | Mask: co.TCIF_TEXT, 69 | } 70 | 71 | var wText wstr.BufEncoder 72 | tci.SetPszText(wText.Slice(text)) 73 | 74 | ret, err := me.owner.hWnd.SendMessage(co.TCM_SETITEM, 75 | win.WPARAM(int32(me.index)), win.LPARAM(unsafe.Pointer(&tci))) 76 | if err != nil || ret == 0 { 77 | panic(fmt.Sprintf("TCM_SETITEM %d to \"%s\" failed.", me.index, text)) 78 | } 79 | 80 | return me 81 | } 82 | 83 | // Retrieves the text with [TCM_GETITEM]. 84 | // 85 | // Panics on error. 86 | // 87 | // [TCM_GETITEM]: https://learn.microsoft.com/en-us/windows/win32/controls/tcm-getitem 88 | func (me TabItem) Text() string { 89 | tci := win.TCITEM{ 90 | Mask: co.TCIF_TEXT, 91 | } 92 | 93 | var wBuf wstr.BufDecoder 94 | wBuf.Alloc(wstr.BUF_MAX) 95 | tci.SetPszText(wBuf.HotSlice()) 96 | 97 | ret, err := me.owner.hWnd.SendMessage(co.TCM_GETITEM, 98 | win.WPARAM(int32(me.index)), win.LPARAM(unsafe.Pointer(&tci))) 99 | if err != nil || ret == 0 { 100 | panic(fmt.Sprintf("TCM_GETITEM %d failed.", me.index)) 101 | } 102 | 103 | return wBuf.String() 104 | } 105 | -------------------------------------------------------------------------------- /win/user_hicon.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/dll" 11 | "github.com/rodrigocfd/windigo/internal/utl" 12 | ) 13 | 14 | // Handle to an [icon]. 15 | // 16 | // [icon]: https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#hicon 17 | type HICON HANDLE 18 | 19 | // [CreateIconIndirect] function. 20 | // 21 | // ⚠️ You must defer [HICON.DestroyIcon]. 22 | // 23 | // [CreateIconIndirect]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createiconindirect 24 | func CreateIconIndirect(info *ICONINFO) (HICON, error) { 25 | ret, _, err := syscall.SyscallN( 26 | dll.Load(dll.USER32, &_CreateIconIndirect, "CreateIconIndirect"), 27 | uintptr(unsafe.Pointer(info))) 28 | if ret == 0 { 29 | return HICON(0), co.ERROR(err) 30 | } 31 | return HICON(ret), nil 32 | } 33 | 34 | var _CreateIconIndirect *syscall.Proc 35 | 36 | // [CopyIcon] function. 37 | // 38 | // ⚠️ You must defer [HICON.DestroyIcon]. 39 | // 40 | // [CopyIcon]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-copyicon 41 | func (hIcon HICON) CopyIcon() (HICON, error) { 42 | ret, _, err := syscall.SyscallN( 43 | dll.Load(dll.USER32, &_CopyIcon, "CopyIcon"), 44 | uintptr(hIcon)) 45 | if ret == 0 { 46 | return HICON(0), co.ERROR(err) 47 | } 48 | return HICON(ret), nil 49 | } 50 | 51 | var _CopyIcon *syscall.Proc 52 | 53 | // [DestroyIcon] function. 54 | // 55 | // [DestroyIcon]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-destroyicon 56 | func (hIcon HICON) DestroyIcon() error { 57 | ret, _, err := syscall.SyscallN( 58 | dll.Load(dll.USER32, &_DestroyIcon, "DestroyIcon"), 59 | uintptr(hIcon)) 60 | return utl.ZeroAsGetLastError(ret, err) 61 | } 62 | 63 | var _DestroyIcon *syscall.Proc 64 | 65 | // [GetIconInfo] function. 66 | // 67 | // ⚠️ You must defer [HBITMAP.DeleteObject] in HbmMask and HbmColor fields. 68 | // 69 | // [GetIconInfo]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-geticoninfo 70 | func (hIcon HICON) GetIconInfo() (ICONINFO, error) { 71 | var ii ICONINFO 72 | ret, _, err := syscall.SyscallN( 73 | dll.Load(dll.USER32, &_GetIconInfo, "GetIconInfo"), 74 | uintptr(hIcon), 75 | uintptr(unsafe.Pointer(&ii))) 76 | if ret == 0 { 77 | return ICONINFO{}, co.ERROR(err) 78 | } 79 | return ii, nil 80 | } 81 | 82 | var _GetIconInfo *syscall.Proc 83 | 84 | // [GetIconInfoEx] function. 85 | // 86 | // ⚠️ You must defer [HBITMAP.DeleteObject] in HbmMask and HbmColor fields. 87 | // 88 | // [GetIconInfoEx]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-geticoninfoexw 89 | func (hIcon HICON) GetIconInfoEx() (ICONINFOEX, error) { 90 | var ii ICONINFOEX 91 | ii.SetCbSize() 92 | 93 | ret, _, _ := syscall.SyscallN( 94 | dll.Load(dll.USER32, &_GetIconInfoExW, "GetIconInfoExW"), 95 | uintptr(hIcon), 96 | uintptr(unsafe.Pointer(&ii))) 97 | if ret == 0 { 98 | return ICONINFOEX{}, co.ERROR_INVALID_PARAMETER 99 | } 100 | return ii, nil 101 | } 102 | 103 | var _GetIconInfoExW *syscall.Proc 104 | -------------------------------------------------------------------------------- /win/oleaut_funcs.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/dll" 11 | "github.com/rodrigocfd/windigo/internal/utl" 12 | "github.com/rodrigocfd/windigo/wstr" 13 | ) 14 | 15 | // [OleLoadPicture] function. 16 | // 17 | // Pass size = 0 to read all the bytes from the stream. 18 | // 19 | // The bytes are copied, so [IStream] can be released after this function 20 | // returns. 21 | // 22 | // The picture must be in the following formats: 23 | // - BMP (bitmap) 24 | // - JPEG 25 | // - WMF (metafile) 26 | // - ICO (icon) 27 | // - GIF 28 | // 29 | // If the image format is not supported, returns 30 | // [co.HRESULT_CTL_E_INVALIDPICTURE]. 31 | // 32 | // Example: 33 | // 34 | // rel := win.NewOleReleaser() 35 | // defer rel.Release() 36 | // 37 | // data := []byte{0x10, 0x11, 0x12} 38 | // defer runtime.KeepAlive(data) 39 | // 40 | // stream, _ := win.SHCreateMemStream(rel, data) 41 | // pic, _ := win.OleLoadPicture(rel, stream, 0, true) 42 | // 43 | // [OleLoadPicture]: https://learn.microsoft.com/en-us/windows/win32/api/olectl/nf-olectl-oleloadpicture 44 | func OleLoadPicture( 45 | releaser *OleReleaser, 46 | stream *IStream, 47 | size int, 48 | keepOriginalFormat bool, 49 | ) (*IPicture, error) { 50 | var ppvtQueried **_IUnknownVt 51 | guid := GuidFrom(co.IID_IPicture) 52 | 53 | ret, _, _ := syscall.SyscallN( 54 | dll.Load(dll.OLEAUT32, &_OleLoadPicture, "OleLoadPicture"), 55 | uintptr(unsafe.Pointer(stream.Ppvt())), 56 | uintptr(int32(size)), 57 | utl.BoolToUintptr(!keepOriginalFormat), // note: reversed 58 | uintptr(unsafe.Pointer(&guid)), 59 | uintptr(unsafe.Pointer(&ppvtQueried))) 60 | 61 | if hr := co.HRESULT(ret); hr == co.HRESULT_S_OK { 62 | pObj := &IPicture{IUnknown{ppvtQueried}} 63 | releaser.Add(pObj) 64 | return pObj, nil 65 | } else { 66 | return nil, hr 67 | } 68 | } 69 | 70 | var _OleLoadPicture *syscall.Proc 71 | 72 | // [OleLoadPicturePath] function. 73 | // 74 | // The picture must be in the following formats: 75 | // - BMP (bitmap) 76 | // - JPEG 77 | // - WMF (metafile) 78 | // - ICO (icon) 79 | // - GIF 80 | // 81 | // If the image format is not supported, returns 82 | // [co.HRESULT_CTL_E_INVALIDPICTURE]. 83 | // 84 | // [OleLoadPicturePath]: https://learn.microsoft.com/en-us/windows/win32/api/olectl/nf-olectl-oleloadpicturepath 85 | func OleLoadPicturePath( 86 | releaser *OleReleaser, 87 | path string, 88 | transparentColor COLORREF, 89 | ) (*IPicture, error) { 90 | var wPath wstr.BufEncoder 91 | var ppvtQueried **_IUnknownVt 92 | guid := GuidFrom(co.IID_IPicture) 93 | 94 | ret, _, _ := syscall.SyscallN( 95 | dll.Load(dll.OLEAUT32, &_OleLoadPicturePath, "OleLoadPicturePath"), 96 | uintptr(wPath.EmptyIsNil(path)), 97 | 0, 0, 98 | uintptr(transparentColor), 99 | uintptr(unsafe.Pointer(&guid)), 100 | uintptr(unsafe.Pointer(&ppvtQueried))) 101 | 102 | if hr := co.HRESULT(ret); hr == co.HRESULT_S_OK { 103 | pObj := &IPicture{IUnknown{ppvtQueried}} 104 | releaser.Add(pObj) 105 | return pObj, nil 106 | } else { 107 | return nil, hr 108 | } 109 | } 110 | 111 | var _OleLoadPicturePath *syscall.Proc 112 | -------------------------------------------------------------------------------- /win/shell_hdrop.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/dll" 11 | "github.com/rodrigocfd/windigo/wstr" 12 | ) 13 | 14 | // Handle to an [internal drop structure]. 15 | // 16 | // [internal drop structure]: https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#hdrop 17 | type HDROP HANDLE 18 | 19 | // [DragFinish] function. 20 | // 21 | // If you're using [RegisterDragDrop], don't call this function. 22 | // 23 | // [DragFinish]: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-dragfinish 24 | // [RegisterDragDrop]: https://learn.microsoft.com/en-us/windows/win32/api/ole/nf-ole-registerdragdrop 25 | func (hDrop HDROP) DragFinish() { 26 | syscall.SyscallN( 27 | dll.Load(dll.SHELL32, &_DragFinish, "DragFinish"), 28 | uintptr(hDrop)) 29 | } 30 | 31 | var _DragFinish *syscall.Proc 32 | 33 | // [DragQueryFile] function. Called internally several times until all files are 34 | // retrieved, then the full paths are returned. 35 | // 36 | // ⚠️ If this HDROP comes from an operation from [co.WS_EX_ACCEPTFILES], you 37 | // must defer [HDROP.DragFinish]. If it comes from [RegisterDragDrop], don't 38 | // call it. 39 | // 40 | // Example: 41 | // 42 | // var hDrop win.HDROP // initialized somewhere 43 | // 44 | // // defer hDrop.DragFinish() // only if you're not using RegisterDragDrop() 45 | // 46 | // files, _ := hDrop.DragQueryFile() 47 | // for _, file := range files { 48 | // println(file) 49 | // } 50 | // 51 | // [DragQueryFile]: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-dragqueryfilew 52 | // [RegisterDragDrop]: https://learn.microsoft.com/en-us/windows/win32/api/ole/nf-ole-registerdragdrop 53 | func (hDrop HDROP) DragQueryFile() ([]string, error) { 54 | ret, _, _ := syscall.SyscallN( 55 | dll.Load(dll.SHELL32, &_DragQueryFileW, "DragQueryFileW"), 56 | uintptr(hDrop), 57 | uintptr(0xffff_ffff), 0, 0) 58 | if ret == 0 { 59 | return nil, co.ERROR_INVALID_PARAMETER 60 | } 61 | 62 | var wBuf wstr.BufDecoder 63 | wBuf.Alloc(wstr.BUF_MAX) 64 | 65 | count := uint32(ret) 66 | paths := make([]string, 0, count) 67 | 68 | for i := uint32(0); i < count; i++ { 69 | wBuf.Zero() 70 | ret, _, _ = syscall.SyscallN( 71 | dll.Load(dll.SHELL32, &_DragQueryFileW, "DragQueryFileW"), 72 | uintptr(hDrop), 73 | uintptr(i), 74 | uintptr(wBuf.Ptr()), 75 | uintptr(uint32(wBuf.Len()))) 76 | if ret == 0 { 77 | return nil, co.ERROR_INVALID_PARAMETER 78 | } 79 | paths = append(paths, wBuf.String()) 80 | } 81 | 82 | return paths, nil 83 | } 84 | 85 | var _DragQueryFileW *syscall.Proc 86 | 87 | // [DragQueryPoint] function. 88 | // 89 | // Returns true if dropped within client area. 90 | // 91 | // [DragQueryPoint]: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-dragquerypoint 92 | func (hDrop HDROP) DragQueryPoint() (POINT, bool) { 93 | var pt POINT 94 | ret, _, _ := syscall.SyscallN( 95 | dll.Load(dll.SHELL32, &_DragQueryPoint, "DragQueryPoint"), 96 | uintptr(hDrop), 97 | uintptr(unsafe.Pointer(&pt))) 98 | return pt, ret != 0 99 | } 100 | 101 | var _DragQueryPoint *syscall.Proc 102 | -------------------------------------------------------------------------------- /win/gdi_hpen.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/dll" 11 | ) 12 | 13 | // Handle to a [pen]. 14 | // 15 | // [pen]: https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#hpen 16 | type HPEN HGDIOBJ 17 | 18 | // [CreatePen] function. 19 | // 20 | // ⚠️ You must defer [HPEN.DeleteObject]. 21 | // 22 | // [CreatePen]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createpen 23 | func CreatePen(style co.PS, width int, color COLORREF) (HPEN, error) { 24 | ret, _, _ := syscall.SyscallN( 25 | dll.Load(dll.GDI32, &_CreatePen, "CreatePen"), 26 | uintptr(style), 27 | uintptr(int32(width)), 28 | uintptr(color)) 29 | if ret == 0 { 30 | return HPEN(0), co.ERROR_INVALID_PARAMETER 31 | } 32 | return HPEN(ret), nil 33 | } 34 | 35 | var _CreatePen *syscall.Proc 36 | 37 | // [CreatePenIndirect] function. 38 | // 39 | // ⚠️ You must defer [HPEN.DeleteObject]. 40 | // 41 | // [CreatePenIndirect]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createpenindirect 42 | func CreatePenIndirect(lp *LOGPEN) (HPEN, error) { 43 | ret, _, _ := syscall.SyscallN( 44 | dll.Load(dll.GDI32, &_CreatePenIndirect, "CreatePenIndirect"), 45 | uintptr(unsafe.Pointer(lp))) 46 | if ret == 0 { 47 | return HPEN(0), co.ERROR_INVALID_PARAMETER 48 | } 49 | return HPEN(ret), nil 50 | } 51 | 52 | var _CreatePenIndirect *syscall.Proc 53 | 54 | // [ExtCreatePen] function. 55 | // 56 | // ⚠️ You must defer [HPEN.DeleteObject]. 57 | // 58 | // [ExtCreatePen]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-extcreatepen 59 | func ExtCreatePen( 60 | penType co.PS_TYPE, 61 | penStyle co.PS_STYLE, 62 | endCap co.PS_ENDCAP, 63 | width int, 64 | brush *LOGBRUSH, 65 | styleLengths []int, 66 | ) (HPEN, error) { 67 | var pLens unsafe.Pointer 68 | if styleLengths != nil { 69 | lens32 := make([]uint32, 0, len(styleLengths)) 70 | for _, sl := range styleLengths { 71 | lens32 = append(lens32, uint32(sl)) 72 | } 73 | pLens = unsafe.Pointer(&lens32[0]) 74 | } 75 | 76 | ret, _, _ := syscall.SyscallN( 77 | dll.Load(dll.GDI32, &_ExtCreatePen, "ExtCreatePen"), 78 | uintptr(uint32(penType)|uint32(penStyle)|uint32(endCap)), 79 | uintptr(uint32(width)), 80 | uintptr(unsafe.Pointer(brush)), 81 | uintptr(uint32(len(styleLengths))), 82 | uintptr(pLens)) 83 | if ret == 0 { 84 | return HPEN(0), co.ERROR_INVALID_PARAMETER 85 | } 86 | return HPEN(ret), nil 87 | } 88 | 89 | var _ExtCreatePen *syscall.Proc 90 | 91 | // [DeleteObject] function. 92 | // 93 | // [DeleteObject]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-deleteobject 94 | func (hPen HPEN) DeleteObject() error { 95 | return HGDIOBJ(hPen).DeleteObject() 96 | } 97 | 98 | // [GetObject] function. 99 | // 100 | // [GetObject]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-getobject 101 | func (hPen HPEN) GetObject() (LOGPEN, error) { 102 | var lp LOGPEN 103 | if err := HGDIOBJ(hPen).GetObject(unsafe.Sizeof(lp), unsafe.Pointer(&lp)); err != nil { 104 | return LOGPEN{}, err 105 | } else { 106 | return lp, nil 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /win/kernel_hinstance.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | 8 | "github.com/rodrigocfd/windigo/co" 9 | "github.com/rodrigocfd/windigo/internal/dll" 10 | "github.com/rodrigocfd/windigo/internal/utl" 11 | "github.com/rodrigocfd/windigo/wstr" 12 | ) 13 | 14 | // Handle to an [instance]. This is the base address of the module in memory. 15 | // 16 | // [instance]: https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#hinstance 17 | type HINSTANCE HANDLE 18 | 19 | // [GetModuleHandle] function. 20 | // 21 | // Example: 22 | // 23 | // Retrieving own .exe handle: 24 | // 25 | // hInst, _ := win.GetModuleHandle("") 26 | // 27 | // [GetModuleHandle]: https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandlew 28 | func GetModuleHandle(moduleName string) (HINSTANCE, error) { 29 | var wModuleName wstr.BufEncoder 30 | ret, _, err := syscall.SyscallN( 31 | dll.Load(dll.KERNEL32, &_GetModuleHandleW, "GetModuleHandleW"), 32 | uintptr(wModuleName.EmptyIsNil(moduleName))) 33 | if ret == 0 { 34 | return HINSTANCE(0), co.ERROR(err) 35 | } 36 | return HINSTANCE(ret), nil 37 | } 38 | 39 | var _GetModuleHandleW *syscall.Proc 40 | 41 | // [LoadLibrary] function. 42 | // 43 | // ⚠️ You must defer [HINSTANCE.FreeLibrary]. 44 | // 45 | // [LoadLibrary]: https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryw 46 | func LoadLibrary(libFileName string) (HINSTANCE, error) { 47 | var wLibFileName wstr.BufEncoder 48 | ret, _, err := syscall.SyscallN( 49 | dll.Load(dll.KERNEL32, &_LoadLibraryW, "LoadLibraryW"), 50 | uintptr(wLibFileName.EmptyIsNil(libFileName))) 51 | if ret == 0 { 52 | return HINSTANCE(0), co.ERROR(err) 53 | } 54 | return HINSTANCE(ret), nil 55 | } 56 | 57 | var _LoadLibraryW *syscall.Proc 58 | 59 | // [FreeLibrary] function. 60 | // 61 | // Paired with [LoadLibrary]. 62 | // 63 | // [FreeLibrary]: https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-freelibrary 64 | func (hInst HINSTANCE) FreeLibrary() error { 65 | ret, _, err := syscall.SyscallN( 66 | dll.Load(dll.KERNEL32, &_FreeLibrary, "FreeLibrary"), 67 | uintptr(hInst)) 68 | return utl.ZeroAsGetLastError(ret, err) 69 | } 70 | 71 | var _FreeLibrary *syscall.Proc 72 | 73 | // [GetModuleFileName] function. 74 | // 75 | // Example: 76 | // 77 | // Retrieving own .exe path: 78 | // 79 | // exePath, _ := win.HINSTANCE(0).GetModuleFileName() 80 | // println(exePath) 81 | // 82 | // [GetModuleFileName]: https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulefilenamew 83 | func (hInst HINSTANCE) GetModuleFileName() (string, error) { 84 | sz := wstr.BUF_MAX 85 | var wBuf wstr.BufDecoder 86 | wBuf.Alloc(sz) 87 | 88 | for { 89 | ret, _, err := syscall.SyscallN( 90 | dll.Load(dll.KERNEL32, &_GetModuleFileNameW, "GetModuleFileNameW"), 91 | uintptr(hInst), 92 | uintptr(wBuf.Ptr()), 93 | uintptr(uint32(sz))) 94 | if ret == 0 { 95 | return "", co.ERROR(err) 96 | } 97 | chCopied := int(ret) + 1 // plus terminating null count 98 | 99 | if chCopied < sz { // to break, must have at least 1 char gap 100 | return wBuf.String(), nil 101 | } 102 | 103 | sz += 64 104 | wBuf.AllocAndZero(sz) // increase buffer size to try again 105 | } 106 | } 107 | 108 | var _GetModuleFileNameW *syscall.Proc 109 | -------------------------------------------------------------------------------- /win/util_Version.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "fmt" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/wstr" 11 | ) 12 | 13 | // Version information from an EXE or DLL, loaded with [GetFileVersionInfo] and 14 | // [VerQueryValue]. 15 | // 16 | // Example: 17 | // 18 | // hInst, _ := win.GetModuleHandle("") 19 | // exeName, _ := hInst.GetModuleFileName() 20 | // info, _ := win.VersionLoad(exeName) 21 | type VersionInfo struct { 22 | Version [4]uint16 23 | LangId LANGID 24 | CodePage co.CP 25 | Comments string 26 | CompanyName string 27 | FileDescription string 28 | FileVersion string 29 | InternalName string 30 | LegalCopyright string 31 | LegalTrademarks string 32 | OriginalFilename string 33 | ProductName string 34 | ProductVersion string 35 | PrivateBuild string 36 | SpecialBuild string 37 | } 38 | 39 | // Loads the embedded version information from an EXE or DLL, with 40 | // [GetFileVersionInfo] and [VerQueryValue]. 41 | // 42 | // Example: 43 | // 44 | // hInst, _ := win.GetModuleHandle("") 45 | // exeName, _ := hInst.GetModuleFileName() 46 | // info, _ := win.VersionLoad(exeName) 47 | func VersionLoad(moduleName string) (VersionInfo, error) { 48 | szData, err := GetFileVersionInfoSize(moduleName) 49 | if err != nil { 50 | return VersionInfo{}, fmt.Errorf("VersionLoad GetFileVersionInfoSize: %w", err) 51 | } 52 | 53 | data := NewVecSized(szData, byte(0)) 54 | defer data.Free() 55 | 56 | if err := GetFileVersionInfo(moduleName, data.HotSlice()); err != nil { 57 | return VersionInfo{}, fmt.Errorf("VersionLoad GetFileVersionInfo: %w", err) 58 | } 59 | 60 | var v VersionInfo // to be returned 61 | 62 | if pNfoRaw, _, ok := VerQueryValue(data.HotSlice(), "\\"); ok { 63 | pNfo := (*VS_FIXEDFILEINFO)(pNfoRaw) 64 | v.Version[0], v.Version[1], v.Version[2], v.Version[3] = pNfo.FileVersion() 65 | } 66 | 67 | type Block struct { 68 | LangId LANGID 69 | CodePage co.CP 70 | } 71 | 72 | if pBlocks, count, ok := VerQueryValue( 73 | data.HotSlice(), "\\VarFileInfo\\Translation"); ok && count > 0 { 74 | 75 | blocks := unsafe.Slice((*Block)(pBlocks), count) 76 | bl := blocks[0] // we'll load the 1st block only, which is the most common case 77 | 78 | v.LangId = bl.LangId 79 | v.CodePage = bl.CodePage 80 | 81 | getStr := func(id string) string { 82 | if pStr, nChars, ok := VerQueryValue(data.HotSlice(), 83 | fmt.Sprintf("\\StringFileInfo\\%04x%04x\\%s", 84 | bl.LangId, bl.CodePage, id)); ok { 85 | 86 | wideStr := unsafe.Slice((*uint16)(pStr), nChars-1) // don't include terminating null 87 | return wstr.DecodeSlice(wideStr) 88 | } 89 | return "" 90 | } 91 | 92 | v.Comments = getStr("Comments") 93 | v.CompanyName = getStr("CompanyName") 94 | v.FileDescription = getStr("FileDescription") 95 | v.FileVersion = getStr("FileVersion") 96 | v.InternalName = getStr("InternalName") 97 | v.LegalCopyright = getStr("LegalCopyright") 98 | v.LegalTrademarks = getStr("LegalTrademarks") 99 | v.OriginalFilename = getStr("OriginalFilename") 100 | v.ProductName = getStr("ProductName") 101 | v.ProductVersion = getStr("ProductVersion") 102 | v.PrivateBuild = getStr("PrivateBuild") 103 | v.SpecialBuild = getStr("SpecialBuild") 104 | } 105 | 106 | return v, nil 107 | } 108 | -------------------------------------------------------------------------------- /win/shell_ITaskbarList.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/utl" 11 | ) 12 | 13 | // [ITaskbarList] COM interface. 14 | // 15 | // Implements [OleObj] and [OleResource]. 16 | // 17 | // Example: 18 | // 19 | // _, _ = win.CoInitializeEx( 20 | // co.COINIT_APARTMENTTHREADED | co.COINIT_DISABLE_OLE1DDE) 21 | // defer win.CoUninitialize() 22 | // 23 | // rel := win.NewOleReleaser() 24 | // defer rel.Release() 25 | // 26 | // var taskbl *win.ITaskbarList 27 | // _ = win.CoCreateInstance( 28 | // rel, 29 | // co.CLSID_TaskbarList, 30 | // nil, 31 | // co.CLSCTX_INPROC_SERVER, 32 | // &taskbl, 33 | // ) 34 | // 35 | // [ITaskbarList]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-itaskbarlist 36 | type ITaskbarList struct{ IUnknown } 37 | 38 | // Returns the unique COM [interface ID]. 39 | // 40 | // [interface ID]: https://learn.microsoft.com/en-us/office/client-developer/outlook/mapi/iid 41 | func (*ITaskbarList) IID() co.IID { 42 | return co.IID_ITaskbarList 43 | } 44 | 45 | // [ActivateTab] method. 46 | // 47 | // [ActivateTab]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-itaskbarlist-activatetab 48 | func (me *ITaskbarList) ActivateTab(hWnd HWND) error { 49 | ret, _, _ := syscall.SyscallN( 50 | (*_ITaskbarListVt)(unsafe.Pointer(*me.Ppvt())).ActivateTab, 51 | uintptr(unsafe.Pointer(me.Ppvt())), 52 | uintptr(hWnd)) 53 | return utl.ErrorAsHResult(ret) 54 | } 55 | 56 | // [AddTab] method. 57 | // 58 | // [AddTab]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-itaskbarlist-addtab 59 | func (me *ITaskbarList) AddTab(hWnd HWND) error { 60 | ret, _, _ := syscall.SyscallN( 61 | (*_ITaskbarListVt)(unsafe.Pointer(*me.Ppvt())).AddTab, 62 | uintptr(unsafe.Pointer(me.Ppvt())), 63 | uintptr(hWnd)) 64 | return utl.ErrorAsHResult(ret) 65 | } 66 | 67 | // [DeleteTab] method. 68 | // 69 | // [DeleteTab]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-itaskbarlist-deletetab 70 | func (me *ITaskbarList) DeleteTab(hWnd HWND) error { 71 | ret, _, _ := syscall.SyscallN( 72 | (*_ITaskbarListVt)(unsafe.Pointer(*me.Ppvt())).DeleteTab, 73 | uintptr(unsafe.Pointer(me.Ppvt())), 74 | uintptr(hWnd)) 75 | return utl.ErrorAsHResult(ret) 76 | } 77 | 78 | // [HrInit] method. 79 | // 80 | // [HrInit]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-itaskbarlist-hrinit 81 | func (me *ITaskbarList) HrInit() error { 82 | ret, _, _ := syscall.SyscallN( 83 | (*_ITaskbarListVt)(unsafe.Pointer(*me.Ppvt())).HrInit, 84 | uintptr(unsafe.Pointer(me.Ppvt()))) 85 | return utl.ErrorAsHResult(ret) 86 | } 87 | 88 | // [SetActiveAlt] method. 89 | // 90 | // [SetActiveAlt]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-itaskbarlist-setactivealt 91 | func (me *ITaskbarList) SetActiveAlt(hWnd HWND) error { 92 | ret, _, _ := syscall.SyscallN( 93 | (*_ITaskbarListVt)(unsafe.Pointer(*me.Ppvt())).SetActiveAlt, 94 | uintptr(unsafe.Pointer(me.Ppvt())), 95 | uintptr(hWnd)) 96 | return utl.ErrorAsHResult(ret) 97 | } 98 | 99 | type _ITaskbarListVt struct { 100 | _IUnknownVt 101 | HrInit uintptr 102 | AddTab uintptr 103 | DeleteTab uintptr 104 | ActivateTab uintptr 105 | SetActiveAlt uintptr 106 | } 107 | -------------------------------------------------------------------------------- /win/kernel_hfilemap.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/dll" 11 | "github.com/rodrigocfd/windigo/internal/utl" 12 | ) 13 | 14 | // Handle to a memory-mapped [file]. 15 | // 16 | // [file]: https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-createfilemappingw 17 | type HFILEMAP HANDLE 18 | 19 | // [CloseHandle] function. 20 | // 21 | // [CloseHandle]: https://learn.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle 22 | func (hMap HFILEMAP) CloseHandle() error { 23 | return HANDLE(hMap).CloseHandle() 24 | } 25 | 26 | // [MapViewOfFile] function. 27 | // 28 | // The offset will be rounded down to a multiple of the allocation granularity, 29 | // which is taken with [GetSystemInfo]. 30 | // 31 | // Note that this function may present issues in x86 architectures. 32 | // 33 | // Panics if offset or numBytesToMap is negative. 34 | // 35 | // ⚠️ You must defer [HFILEMAPVIEW.UnmapViewOfFile]. 36 | // 37 | // [MapViewOfFile]: https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-mapviewoffile 38 | func (hMap HFILEMAP) MapViewOfFile( 39 | desiredAccess co.FILE_MAP, 40 | offset int, 41 | numBytesToMap int, 42 | ) (HFILEMAPVIEW, error) { 43 | utl.PanicNeg(offset, numBytesToMap) 44 | 45 | si := GetSystemInfo() 46 | offset64 := uint64(offset) 47 | if (offset64 % uint64(si.DwAllocationGranularity)) != 0 { 48 | offset64 -= offset64 % uint64(si.DwAllocationGranularity) 49 | } 50 | 51 | ret, _, err := syscall.SyscallN( 52 | dll.Load(dll.KERNEL32, &_MapViewOfFileFromApp, "MapViewOfFileFromApp"), 53 | uintptr(hMap), 54 | uintptr(desiredAccess), 55 | uintptr(offset64), 56 | uintptr(uint64(numBytesToMap))) 57 | if ret == 0 { 58 | return HFILEMAPVIEW(0), co.ERROR(err) 59 | } 60 | return HFILEMAPVIEW(ret), nil 61 | } 62 | 63 | var _MapViewOfFileFromApp *syscall.Proc 64 | 65 | // Handle to the memory block of a memory-mapped [file]. Actually, this is the 66 | // starting address of the mapped view. 67 | // 68 | // [file]: https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-mapviewoffile 69 | type HFILEMAPVIEW HANDLE 70 | 71 | // Returns a pointer to the beginning of the mapped memory block. 72 | func (hMem HFILEMAPVIEW) Ptr() *byte { 73 | return (*byte)(unsafe.Pointer(hMem)) 74 | } 75 | 76 | // [FlushViewOfFile] function. 77 | // 78 | // Panics if numBytes is negative. 79 | // 80 | // [FlushViewOfFile]: https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-flushviewoffile 81 | func (hMem HFILEMAPVIEW) FlushViewOfFile(numBytes int) error { 82 | utl.PanicNeg(numBytes) 83 | ret, _, err := syscall.SyscallN( 84 | dll.Load(dll.KERNEL32, &_FlushViewOfFile, "FlushViewOfFile"), 85 | uintptr(hMem), 86 | uintptr(uint64(numBytes))) 87 | return utl.ZeroAsGetLastError(ret, err) 88 | } 89 | 90 | var _FlushViewOfFile *syscall.Proc 91 | 92 | // [UnmapViewOfFile] function. 93 | // 94 | // Paired with [HFILEMAP.MapViewOfFile]. 95 | // 96 | // [UnmapViewOfFile]: https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-unmapviewoffile 97 | func (hMem HFILEMAPVIEW) UnmapViewOfFile() error { 98 | ret, _, err := syscall.SyscallN( 99 | dll.Load(dll.KERNEL32, &_UnmapViewOfFile, "UnmapViewOfFile"), 100 | uintptr(hMem)) 101 | return utl.ZeroAsGetLastError(ret, err) 102 | } 103 | 104 | var _UnmapViewOfFile *syscall.Proc 105 | -------------------------------------------------------------------------------- /win/kernel_hactctx.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/dll" 11 | "github.com/rodrigocfd/windigo/internal/utl" 12 | ) 13 | 14 | // Handle to an [activation context]. 15 | // 16 | // [activation context]: https://learn.microsoft.com/en-us/windows/win32/sbscs/activation-contexts 17 | type HACTCTX HANDLE 18 | 19 | // [CreateActCtx] function. 20 | // 21 | // ⚠️ You must defer [HACTCTX.ReleaseActCtx]. 22 | // 23 | // [CreateActCtx]: https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createactctxw 24 | func CreateActCtx(actctx *ACTCTX) (HACTCTX, error) { 25 | ret, _, err := syscall.SyscallN( 26 | dll.Load(dll.KERNEL32, &_CreateActCtx, "CreateActCtx"), 27 | uintptr(unsafe.Pointer(actctx))) 28 | 29 | if int(ret) == utl.INVALID_HANDLE_VALUE { 30 | return HACTCTX(0), co.ERROR(err) 31 | } 32 | return HACTCTX(ret), nil 33 | } 34 | 35 | var _CreateActCtx *syscall.Proc 36 | 37 | // [GetCurrentActCtx] function. 38 | // 39 | // ⚠️ You must defer [HACTCTX.ReleaseActCtx]. 40 | // 41 | // [GetCurrentActCtx]: https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getcurrentactctx 42 | func GetCurrentActCtx() (HACTCTX, error) { 43 | var hActCtx HACTCTX 44 | ret, _, err := syscall.SyscallN( 45 | dll.Load(dll.KERNEL32, &_GetCurrentActCtx, "GetCurrentActCtx"), 46 | uintptr(unsafe.Pointer(hActCtx))) 47 | if ret == 0 { 48 | return HACTCTX(0), co.ERROR(err) 49 | } 50 | return HACTCTX(ret), nil 51 | } 52 | 53 | var _GetCurrentActCtx *syscall.Proc 54 | 55 | // [ActivateActCtx] function. 56 | // 57 | // Deactivation is made by [DeactivateActCtx]. 58 | // 59 | // [ActivateActCtx]: https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-activateactctx 60 | func (hActCtx HACTCTX) ActivateActCtx() (int, error) { 61 | var cookie uintptr 62 | ret, _, err := syscall.SyscallN( 63 | dll.Load(dll.KERNEL32, &_ActivateActCtx, "ActivateActCtx"), 64 | uintptr(unsafe.Pointer(hActCtx)), 65 | uintptr(unsafe.Pointer(&cookie))) 66 | if ret == 0 { 67 | return 0, co.ERROR(err) 68 | } 69 | return int(cookie), nil 70 | } 71 | 72 | var _ActivateActCtx *syscall.Proc 73 | 74 | // [AddRefActCtx] function. 75 | // 76 | // ⚠️ You must defer [HACTCTX.ReleaseActCtx]. 77 | // 78 | // [AddRefActCtx]: https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-addrefactctx 79 | func (hActCtx HACTCTX) AddRefActCtx() HACTCTX { 80 | ret, _, _ := syscall.SyscallN( 81 | dll.Load(dll.KERNEL32, &_AddRefActCtx, "AddRefActCtx"), 82 | uintptr(hActCtx)) 83 | return HACTCTX(ret) 84 | } 85 | 86 | var _AddRefActCtx *syscall.Proc 87 | 88 | // [ReleaseActCtx] function. 89 | // 90 | // [ReleaseActCtx]: https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-releaseactctx 91 | func (hActCtx HACTCTX) ReleaseActCtx() { 92 | syscall.SyscallN( 93 | dll.Load(dll.KERNEL32, &_ReleaseActCtx, "ReleaseActCtx"), 94 | uintptr(hActCtx)) 95 | } 96 | 97 | var _ReleaseActCtx *syscall.Proc 98 | 99 | // [ZombifyActCtx] function. 100 | // 101 | // [ZombifyActCtx]: https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-zombifyactctx 102 | func (hActCtx HACTCTX) ZombifyActCtx() error { 103 | ret, _, err := syscall.SyscallN( 104 | dll.Load(dll.KERNEL32, &_ZombifyActCtx, "ZombifyActCtx"), 105 | uintptr(hActCtx)) 106 | return utl.ZeroAsGetLastError(ret, err) 107 | } 108 | 109 | var _ZombifyActCtx *syscall.Proc 110 | -------------------------------------------------------------------------------- /win/shell_IFileOpenDialog.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | ) 11 | 12 | // [IFileOpenDialog] COM interface. 13 | // 14 | // Implements [OleObj] and [OleResource]. 15 | // 16 | // Example: 17 | // 18 | // var hWnd win.HWND // initialized somewhere 19 | // 20 | // _, _ = win.CoInitializeEx( 21 | // co.COINIT_APARTMENTTHREADED | co.COINIT_DISABLE_OLE1DDE) 22 | // defer win.CoUninitialize() 23 | // 24 | // rel := win.NewOleReleaser() 25 | // defer rel.Release() 26 | // 27 | // var fod *win.IFileOpenDialog 28 | // _ = win.CoCreateInstance( 29 | // rel, 30 | // co.CLSID_FileOpenDialog, 31 | // nil, 32 | // co.CLSCTX_INPROC_SERVER, 33 | // &fod, 34 | // ) 35 | // 36 | // defOpts, _ := fod.GetOptions() 37 | // _ = fod.SetOptions(defOpts | 38 | // co.FOS_FORCEFILESYSTEM | 39 | // co.FOS_FILEMUSTEXIST, 40 | // ) 41 | // 42 | // _ = fod.SetFileTypes([]win.COMDLG_FILTERSPEC{ 43 | // {Name: "Text files", Spec: "*.txt"}, 44 | // {Name: "All files", Spec: "*.*"}, 45 | // }) 46 | // _ = fod.SetFileTypeIndex(1) 47 | // 48 | // if ok, _ := fod.Show(hWnd); ok { 49 | // item, _ := fod.GetResult(rel) 50 | // fileName, _ := item.GetDisplayName(co.SIGDN_FILESYSPATH) 51 | // println(fileName) 52 | // } 53 | // 54 | // [IFileOpenDialog]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-ifileopendialog 55 | type IFileOpenDialog struct{ IFileDialog } 56 | 57 | // Returns the unique COM [interface ID]. 58 | // 59 | // [interface ID]: https://learn.microsoft.com/en-us/office/client-developer/outlook/mapi/iid 60 | func (*IFileOpenDialog) IID() co.IID { 61 | return co.IID_IFileOpenDialog 62 | } 63 | 64 | // [GetResults] method. 65 | // 66 | // Returns the selected items after user confirmation, for multi-selection 67 | // dialogs – those with [co.FOS_ALLOWMULTISELECT] option. 68 | // 69 | // For single-selection dialogs, use [IFileDialog.GetResult]. 70 | // 71 | // [GetResults]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ifileopendialog-getresults 72 | func (me *IFileOpenDialog) GetResults(releaser *OleReleaser) (*IShellItemArray, error) { 73 | var ppvtQueried **_IUnknownVt 74 | ret, _, _ := syscall.SyscallN( 75 | (*_IFileOpenDialogVt)(unsafe.Pointer(*me.Ppvt())).GetResults, 76 | uintptr(unsafe.Pointer(me.Ppvt())), 77 | uintptr(unsafe.Pointer(&ppvtQueried))) 78 | 79 | if hr := co.HRESULT(ret); hr == co.HRESULT_S_OK { 80 | pObj := &IShellItemArray{IUnknown{ppvtQueried}} 81 | releaser.Add(pObj) 82 | return pObj, nil 83 | } else { 84 | return nil, hr 85 | } 86 | } 87 | 88 | // [GetSelectedItems] method. 89 | // 90 | // [GetSelectedItems]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ifileopendialog-getselecteditems 91 | func (me *IFileOpenDialog) GetSelectedItems(releaser *OleReleaser) (*IShellItemArray, error) { 92 | var ppvtQueried **_IUnknownVt 93 | ret, _, _ := syscall.SyscallN( 94 | (*_IFileOpenDialogVt)(unsafe.Pointer(*me.Ppvt())).GetSelectedItems, 95 | uintptr(unsafe.Pointer(me.Ppvt())), 96 | uintptr(unsafe.Pointer(&ppvtQueried))) 97 | 98 | if hr := co.HRESULT(ret); hr == co.HRESULT_S_OK { 99 | pObj := &IShellItemArray{IUnknown{ppvtQueried}} 100 | releaser.Add(pObj) 101 | return pObj, nil 102 | } else { 103 | return nil, hr 104 | } 105 | } 106 | 107 | type _IFileOpenDialogVt struct { 108 | _IFileDialogVt 109 | GetResults uintptr 110 | GetSelectedItems uintptr 111 | } 112 | -------------------------------------------------------------------------------- /co/guid.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package co 4 | 5 | // A [GUID] struct, represented as a string. 6 | // 7 | // [GUID]: https://learn.microsoft.com/en-us/windows/win32/api/guiddef/ns-guiddef-guid 8 | type GUID string 9 | 10 | // A COM [class ID], represented as a string. 11 | // 12 | // [class ID]: https://learn.microsoft.com/en-us/windows/win32/com/clsid-key-hklm 13 | type CLSID GUID 14 | 15 | // A COM [interface ID], represented as a string. 16 | // 17 | // [interface ID]: https://learn.microsoft.com/en-us/office/client-developer/outlook/mapi/iid 18 | type IID GUID 19 | 20 | // Ole GUID identifier. 21 | const ( 22 | IID_IBindCtx IID = "0000000e-0000-0000-c000-000000000046" 23 | IID_IDataObject IID = "0000010e-0000-0000-c000-000000000046" 24 | IID_IDropTarget IID = "00000122-0000-0000-c000-000000000046" 25 | IID_IEnumString IID = "00000101-0000-0000-c000-000000000046" 26 | IID_ISequentialStream IID = "0c733a30-2a1c-11ce-ade5-00aa0044773d" 27 | IID_IStream IID = "0000000c-0000-0000-c000-000000000046" 28 | IID_IUnknown IID = "00000000-0000-0000-c000-000000000046" 29 | IID_NULL IID = "00000000-0000-0000-0000-000000000000" 30 | ) 31 | 32 | // Oleaut GUID identifier. 33 | const ( 34 | IID_IDispatch IID = "00020400-0000-0000-c000-000000000046" 35 | IID_IPicture IID = "7bf80980-bf32-101a-8bbb-00aa00300cab" 36 | IID_IPropertyStore IID = "886d8eeb-8cf2-4446-8d02-cdba1dbdcf99" 37 | IID_ITypeInfo IID = "00020401-0000-0000-c000-000000000046" 38 | IID_ITypeLib IID = "00020402-0000-0000-c000-000000000046" 39 | ) 40 | 41 | // Shell GUID identifier. 42 | const ( 43 | CLSID_FileOpenDialog CLSID = "dc1c5a9c-e88a-4dde-a5a1-60f82a20aef7" 44 | CLSID_FileOperation CLSID = "3ad05575-8857-4850-9277-11b85bdb8e09" 45 | CLSID_FileSaveDialog CLSID = "c0b4e2f3-ba21-4773-8dba-335ec946eb8b" 46 | CLSID_ShellLink CLSID = "00021401-0000-0000-c000-000000000046" 47 | CLSID_TaskbarList CLSID = "56fdf344-fd6d-11d0-958a-006097c9a090" 48 | 49 | IID_IEnumIDList IID = "000214f2-0000-0000-c000-000000000046" 50 | IID_IEnumShellItems IID = "70629033-e363-4a28-a567-0db78006e6d7" 51 | IID_IFileDialog IID = "42f85136-db7e-439c-85f1-e4075d135fc8" 52 | IID_IFileDialogEvents IID = "973510db-7d7f-452b-8975-74a85828d354" 53 | IID_IFileOpenDialog IID = "d57c7288-d4ad-4768-be02-9d969532d960" 54 | IID_IFileOperation IID = "947aab5f-0a5c-4c13-b4d6-4bf7836fc9f8" 55 | IID_IFileOperationProgressSink IID = "04b0f1a7-9490-44bc-96e1-4296a31252e2" 56 | IID_IFileSaveDialog IID = "84bccd23-5fde-4cdb-aea4-af64b83d78ab" 57 | IID_IModalWindow IID = "b4db1657-70d7-485e-8e3e-6fcb5a5c1802" 58 | IID_IOleWindow IID = "00000114-0000-0000-c000-000000000046" 59 | IID_IShellFolder IID = "000214e6-0000-0000-c000-000000000046" 60 | IID_IShellItem IID = "43826d1e-e718-42ee-bc55-a1e261c37bfe" 61 | IID_IShellItem2 IID = "7e9fb0d3-919f-4307-ab2e-9b1860310c93" 62 | IID_IShellItemArray IID = "b63ea76d-1f85-456f-a19c-48159efa858b" 63 | IID_IShellItemFilter IID = "2659b475-eeb8-48b7-8f07-b378810f48cf" 64 | IID_IShellLink IID = "000214f9-0000-0000-c000-000000000046" 65 | IID_IShellView IID = "000214e3-0000-0000-c000-000000000046" 66 | IID_ITaskbarList IID = "56fdf342-fd6d-11d0-958a-006097c9a090" 67 | IID_ITaskbarList2 IID = "602d4995-b13a-429b-a66e-1935e44f4317" 68 | IID_ITaskbarList3 IID = "ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf" 69 | IID_ITaskbarList4 IID = "c43dc798-95d1-4bea-9030-bb99e2983a1a" 70 | ) 71 | -------------------------------------------------------------------------------- /win/ole_IEnumString.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/utl" 11 | "github.com/rodrigocfd/windigo/wstr" 12 | ) 13 | 14 | // [IEnumString] COM interface. 15 | // 16 | // Implements [OleObj] and [OleResource]. 17 | // 18 | // [IEnumString]: https://learn.microsoft.com/en-us/windows/win32/api/objidl/nn-objidl-ienumstring 19 | type IEnumString struct{ IUnknown } 20 | 21 | // Returns the unique COM [interface ID]. 22 | // 23 | // [interface ID]: https://learn.microsoft.com/en-us/office/client-developer/outlook/mapi/iid 24 | func (*IEnumString) IID() co.IID { 25 | return co.IID_IEnumString 26 | } 27 | 28 | // [Clone] method. 29 | // 30 | // [Clone]: https://learn.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-ienumstring-clone 31 | func (me *IEnumString) Clone(releaser *OleReleaser) (*IEnumString, error) { 32 | var ppvtQueried **_IUnknownVt 33 | ret, _, _ := syscall.SyscallN( 34 | (*_IEnumStringVt)(unsafe.Pointer(*me.Ppvt())).Clone, 35 | uintptr(unsafe.Pointer(me.Ppvt())), 36 | uintptr(unsafe.Pointer(&ppvtQueried))) 37 | 38 | if hr := co.HRESULT(ret); hr == co.HRESULT_S_OK { 39 | pObj := &IEnumString{IUnknown{ppvtQueried}} 40 | releaser.Add(pObj) 41 | return pObj, nil 42 | } else { 43 | return nil, hr 44 | } 45 | } 46 | 47 | // Returns all string values by calling [IEnumString.Next]. 48 | func (me *IEnumString) Enum() ([]string, error) { 49 | strs := make([]string, 0) 50 | var s string 51 | var hr error 52 | 53 | for { 54 | s, hr = me.Next() 55 | if hr != nil { // actual error 56 | return nil, hr 57 | } else if s == "" { // no more items to fetch 58 | return strs, nil 59 | } else { // item fetched 60 | strs = append(strs, s) 61 | } 62 | } 63 | } 64 | 65 | // [Next] method. 66 | // 67 | // [Next]: https://learn.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-ienumstring-next 68 | func (me *IEnumString) Next() (string, error) { 69 | var pv uintptr 70 | var numFetched uint32 71 | 72 | ret, _, _ := syscall.SyscallN( 73 | (*_IEnumStringVt)(unsafe.Pointer(*me.Ppvt())).Next, 74 | uintptr(unsafe.Pointer(me.Ppvt())), 75 | 1, 76 | uintptr(unsafe.Pointer(&pv)), 77 | uintptr(unsafe.Pointer(&numFetched))) 78 | 79 | if hr := co.HRESULT(ret); hr == co.HRESULT_S_OK { 80 | defer HTASKMEM(pv).CoTaskMemFree() 81 | name := wstr.DecodePtr((*uint16)(unsafe.Pointer(pv))) 82 | return name, nil 83 | } else if hr == co.HRESULT_S_FALSE { 84 | return "", nil 85 | } else { 86 | return "", hr 87 | } 88 | } 89 | 90 | // [Reset] method. 91 | // 92 | // [Reset]: https://learn.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-ienumstring-reset 93 | func (me *IEnumString) Reset() error { 94 | ret, _, _ := syscall.SyscallN( 95 | (*_IEnumStringVt)(unsafe.Pointer(*me.Ppvt())).Reset, 96 | uintptr(unsafe.Pointer(me.Ppvt()))) 97 | return utl.ErrorAsHResult(ret) 98 | } 99 | 100 | // [Skip] method. 101 | // 102 | // Panics if count is negative. 103 | // 104 | // [Skip]: https://learn.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-ienumstring-skip 105 | func (me *IEnumString) Skip(count int) error { 106 | utl.PanicNeg(count) 107 | ret, _, _ := syscall.SyscallN( 108 | (*_IEnumStringVt)(unsafe.Pointer(*me.Ppvt())).Skip, 109 | uintptr(unsafe.Pointer(me.Ppvt())), 110 | uintptr(uint32(count))) 111 | return utl.ErrorAsHResult(ret) 112 | } 113 | 114 | type _IEnumStringVt struct { 115 | _IUnknownVt 116 | Next uintptr 117 | Skip uintptr 118 | Reset uintptr 119 | Clone uintptr 120 | } 121 | -------------------------------------------------------------------------------- /wstr/encoders.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package wstr 4 | 5 | import ( 6 | "unicode/utf16" 7 | ) 8 | 9 | // Converts multiple Go strings into multiple null-terminated UTF-16 strings, 10 | // with a double null terminator. Writes to a previously allocated buffer. If 11 | // the buffer isn't long enough, the output strings will be truncated. 12 | // 13 | // Returns the number of uint16 words written, including the double terminating 14 | // null. 15 | func EncodeArrToBuf(dest []uint16, strs ...string) int { 16 | if len(dest) == 0 { 17 | return 0 18 | } 19 | 20 | totWritten := 1 // count double terminating null 21 | for _, str := range strs { 22 | szDest := len(dest) 23 | if szDest == 1 { // we need a second terminating null 24 | break 25 | } 26 | 27 | numWritten := EncodeToBuf(dest[:szDest-1], str) // will write a terminating null 28 | dest = dest[numWritten:] 29 | totWritten += numWritten 30 | } 31 | dest[0] = 0x0000 // double terminating null 32 | return totWritten 33 | } 34 | 35 | // Converts multiple Go strings into multiple null-terminated UTF-16 strings, 36 | // with a double null terminator. Returns a new heap-allocated *uint16. 37 | func EncodeArrToPtr(strs ...string) *uint16 { 38 | buf := EncodeArrToSlice(strs...) 39 | return &buf[0] 40 | } 41 | 42 | // Converts multiple Go strings into multiple null-terminated UTF-16 strings, 43 | // with a double null terminator. Returns a new heap-allocated []uint16. 44 | func EncodeArrToSlice(strs ...string) []uint16 { 45 | numWords := 1 // count double terminating null 46 | for _, s := range strs { 47 | numWords += CountUtf16Len(s) + 1 // count terminating null 48 | } 49 | 50 | buf := make([]uint16, numWords) 51 | EncodeArrToBuf(buf, strs...) 52 | return buf 53 | } 54 | 55 | // Converts a Go string into a null-terminated UTF-16 string, writing to a 56 | // previously allocated buffer. If the buffer isn't long enough, the output 57 | // string will be truncated. 58 | // 59 | // Returns the number of uint16 words written, including the terminating null. 60 | // 61 | // Adapted from [utf16.Encode]; performs no allocations. 62 | // 63 | // Example: 64 | // 65 | // buf := make([]uint16, 10) 66 | // wstr.EncodeToBuf(buf, "abc") 67 | func EncodeToBuf(dest []uint16, s string) int { 68 | const ( 69 | _SURR_SELF = 0x10000 70 | _REPLAC_CHAR = '\uFFFD' 71 | ) 72 | 73 | szDest := len(dest) 74 | if szDest == 0 { 75 | return 0 76 | } 77 | 78 | idx := 0 79 | EachRune: 80 | for _, ch := range s { 81 | if idx >= szDest-1 { 82 | break // truncate to prevent buffer overrun 83 | } 84 | 85 | switch utf16.RuneLen(ch) { 86 | case 1: // normal rune 87 | dest[idx] = uint16(ch) 88 | idx++ 89 | case 2: // needs surrogate sequence 90 | if idx+1 >= szDest-1 { 91 | break EachRune // no room for surrogate pair 92 | } else { 93 | r1, r2 := utf16.EncodeRune(ch) 94 | dest[idx] = uint16(r1) 95 | dest[idx+1] = uint16(r2) 96 | idx += 2 97 | } 98 | default: // cannot be properly encoded 99 | dest[idx] = uint16(_REPLAC_CHAR) 100 | idx++ 101 | } 102 | } 103 | dest[idx] = 0x0000 // terminating null 104 | return idx + 1 105 | } 106 | 107 | // Converts a Go string into a null-terminated UTF-16 string. Returns a new 108 | // heap-allocated *uint16. 109 | func EncodeToPtr(s string) *uint16 { 110 | buf := EncodeToSlice(s) 111 | return &buf[0] 112 | } 113 | 114 | // Converts a Go string into a null-terminated UTF-16 string. Returns a new 115 | // heap-allocated []uint16. 116 | func EncodeToSlice(s string) []uint16 { 117 | buf := make([]uint16, CountUtf16Len(s)+1) // count terminating null 118 | EncodeToBuf(buf, s) 119 | return buf 120 | } 121 | -------------------------------------------------------------------------------- /win/shell_IShellView.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/utl" 11 | ) 12 | 13 | // [IShellView] COM interface. 14 | // 15 | // Implements [OleObj] and [OleResource]. 16 | // 17 | // [IShellView]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-ishellview 18 | type IShellView struct{ IOleWindow } 19 | 20 | // Returns the unique COM [interface ID]. 21 | // 22 | // [interface ID]: https://learn.microsoft.com/en-us/office/client-developer/outlook/mapi/iid 23 | func (*IShellView) IID() co.IID { 24 | return co.IID_IShellView 25 | } 26 | 27 | // [DestroyViewWindow] method. 28 | // 29 | // [DestroyViewWindow]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ishellview-destroyviewwindow 30 | func (me *IShellView) DestroyViewWindow() error { 31 | ret, _, _ := syscall.SyscallN( 32 | (*_IShellViewVt)(unsafe.Pointer(*me.Ppvt())).DestroyViewWindow, 33 | uintptr(unsafe.Pointer(me.Ppvt()))) 34 | return utl.ErrorAsHResult(ret) 35 | } 36 | 37 | // [EnableModeless] method. 38 | // 39 | // [EnableModeless]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ishellview-enablemodeless 40 | func (me *IShellView) EnableModeless(enable bool) error { 41 | ret, _, _ := syscall.SyscallN( 42 | (*_IShellViewVt)(unsafe.Pointer(*me.Ppvt())).EnableModeless, 43 | uintptr(unsafe.Pointer(me.Ppvt())), 44 | utl.BoolToUintptr(enable)) 45 | return utl.ErrorAsHResult(ret) 46 | } 47 | 48 | // [Refresh] method. 49 | // 50 | // [Refresh]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ishellview-refresh 51 | func (me *IShellView) Refresh() error { 52 | ret, _, _ := syscall.SyscallN( 53 | (*_IShellViewVt)(unsafe.Pointer(*me.Ppvt())).Refresh, 54 | uintptr(unsafe.Pointer(me.Ppvt()))) 55 | return utl.ErrorAsHResult(ret) 56 | } 57 | 58 | // [SaveViewState] method. 59 | // 60 | // [SaveViewState]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ishellview-saveviewstate 61 | func (me *IShellView) SaveViewState() error { 62 | ret, _, _ := syscall.SyscallN( 63 | (*_IShellViewVt)(unsafe.Pointer(*me.Ppvt())).SaveViewState, 64 | uintptr(unsafe.Pointer(me.Ppvt()))) 65 | return utl.ErrorAsHResult(ret) 66 | } 67 | 68 | // [TranslateAccelerator] method. 69 | // 70 | // [TranslateAccelerator]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ishellview-translateaccelerator 71 | func (me *IShellView) TranslateAccelerator(msg *MSG) error { 72 | ret, _, _ := syscall.SyscallN( 73 | (*_IShellViewVt)(unsafe.Pointer(*me.Ppvt())).TranslateAccelerator, 74 | uintptr(unsafe.Pointer(me.Ppvt())), 75 | uintptr(unsafe.Pointer(msg))) 76 | return utl.ErrorAsHResult(ret) 77 | } 78 | 79 | // [UIActivate] method. 80 | // 81 | // [UIActivate]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ishellview-uiactivate 82 | func (me *IShellView) UIActivate(state co.SVUIA) error { 83 | ret, _, _ := syscall.SyscallN( 84 | (*_IShellViewVt)(unsafe.Pointer(*me.Ppvt())).UIActivate, 85 | uintptr(unsafe.Pointer(me.Ppvt())), 86 | uintptr(state)) 87 | return utl.ErrorAsHResult(ret) 88 | } 89 | 90 | type _IShellViewVt struct { 91 | _IOleWindowVt 92 | TranslateAccelerator uintptr 93 | EnableModeless uintptr 94 | UIActivate uintptr 95 | Refresh uintptr 96 | CreateViewWindow uintptr 97 | DestroyViewWindow uintptr 98 | GetCurrentInfo uintptr 99 | AddPropertySheetPages uintptr 100 | SaveViewState uintptr 101 | SelectItem uintptr 102 | GetItemObject uintptr 103 | } 104 | -------------------------------------------------------------------------------- /ui/wndu_Modal.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ui 4 | 5 | import ( 6 | "github.com/rodrigocfd/windigo/win" 7 | ) 8 | 9 | // Modal window. 10 | // 11 | // Implements: 12 | // - [Window] 13 | // - [Parent] 14 | type Modal struct { 15 | raw *_RawModal 16 | dlg *_DlgModal 17 | } 18 | 19 | // Creates a new modal window with [CreateWindowEx]. 20 | // 21 | // Example: 22 | // 23 | // var wndParent ui.Parent // initialized somewhere 24 | // 25 | // wndModal := ui.NewModal( 26 | // wndParent, 27 | // ui.OptsModal(). 28 | // Title("Hello modal"), 29 | // ) 30 | // wndModal.ShowModal() 31 | // 32 | // [CreateWindowEx]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw 33 | func NewModal(parent Parent, opts *VarOptsModal) *Modal { 34 | return &Modal{ 35 | raw: newModalRaw(parent, opts), 36 | dlg: nil, 37 | } 38 | } 39 | 40 | // Creates a new dialog-based Modal with [DialogBoxParam]. 41 | // 42 | // Example: 43 | // 44 | // const ID_MODAL_DLG uint16 = 2000 45 | // 46 | // var wndParent ui.Parent // initialized somewhere 47 | // 48 | // wndModal := ui.NewModalDlg(wndParent, 2000) 49 | // wndModal.ShowModal() 50 | // 51 | // [DialogBoxParam]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-dialogboxparamw 52 | func NewModalDlg(parent Parent, dlgId uint16) *Modal { 53 | return &Modal{ 54 | raw: nil, 55 | dlg: newModalDlg(parent, dlgId), 56 | } 57 | } 58 | 59 | // Physically creates the window, then runs the modal loop. This method will 60 | // block until the window is closed. 61 | func (me *Modal) ShowModal() { 62 | if me.raw != nil { 63 | me.raw.showModal() 64 | } else { 65 | me.dlg.showModal() 66 | } 67 | } 68 | 69 | // Returns the underlying HWND handle of this window. 70 | // 71 | // Implements [Window]. 72 | // 73 | // Note that this handle is initially zero, existing only after window creation. 74 | func (me *Modal) Hwnd() win.HWND { 75 | if me.raw != nil { 76 | return me.raw.hWnd 77 | } else { 78 | return me.dlg.hWnd 79 | } 80 | } 81 | 82 | // Exposes all the window notifications the can be handled. 83 | // 84 | // Implements [Parent]. 85 | // 86 | // Panics if called after the window has been created. 87 | func (me *Modal) On() *EventsWindow { 88 | if me.Hwnd() != 0 { 89 | panic("Cannot add event handling after the window has been created.") 90 | } 91 | 92 | if me.raw != nil { 93 | return &me.raw.userEvents 94 | } else { 95 | return &me.dlg.userEvents 96 | } 97 | } 98 | 99 | // This method is analog to [SendMessage] (synchronous), but intended to be 100 | // called from another thread, so a callback function can, tunelled by 101 | // [WNDPROC], run in the original thread of the window, thus allowing GUI 102 | // updates. With this, the user doesn't have to deal with a custom WM_ message. 103 | // 104 | // Implements [Parent]. 105 | // 106 | // Example: 107 | // 108 | // var wnd *ui.WindowModal // initialized somewhere 109 | // 110 | // wnd.On().WmCreate(func(p WmCreate) int { 111 | // go func() { 112 | // // process to be done in a parallel goroutine... 113 | // 114 | // wnd.UiThread(func() { 115 | // // update the UI in the original UI thread... 116 | // }) 117 | // }() 118 | // return 0 119 | // }) 120 | // 121 | // [SendMessage]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendmessagew 122 | // [WNDPROC]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nc-winuser-wndproc 123 | func (me *Modal) UiThread(fun func()) { 124 | if me.raw != nil { 125 | me.raw.uiThread(fun) 126 | } else { 127 | me.dlg.uiThread(fun) 128 | } 129 | } 130 | 131 | // Implements [Parent]. 132 | func (me *Modal) base() *_BaseContainer { 133 | if me.raw != nil { 134 | return &me.raw._BaseContainer 135 | } else { 136 | return &me.dlg._BaseContainer 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /ui/ctl_HeaderItems.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ui 4 | 5 | import ( 6 | "fmt" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/win" 11 | "github.com/rodrigocfd/windigo/wstr" 12 | ) 13 | 14 | // The items collection. 15 | // 16 | // You cannot create this object directly, it will be created automatically 17 | // by the owning [Header]. 18 | type CollectionHeaderItems struct { 19 | owner *Header 20 | } 21 | 22 | // Adds a new item with its width, using [HDM_INSERTITEM], and returns the new 23 | // item. 24 | // 25 | // Panics on error. 26 | // 27 | // [HDM_INSERTITEM]: https://learn.microsoft.com/en-us/windows/win32/controls/hdm-insertitem 28 | func (me *CollectionHeaderItems) Add(text string, width int) HeaderItem { 29 | hdi := win.HDITEM{ 30 | Mask: co.HDI_TEXT | co.HDI_WIDTH, 31 | Cxy: int32(width), 32 | } 33 | 34 | var wText wstr.BufEncoder 35 | hdi.SetPszText(wText.Slice(text)) 36 | 37 | newIdxRet, err := me.owner.hWnd.SendMessage(co.HDM_INSERTITEM, 38 | 0xffff, win.LPARAM(unsafe.Pointer(&hdi))) 39 | newIdx := int(newIdxRet) 40 | if err != nil || newIdx == -1 { 41 | panic(fmt.Sprintf("HDM_INSERTITEM \"%s\" failed.", text)) 42 | } 43 | 44 | return me.Get(newIdx) 45 | } 46 | 47 | // Returns all items. 48 | func (me *CollectionHeaderItems) All() []HeaderItem { 49 | nItems := me.Count() 50 | items := make([]HeaderItem, 0, nItems) 51 | for i := 0; i < nItems; i++ { 52 | items = append(items, me.Get(i)) 53 | } 54 | return items 55 | } 56 | 57 | // Sends [HDM_GETORDERARRAY] to retrieve the items in the current order. 58 | // 59 | // [HDM_GETORDERARRAY]: https://learn.microsoft.com/en-us/windows/win32/controls/hdm-getorderarray 60 | func (me *CollectionHeaderItems) AllOrdered() []HeaderItem { 61 | nItems := me.Count() 62 | indexes := make([]int32, nItems) 63 | 64 | me.owner.hWnd.SendMessage(co.HDM_GETORDERARRAY, 65 | win.WPARAM(int32(nItems)), win.LPARAM(unsafe.Pointer(&indexes[0]))) 66 | 67 | items := make([]HeaderItem, 0, nItems) 68 | for _, index := range indexes { 69 | items = append(items, me.Get(int(index))) 70 | } 71 | return items 72 | } 73 | 74 | // Retrieves the number of items with [HDM_GETITEMCOUNT]. 75 | // 76 | // Panics on error. 77 | // 78 | // [HDM_GETITEMCOUNT]: https://learn.microsoft.com/en-us/windows/win32/controls/hdm-getitemcount 79 | func (me *CollectionHeaderItems) Count() int { 80 | countRet, err := me.owner.hWnd.SendMessage(co.HDM_GETITEMCOUNT, 0, 0) 81 | count := int(countRet) 82 | if err != nil || count == -1 { 83 | panic("HDM_GETITEMCOUNT failed.") 84 | } 85 | return count 86 | } 87 | 88 | // Returns the item at the given index. 89 | func (me *CollectionHeaderItems) Get(index int) HeaderItem { 90 | return HeaderItem{ 91 | owner: me.owner, 92 | index: int32(index), 93 | } 94 | } 95 | 96 | // Sends [HDM_ORDERTOINDEX] to retrieve the item at the given order. 97 | // 98 | // [HDM_ORDERTOINDEX]: https://learn.microsoft.com/en-us/windows/win32/controls/hdm-ordertoindex 99 | func (me *CollectionHeaderItems) GetByOrder(order int) HeaderItem { 100 | idx, _ := me.owner.hWnd.SendMessage(co.HDM_ORDERTOINDEX, win.WPARAM(int32(order)), 0) 101 | return me.Get(int(idx)) 102 | } 103 | 104 | // Returns the last item. 105 | func (me *CollectionHeaderItems) Last() HeaderItem { 106 | return me.Get(me.Count() - 1) 107 | } 108 | 109 | // Sends a [HDM_SETORDERARRAY] to reorder the items with the given order. 110 | // 111 | // [HDM_SETORDERARRAY]: https://learn.microsoft.com/en-us/windows/win32/controls/hdm-setorderarray 112 | func (me *CollectionHeaderItems) Reorder(indexes []int) { 113 | buf := make([]int32, 0, len(indexes)) 114 | for _, index := range indexes { 115 | buf = append(buf, int32(index)) 116 | } 117 | 118 | me.owner.hWnd.SendMessage(co.HDM_SETORDERARRAY, 119 | win.WPARAM(int32(len(buf))), win.LPARAM(unsafe.Pointer(&buf[0]))) 120 | } 121 | -------------------------------------------------------------------------------- /win/version_funcs.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/dll" 11 | "github.com/rodrigocfd/windigo/internal/utl" 12 | "github.com/rodrigocfd/windigo/wstr" 13 | ) 14 | 15 | // [GetFileVersionInfo] function. 16 | // 17 | // This is a low-level function, prefer using [VersionLoad]. 18 | // 19 | // Example: 20 | // 21 | // hInst, _ := win.GetModuleHandle("") 22 | // exeName, _ := hInst.GetModuleFileName() 23 | // szData, _ := win.GetFileVersionInfoSize(exeName) 24 | // 25 | // data := heap.NewVecSized(szData, byte(0)) 26 | // defer data.Free() 27 | // 28 | // _ = win.GetFileVersionInfo(exeName, data.HotSlice()) 29 | // 30 | // [GetFileVersionInfo]: https://learn.microsoft.com/en-us/windows/win32/api/winver/nf-winver-getfileversioninfow 31 | func GetFileVersionInfo(fileName string, dest []byte) error { 32 | var wFileName wstr.BufEncoder 33 | ret, _, err := syscall.SyscallN( 34 | dll.Load(dll.VERSION, &_GetFileVersionInfoW, "GetFileVersionInfoW"), 35 | uintptr(wFileName.EmptyIsNil(fileName)), 36 | 0, 37 | uintptr(uint32(len(dest))), 38 | uintptr(unsafe.Pointer(&dest[0]))) 39 | return utl.ZeroAsGetLastError(ret, err) 40 | } 41 | 42 | var _GetFileVersionInfoW *syscall.Proc 43 | 44 | // [GetFileVersionInfoSize] function. 45 | // 46 | // [GetFileVersionInfoSize]: https://learn.microsoft.com/en-us/windows/win32/api/winver/nf-winver-getfileversioninfosizew 47 | func GetFileVersionInfoSize(fileName string) (int, error) { 48 | var wFileName wstr.BufEncoder 49 | var dummy uint32 50 | 51 | ret, _, err := syscall.SyscallN( 52 | dll.Load(dll.VERSION, &_GetFileVersionInfoSizeW, "GetFileVersionInfoSizeW"), 53 | uintptr(wFileName.EmptyIsNil(fileName)), 54 | uintptr(unsafe.Pointer(&dummy))) 55 | if ret == 0 { 56 | return 0, co.ERROR(err) 57 | } 58 | return int(uint32(ret)), nil 59 | } 60 | 61 | var _GetFileVersionInfoSizeW *syscall.Proc 62 | 63 | // [VerQueryValue] function. 64 | // 65 | // This is a low-level function, prefer using [VersionLoad]. 66 | // 67 | // Example: 68 | // 69 | // hInst, _ := win.GetModuleHandle("") 70 | // exeName, _ := hInst.GetModuleFileName() 71 | // szData, _ := win.GetFileVersionInfoSize(exeName) 72 | // 73 | // data := heap.NewVecSized(szData, byte(0)) 74 | // defer data.Free() 75 | // 76 | // _ = win.GetFileVersionInfo(exeName, data.HotSlice()) 77 | // 78 | // if pNfoRaw, _, ok := win.VerQueryValue(data.HotSlice(), "\\"); ok { 79 | // pNfo := (*win.VS_FIXEDFILEINFO)(pNfoRaw) 80 | // println(pNfo.FileVersion()) 81 | // } 82 | // 83 | // type Block struct { 84 | // LangId win.LANGID 85 | // CodePage co.CP 86 | // } 87 | // 88 | // if pBlocks, count, ok := win.VerQueryValue( 89 | // data.HotSlice(), "\\VarFileInfo\\Translation"); ok { 90 | // 91 | // blocks := unsafe.Slice((*Block)(pBlocks), count) 92 | // for _, block := range blocks { 93 | // if pStr, nChars, ok := win.VerQueryValue(data.HotSlice(), 94 | // fmt.Sprintf("\\StringFileInfo\\%04x%04x\\%s", 95 | // block.LangId, block.CodePage, "ProductName")); ok { 96 | // 97 | // wideStr := unsafe.Slice((*uint16)(pStr), nChars) 98 | // str := wstr.DecodeSlice(wideStr) 99 | // println(str) 100 | // } 101 | // } 102 | // } 103 | // 104 | // [VerQueryValue]: https://learn.microsoft.com/en-us/windows/win32/api/winver/nf-winver-verqueryvaluew 105 | func VerQueryValue(block []byte, subBlock string) (unsafe.Pointer, int, bool) { 106 | var wSubBlock wstr.BufEncoder 107 | var lplpBuffer uintptr 108 | var puLen uint32 109 | 110 | ret, _, _ := syscall.SyscallN( 111 | dll.Load(dll.VERSION, &_VerQueryValueW, "VerQueryValueW"), 112 | uintptr(unsafe.Pointer(&block[0])), 113 | uintptr(wSubBlock.AllowEmpty(subBlock)), 114 | uintptr(unsafe.Pointer(&lplpBuffer)), 115 | uintptr(unsafe.Pointer(&puLen))) 116 | if ret == 0 { 117 | return nil, 0, false 118 | } 119 | return unsafe.Pointer(lplpBuffer), int(puLen), true 120 | } 121 | 122 | var _VerQueryValueW *syscall.Proc 123 | -------------------------------------------------------------------------------- /ui/globalsPub.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ui 4 | 5 | import ( 6 | "github.com/rodrigocfd/windigo/co" 7 | "github.com/rodrigocfd/windigo/win" 8 | ) 9 | 10 | // Returns the value adjusted according to the current horizontal system DPI. 11 | func DpiX(x int) int { 12 | initalGuiSetup() 13 | return x * dpiX / 96 14 | } 15 | 16 | // Returns the value adjusted according to the current vertical system DPI. 17 | func DpiY(y int) int { 18 | initalGuiSetup() 19 | return y * dpiY / 96 20 | } 21 | 22 | // Returns the value adjusted according to the current system DPI. 23 | func Dpi(x, y int) (int, int) { 24 | return DpiX(x), DpiY(y) 25 | } 26 | 27 | // Syntactic sugar to [win.TaskDialogIndirect] to display a message box 28 | // indicating an error. 29 | // 30 | // Panics on error. 31 | // 32 | // Example: 33 | // 34 | // var wndOwner ui.Parent // initialized somewhere 35 | // 36 | // ui.MsgError( 37 | // wndOwner, 38 | // "Title", 39 | // "Big caption above text", 40 | // "Here goes the text", 41 | // ) 42 | func MsgError(wnd Parent, title, caption, body string) { 43 | msgBuild(wnd, title, caption, body, co.TDICON_ERROR, "", false) 44 | } 45 | 46 | // Syntactic sugar to [win.TaskDialogIndirect] to display a message box 47 | // indicating a warning. 48 | // 49 | // Panics on error. 50 | // 51 | // Example: 52 | // 53 | // var wndOwner ui.Parent // initialized somewhere 54 | // 55 | // ui.MsgWarn( 56 | // wndOwner, 57 | // "Title", 58 | // "Big caption above text", 59 | // "Here goes the text", 60 | // ) 61 | func MsgWarn(wnd Parent, title, caption, body string) { 62 | msgBuild(wnd, title, caption, body, co.TDICON_WARNING, "", false) 63 | } 64 | 65 | // Syntactic sugar to [win.TaskDialogIndirect] to display a message box 66 | // indicating a successful operation. 67 | // 68 | // Panics on error. 69 | // 70 | // Example: 71 | // 72 | // var wndOwner ui.Parent // initialized somewhere 73 | // 74 | // ui.MsgOk( 75 | // wndOwner, 76 | // "Title", 77 | // "Big caption above text", 78 | // "Here goes the text", 79 | // ) 80 | func MsgOk(wnd Parent, title, caption, body string) { 81 | msgBuild(wnd, title, caption, body, co.TDICON_INFORMATION, "", false) 82 | } 83 | 84 | // Syntactic sugar to [win.TaskDialogIndirect] to display a message box prompting 85 | // the user to choose "Ok" or "Cancel". The "Ok" text can be customized. 86 | // 87 | // Returns co.ID_OK or co.ID_CANCEL. 88 | // 89 | // Panics on error. 90 | // 91 | // Example: 92 | // 93 | // var wndOwner ui.Parent // initialized somewhere 94 | // 95 | // ret := ui.MsgOkCancel( 96 | // wndOwner, 97 | // "Title", 98 | // "Big caption above text", 99 | // "Here goes the text", 100 | // "&Confirm", 101 | // ) 102 | // if ret == co.ID_OK { 103 | // // ... 104 | // } 105 | func MsgOkCancel(wnd Parent, title, caption, body, okText string) co.ID { 106 | return msgBuild(wnd, title, caption, body, co.TDICON_INFORMATION, okText, true) 107 | } 108 | 109 | func msgBuild( 110 | wnd Parent, 111 | title, caption, body string, 112 | icon co.TDICON, 113 | okText string, 114 | hasCancel bool, 115 | ) co.ID { 116 | var hParent win.HWND 117 | if wnd != nil { 118 | hParent = wnd.Hwnd() 119 | } 120 | 121 | var commonButtons co.TDCBF 122 | var buttons []win.TASKDIALOG_BUTTON 123 | if hasCancel { 124 | finalOkText := okText 125 | if finalOkText == "" { 126 | finalOkText = "&OK" 127 | } 128 | buttons = []win.TASKDIALOG_BUTTON{ 129 | {Id: co.ID_OK, Text: finalOkText}, 130 | {Id: co.ID_CANCEL, Text: "&Cancel"}, 131 | } 132 | } else { 133 | commonButtons = co.TDCBF_OK 134 | } 135 | 136 | ret, err := win.TaskDialogIndirect(win.TASKDIALOGCONFIG{ 137 | HwndParent: hParent, 138 | WindowTitle: title, 139 | MainInstruction: caption, 140 | Content: body, 141 | HMainIcon: win.TdcIconTdi(icon), 142 | CommonButtons: commonButtons, 143 | Flags: co.TDF_ALLOW_DIALOG_CANCELLATION | co.TDF_POSITION_RELATIVE_TO_WINDOW, 144 | Buttons: buttons, 145 | }) 146 | 147 | if err != nil { 148 | panic(err) 149 | } 150 | return ret 151 | } 152 | -------------------------------------------------------------------------------- /ui/ctl_StatusBar.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ui 4 | 5 | import ( 6 | "unsafe" 7 | 8 | "github.com/rodrigocfd/windigo/co" 9 | "github.com/rodrigocfd/windigo/internal/utl" 10 | "github.com/rodrigocfd/windigo/win" 11 | ) 12 | 13 | // Native [status bar] control. 14 | // 15 | // [status bar]: https://learn.microsoft.com/en-us/windows/win32/controls/status-bars 16 | type StatusBar struct { 17 | _BaseCtrl 18 | events EventsStatusBar 19 | Parts CollectionStatusBarParts // Methods to interact with the parts collection. 20 | } 21 | 22 | // Creates a new [StatusBar] with [win.CreateWindowEx]. 23 | func NewStatusBar(parent Parent) *StatusBar { 24 | ctrlId := nextCtrlId() // always give it an auto ID 25 | me := &StatusBar{ 26 | _BaseCtrl: newBaseCtrl(ctrlId), 27 | events: EventsStatusBar{ctrlId, &parent.base().userEvents}, 28 | } 29 | me.Parts.owner = me 30 | 31 | parent.base().beforeUserEvents.wmCreateOrInitdialog(func() { 32 | sbStyle := co.WS_CHILD | co.WS_VISIBLE | co.WS(co.SBARS_TOOLTIPS) 33 | parentStyle, _ := parent.Hwnd().Style() 34 | isParentResizable := (parentStyle&co.WS_MAXIMIZEBOX) != 0 || 35 | (parentStyle&co.WS_SIZEBOX) != 0 36 | if isParentResizable { 37 | sbStyle |= co.WS(co.SBARS_SIZEGRIP) 38 | } 39 | 40 | me.createWindow(co.WS_EX_NONE, "msctls_statusbar32", "", 41 | sbStyle, win.POINT{}, win.SIZE{}, parent, false) 42 | }) 43 | 44 | parent.base().beforeUserEvents.wm(co.WM_SIZE, func(p Wm) { 45 | me.Parts.resizeToFitParent(WmSize{p}) 46 | }) 47 | 48 | return me 49 | } 50 | 51 | // Exposes all the control notifications the can be handled. 52 | // 53 | // Panics if called after the control has been created. 54 | func (me *StatusBar) On() *EventsStatusBar { 55 | me.panicIfAddingEventAfterCreated() 56 | return &me.events 57 | } 58 | 59 | // Native [status bar] control events. 60 | // 61 | // You cannot create this object directly, it will be created automatically 62 | // by the owning control. 63 | // 64 | // [status bar]: https://learn.microsoft.com/en-us/windows/win32/controls/status-bars 65 | type EventsStatusBar struct { 66 | ctrlId uint16 67 | parentEvents *EventsWindow 68 | } 69 | 70 | // [NM_CLICK] message handler. 71 | // 72 | // [NM_CLICK]: https://learn.microsoft.com/en-us/windows/win32/controls/nm-click-status-bar 73 | func (me *EventsStatusBar) NmClick(fun func(p *win.NMMOUSE) bool) { 74 | me.parentEvents.WmNotify(me.ctrlId, co.NM_CLICK, func(p unsafe.Pointer) uintptr { 75 | return utl.BoolToUintptr(fun((*win.NMMOUSE)(p))) 76 | }) 77 | } 78 | 79 | // [NM_DBLCLK] message handler. 80 | // 81 | // [NM_DBLCLK]: https://learn.microsoft.com/en-us/windows/win32/controls/nm-dblclk-status-bar 82 | func (me *EventsStatusBar) NmDblClk(fun func(p *win.NMMOUSE) bool) { 83 | me.parentEvents.WmNotify(me.ctrlId, co.NM_DBLCLK, func(p unsafe.Pointer) uintptr { 84 | return utl.BoolToUintptr(fun((*win.NMMOUSE)(p))) 85 | }) 86 | } 87 | 88 | // [NM_RCLICK] message handler. 89 | // 90 | // [NM_RCLICK]: https://learn.microsoft.com/en-us/windows/win32/controls/nm-rclick-status-bar 91 | func (me *EventsStatusBar) NmRClick(fun func(p *win.NMMOUSE) bool) { 92 | me.parentEvents.WmNotify(me.ctrlId, co.NM_RCLICK, func(p unsafe.Pointer) uintptr { 93 | return utl.BoolToUintptr(fun((*win.NMMOUSE)(p))) 94 | }) 95 | } 96 | 97 | // [NM_RDBLCLK] message handler. 98 | // 99 | // [NM_RDBLCLK]: https://learn.microsoft.com/en-us/windows/win32/controls/nm-rdblclk-status-bar 100 | func (me *EventsStatusBar) NmRDblClk(fun func(p *win.NMMOUSE) bool) { 101 | me.parentEvents.WmNotify(me.ctrlId, co.NM_RDBLCLK, func(p unsafe.Pointer) uintptr { 102 | return utl.BoolToUintptr(fun((*win.NMMOUSE)(p))) 103 | }) 104 | } 105 | 106 | // [SBN_SIMPLEMODECHANGE] message handler. 107 | // 108 | // [SBN_SIMPLEMODECHANGE]: https://learn.microsoft.com/en-us/windows/win32/controls/sbn-simplemodechange 109 | func (me *EventsStatusBar) SbnSimpleModeChange(fun func(p *win.NMMOUSE)) { 110 | me.parentEvents.WmNotify(me.ctrlId, co.SBN_SIMPLEMODECHANGE, func(p unsafe.Pointer) uintptr { 111 | fun((*win.NMMOUSE)(p)) 112 | return me.parentEvents.defProcVal 113 | }) 114 | } 115 | -------------------------------------------------------------------------------- /win/shell_IFileSaveDialog.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/co" 10 | "github.com/rodrigocfd/windigo/internal/utl" 11 | ) 12 | 13 | // [IFileSaveDialog] COM interface. 14 | // 15 | // Implements [OleObj] and [OleResource]. 16 | // 17 | // Example: 18 | // 19 | // var hWnd win.HWND // initialized somewhere 20 | // 21 | // rel := win.NewOleReleaser() 22 | // defer rel.Release() 23 | // 24 | // var fsd *win.IFileSaveDialog 25 | // _ = win.CoCreateInstance( 26 | // rel, 27 | // co.CLSID_FileSaveDialog, 28 | // nil, 29 | // co.CLSCTX_INPROC_SERVER, 30 | // &fsd, 31 | // ) 32 | // 33 | // _ = fsd.SetFileTypes([]win.COMDLG_FILTERSPEC{ 34 | // {Name: "Text files", Spec: "*.txt"}, 35 | // {Name: "All files", Spec: "*.*"}, 36 | // }) 37 | // _ = fsd.SetFileTypeIndex(1) 38 | // 39 | // _ = fsd.SetFileName("default-file-name.txt") 40 | // 41 | // if ok, _ := fsd.Show(hWnd); ok { 42 | // item, _ := fsd.GetResult(rel) 43 | // txtPath, _ := item.GetDisplayName(co.SIGDN_FILESYSPATH) 44 | // println(txtPath) 45 | // } 46 | // 47 | // [IFileSaveDialog]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-ifilesavedialog 48 | type IFileSaveDialog struct{ IFileDialog } 49 | 50 | // Returns the unique COM [interface ID]. 51 | // 52 | // [interface ID]: https://learn.microsoft.com/en-us/office/client-developer/outlook/mapi/iid 53 | func (*IFileSaveDialog) IID() co.IID { 54 | return co.IID_IFileSaveDialog 55 | } 56 | 57 | // [ApplyProperties] method. 58 | // 59 | // [ApplyProperties]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ifilesavedialog-applyproperties 60 | func (me *IFileSaveDialog) ApplyProperties( 61 | item *IShellItem, 62 | store *IPropertyStore, 63 | hwnd HWND, 64 | sink *IFileOperationProgressSink, 65 | ) error { 66 | ret, _, _ := syscall.SyscallN( 67 | (*_IFileSaveDialogVt)(unsafe.Pointer(*me.Ppvt())).ApplyProperties, 68 | uintptr(unsafe.Pointer(me.Ppvt())), 69 | uintptr(unsafe.Pointer(item.Ppvt())), 70 | uintptr(unsafe.Pointer(store.Ppvt())), 71 | uintptr(hwnd), 72 | uintptr(ppvtOrNil(sink))) 73 | return utl.ErrorAsHResult(ret) 74 | } 75 | 76 | // [GetProperties] method. 77 | // 78 | // [GetProperties]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ifilesavedialog-getproperties 79 | func (me *IFileSaveDialog) GetProperties(releaser *OleReleaser) (*IPropertyStore, error) { 80 | var ppvtQueried **_IUnknownVt 81 | ret, _, _ := syscall.SyscallN( 82 | (*_IFileSaveDialogVt)(unsafe.Pointer(*me.Ppvt())).GetProperties, 83 | uintptr(unsafe.Pointer(me.Ppvt())), 84 | uintptr(unsafe.Pointer(&ppvtQueried))) 85 | 86 | if hr := co.HRESULT(ret); hr == co.HRESULT_S_OK { 87 | pObj := &IPropertyStore{IUnknown{ppvtQueried}} 88 | releaser.Add(pObj) 89 | return pObj, nil 90 | } else { 91 | return nil, hr 92 | } 93 | } 94 | 95 | // [SetProperties] method. 96 | // 97 | // [SetProperties]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ifilesavedialog-setproperties 98 | func (me *IFileSaveDialog) SetProperties(store *IPropertyStore) error { 99 | ret, _, _ := syscall.SyscallN( 100 | (*_IFileSaveDialogVt)(unsafe.Pointer(*me.Ppvt())).SetProperties, 101 | uintptr(unsafe.Pointer(me.Ppvt())), 102 | uintptr(unsafe.Pointer(store.Ppvt()))) 103 | return utl.ErrorAsHResult(ret) 104 | } 105 | 106 | // [SetSaveAsItem] method. 107 | // 108 | // [SetSaveAsItem]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ifilesavedialog-setsaveasitem 109 | func (me *IFileSaveDialog) SetSaveAsItem(item *IShellItem) error { 110 | ret, _, _ := syscall.SyscallN( 111 | (*_IFileSaveDialogVt)(unsafe.Pointer(*me.Ppvt())).SetSaveAsItem, 112 | uintptr(unsafe.Pointer(me.Ppvt())), 113 | uintptr(unsafe.Pointer(item.Ppvt()))) 114 | return utl.ErrorAsHResult(ret) 115 | } 116 | 117 | type _IFileSaveDialogVt struct { 118 | _IFileDialogVt 119 | SetSaveAsItem uintptr 120 | SetProperties uintptr 121 | SetCollectedProperties uintptr 122 | GetProperties uintptr 123 | ApplyProperties uintptr 124 | } 125 | --------------------------------------------------------------------------------