├── LICENSE.md ├── README.md ├── example.gif ├── go.mod ├── internal ├── dll │ └── dll.go └── utl │ ├── PtrCache.go │ ├── bitops.go │ ├── bool.go │ ├── com.go │ ├── consts.go │ ├── errors.go │ └── str.go ├── resources ├── README.md ├── gopher.ico ├── minimal.syso └── win10.exe.manifest ├── ui ├── Layout.go ├── ctl_BaseCtrl.go ├── ctl_Button.go ├── ctl_CheckBox.go ├── ctl_ComboBox.go ├── ctl_ComboBoxItems.go ├── ctl_DateTimePicker.go ├── ctl_Edit.go ├── ctl_Header.go ├── ctl_HeaderItem.go ├── ctl_HeaderItems.go ├── ctl_ListView.go ├── ctl_ListViewCol.go ├── ctl_ListViewCols.go ├── ctl_ListViewItem.go ├── ctl_ListViewItems.go ├── ctl_MonthCalendar.go ├── ctl_ProgressBar.go ├── ctl_RadioButton.go ├── ctl_RadioGroup.go ├── ctl_Static.go ├── ctl_StatusBar.go ├── ctl_StatusBarPart.go ├── ctl_StatusBarParts.go ├── ctl_SysLink.go ├── ctl_Tab.go ├── ctl_TabItem.go ├── ctl_TabItems.go ├── ctl_Toolbar.go ├── ctl_ToolbarButtons.go ├── ctl_Trackbar.go ├── ctl_TreeView.go ├── ctl_TreeViewItem.go ├── ctl_TreeViewItems.go ├── ctl_UpDown.go ├── globalsPriv.go ├── globalsPub.go ├── interfaces.go ├── params.go ├── wnd_BaseContainer.go ├── wnd_BaseDlg.go ├── wnd_BaseRaw.go ├── wnd_Control.go ├── wnd_ControlDlg.go ├── wnd_ControlRaw.go ├── wnd_EventsWindow.go ├── wnd_Main.go ├── wnd_MainDlg.go ├── wnd_MainRaw.go ├── wnd_Modal.go ├── wnd_ModalDlg.go └── wnd_ModalRaw.go └── win ├── advapi_funcs.go ├── advapi_hkey.go ├── advapi_structs.go ├── advapi_unions.go ├── co ├── advapi.go ├── comctl.go ├── comctl_vs.go ├── dwmapi.go ├── err_error.go ├── err_hresult.go ├── gdi.go ├── kernel.go ├── ole.go ├── ole_oleaut.go ├── ole_shell.go ├── ole_shell_pkey.go ├── shell.go ├── user.go ├── user_386.go ├── user_amd64.go ├── user_nm.go ├── user_wm.go ├── uxtheme.go └── version.go ├── comctl_funcs.go ├── comctl_himagelist.go ├── comctl_hwnd.go ├── comctl_structs.go ├── comctl_unions.go ├── dwmapi_funcs.go ├── dwmapi_hwnd.go ├── dwmapi_unions.go ├── gdi_funcs.go ├── gdi_hbitmap.go ├── gdi_hbrush.go ├── gdi_hdc.go ├── gdi_hfont.go ├── gdi_hgdiobject.go ├── gdi_hpalette.go ├── gdi_hpen.go ├── gdi_hrgn.go ├── gdi_structs.go ├── kernel_funcs.go ├── kernel_funcs_386.go ├── kernel_funcs_amd64.go ├── kernel_handle.go ├── kernel_hfile.go ├── kernel_hfile_386.go ├── kernel_hfile_amd64.go ├── kernel_hfilemap.go ├── kernel_hfind.go ├── kernel_hglobal.go ├── kernel_hheap.go ├── kernel_hinstance.go ├── kernel_hprocess.go ├── kernel_hprocsnap.go ├── kernel_hthread.go ├── kernel_structs.go ├── ole ├── IBindCtx.go ├── IDataObject.go ├── IDropTarget.go ├── IEnumString.go ├── ISequentialStream.go ├── IStream.go ├── IUnknown.go ├── Releaser.go ├── funcs.go ├── htaskmem.go ├── oleaut │ ├── IDispatch.go │ ├── IPicture.go │ ├── ITypeInfo.go │ ├── ITypeLib.go │ ├── bstr.go │ ├── funcs.go │ ├── structs.go │ └── variant.go ├── shell │ ├── IEnumShellItems.go │ ├── IFileDialog.go │ ├── IFileDialogEvents.go │ ├── IFileOpenDialog.go │ ├── IFileOperation.go │ ├── IFileOperationProgressSink.go │ ├── IFileSaveDialog.go │ ├── IModalWindow.go │ ├── IShellFolder.go │ ├── IShellItem.go │ ├── IShellItem2.go │ ├── IShellItemArray.go │ ├── IShellLink.go │ ├── ITaskbarList.go │ ├── ITaskbarList2.go │ ├── ITaskbarList3.go │ ├── ITaskbarList4.go │ ├── funcs.go │ └── structs.go └── structs.go ├── psapi_funcs.go ├── psapi_hprocess.go ├── psapi_structs.go ├── shell_funcs.go ├── shell_hdrop.go ├── shell_hwnd.go ├── shell_structs.go ├── user_funcs.go ├── user_haccel.go ├── user_hbrush.go ├── user_hclipboard.go ├── user_hcursor.go ├── user_hdc.go ├── user_hdwp.go ├── user_hicon.go ├── user_hinstance.go ├── user_hmenu.go ├── user_hmonitor.go ├── user_hprocess.go ├── user_hwnd.go ├── user_hwnd_386.go ├── user_hwnd_amd64.go ├── user_structs.go ├── user_unions.go ├── util_File.go ├── util_Ini.go ├── util_Path.go ├── util_Vec.go ├── util_Version.go ├── uxtheme_funcs.go ├── uxtheme_htheme.go ├── uxtheme_hwnd.go ├── uxtheme_structs.go ├── version_funcs.go ├── version_structs.go └── wstr ├── Array.go ├── Buf.go └── funcs.go /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 | -------------------------------------------------------------------------------- /example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rodrigocfd/windigo/c1ddb3bd76afbe3ca74d691690b376969dafe7bb/example.gif -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/rodrigocfd/windigo 2 | 3 | go 1.20 4 | -------------------------------------------------------------------------------- /internal/dll/dll.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package dll 4 | 5 | import ( 6 | "syscall" 7 | ) 8 | 9 | var ( 10 | Advapi32 = syscall.NewLazyDLL("advapi32.dll") 11 | Comctl32 = syscall.NewLazyDLL("comctl32.dll") 12 | Dwmapi = syscall.NewLazyDLL("dwmapi.dll") 13 | Gdi32 = syscall.NewLazyDLL("gdi32.dll") 14 | Kernel32 = syscall.NewLazyDLL("kernel32.dll") 15 | Ole32 = syscall.NewLazyDLL("ole32.dll") 16 | Oleaut32 = syscall.NewLazyDLL("oleaut32.dll") 17 | Psapi = syscall.NewLazyDLL("psapi.dll") 18 | Shell32 = syscall.NewLazyDLL("shell32.dll") 19 | Shlwapi = syscall.NewLazyDLL("shlwapi") 20 | User32 = syscall.NewLazyDLL("user32.dll") 21 | Uxtheme = syscall.NewLazyDLL("uxtheme.dll") 22 | Version = syscall.NewLazyDLL("version.dll") 23 | ) 24 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /internal/utl/com.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 ComValidateObj(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 | // Creates a COM object, assign it to the pointer, and sets is **IUnknownVt. 64 | // 65 | // Returns the underlying pointed-to object. 66 | func ComCreateObj(ppOut interface{}, ppIUnknownVt unsafe.Pointer) interface{} { 67 | pTarget := reflect.ValueOf(ppOut).Elem() // *IUnknown 68 | ty := reflect.TypeOf(ppOut).Elem().Elem() // IUnknown 69 | pTarget.Set(reflect.New(ty)) // instantiate new object on the heap and assign its pointer 70 | 71 | addrField0 := pTarget.Elem().Field(0).UnsafeAddr() 72 | *(*uintptr)(unsafe.Pointer(addrField0)) = uintptr(ppIUnknownVt) // assign ppvt field 73 | 74 | return pTarget.Interface() 75 | } 76 | -------------------------------------------------------------------------------- /internal/utl/consts.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package utl 4 | 5 | // Internal constants for ole/shell. 6 | const ( 7 | INFOTIPSIZE = 1024 8 | ) 9 | 10 | // Internal constants for comctl. 11 | const ( 12 | L_MAX_URL_LENGTH = 2048 + 32 + 4 13 | MAX_LINKID_TEXT = 48 14 | ) 15 | 16 | // Internal constants for gdi. 17 | const ( 18 | CCHDEVICENAME = 32 19 | CCHFORMNAME = 32 20 | CLR_INVALID = 0xffff_ffff 21 | DM_SPECVERSION = 0x0401 22 | GDI_ERR = 0xffff_ffff 23 | HGDI_ERROR = 0xffff_ffff 24 | HIMETRIC_PER_INCH = 2540 25 | LF_FACESIZE = 32 26 | MAXLONG = 0x7fff_ffff 27 | REGION_ERROR = 0 28 | THREAD_PRIORITY_ERROR_RETURN = MAXLONG 29 | ) 30 | 31 | // Internal constants for kernel. 32 | const ( 33 | GMEM_INVALID_HANDLE = 0x8000 34 | INFINITE = 0xffff_ffff 35 | INVALID_HANDLE_VALUE = -1 36 | MAX_MODULE_NAME32 = 255 37 | MAX_PATH = 260 38 | TIME_ZONE_INVALID = 0xffff_ffff 39 | ) 40 | 41 | // Internal constants for user. 42 | const ( 43 | CCHILDREN_TITLEBAR = 5 44 | WC_DIALOG = uint16(0x8002) 45 | ) 46 | -------------------------------------------------------------------------------- /internal/utl/errors.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package utl 4 | 5 | import ( 6 | "syscall" 7 | 8 | "github.com/rodrigocfd/windigo/win/co" 9 | ) 10 | 11 | // Error handling syntactic sugar for syscalls which call GetLastError(). 12 | func ZeroAsGetLastError(ret uintptr, err syscall.Errno) error { 13 | if ret == 0 { 14 | return co.ERROR(err) 15 | } 16 | return nil 17 | } 18 | 19 | // Error handling syntactic sugar for syscalls directly returning a standard 20 | // Windows error. 21 | func ZeroAsSysError(ret uintptr) error { 22 | if wErr := co.ERROR(ret); wErr != co.ERROR_SUCCESS { 23 | return wErr 24 | } 25 | return nil 26 | } 27 | 28 | // Error handling syntactic sugar for syscalls returning the standard 29 | // co.ERROR_INVALID_PARAMETER. 30 | func ZeroAsSysInvalidParm(ret uintptr) error { 31 | if ret == 0 { 32 | return co.ERROR_INVALID_PARAMETER 33 | } 34 | return nil 35 | } 36 | 37 | // Error handling syntactic sugar for syscalls returning -1. 38 | func Minus1AsSysInvalidParm(ret uintptr) error { 39 | if int32(ret) == -1 { 40 | return co.ERROR_INVALID_PARAMETER 41 | } 42 | return nil 43 | } 44 | 45 | // Error handling syntactic sugar for syscalls directly returning an HRESULT. 46 | func ErrorAsHResult(ret uintptr) error { 47 | if hr := co.HRESULT(ret); hr != co.HRESULT_S_OK { 48 | return hr 49 | } 50 | return nil 51 | } 52 | -------------------------------------------------------------------------------- /internal/utl/str.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package utl 4 | 5 | import ( 6 | "strings" 7 | "unicode/utf8" 8 | ) 9 | 10 | // "&He && she" becomes "He & she". 11 | func RemoveAccelAmpersands(text string) string { 12 | runes := []rune(text) 13 | var buf strings.Builder 14 | buf.Grow(len(runes)) // prealloc for performance 15 | 16 | for i := 0; i < len(runes)-1; i++ { 17 | if runes[i] == '&' && runes[i+1] != '&' { 18 | continue 19 | } 20 | buf.WriteRune(runes[i]) 21 | } 22 | if runes[len(runes)-1] != '&' { 23 | buf.WriteRune(runes[len(runes)-1]) 24 | } 25 | return buf.String() 26 | } 27 | 28 | // Removes anchor markdowns from SysLink texts. Will fail if '<' char is present 29 | // without delimiting a tag. 30 | func RemoveHtmlAnchor(text string) string { 31 | var buf strings.Builder 32 | buf.Grow(utf8.RuneCountInString(text)) 33 | 34 | withinAnchor := false 35 | for _, ch := range text { 36 | if withinAnchor { 37 | if ch == '>' { 38 | withinAnchor = false 39 | continue 40 | } 41 | } 42 | if ch == '<' { 43 | withinAnchor = true 44 | continue 45 | } 46 | if !withinAnchor { 47 | buf.WriteRune(ch) 48 | } 49 | } 50 | return buf.String() 51 | } 52 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /resources/gopher.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rodrigocfd/windigo/c1ddb3bd76afbe3ca74d691690b376969dafe7bb/resources/gopher.ico -------------------------------------------------------------------------------- /resources/minimal.syso: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rodrigocfd/windigo/c1ddb3bd76afbe3ca74d691690b376969dafe7bb/resources/minimal.syso -------------------------------------------------------------------------------- /resources/win10.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /ui/ctl_ComboBoxItems.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ui 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/rodrigocfd/windigo/win" 9 | "github.com/rodrigocfd/windigo/win/co" 10 | "github.com/rodrigocfd/windigo/win/wstr" 11 | ) 12 | 13 | // The items collection. 14 | // 15 | // You cannot create this object directly, it will be created automatically 16 | // by the owning [ComboBox]. 17 | type CollectionComboBoxItems struct { 18 | owner *ComboBox 19 | } 20 | 21 | // Adds one or more items using [CB_ADDSTRING]. 22 | // 23 | // [CB_ADDSTRING]: https://learn.microsoft.com/en-us/windows/win32/controls/cb-addstring 24 | func (me *CollectionComboBoxItems) Add(texts ...string) { 25 | buf := wstr.NewBuf[wstr.Stack20]() 26 | for _, text := range texts { 27 | buf.Set(text, wstr.ALLOW_EMPTY) 28 | me.owner.hWnd.SendMessage(co.CB_ADDSTRING, 29 | 0, win.LPARAM(buf.UnsafePtr())) 30 | } 31 | } 32 | 33 | // Returns all items with [CB_GETLBTEXT]. 34 | // 35 | // [CB_GETLBTEXT]: https://learn.microsoft.com/en-us/windows/win32/controls/cb-getlbtext 36 | func (me *CollectionComboBoxItems) All() []string { 37 | buf := wstr.NewBuf[wstr.Stack64]() 38 | nItems := me.Count() 39 | items := make([]string, 0, nItems) 40 | 41 | for i := uint(0); i < nItems; i++ { 42 | nChars, _ := me.owner.hWnd.SendMessage(co.CB_GETLBTEXTLEN, win.WPARAM(i), 0) 43 | buf.Resize(uint(nChars) + 1) 44 | 45 | me.owner.hWnd.SendMessage(co.CB_GETLBTEXT, 46 | win.WPARAM(i), win.LPARAM(buf.UnsafePtr())) 47 | items = append(items, wstr.WstrSliceToStr(buf.HotSlice())) 48 | } 49 | 50 | return items 51 | } 52 | 53 | // Retrieves the number of items with [CB_GETCOUNT]. 54 | // 55 | // [CB_GETCOUNT]: https://learn.microsoft.com/en-us/windows/win32/controls/cb-getcount 56 | func (me *CollectionComboBoxItems) Count() uint { 57 | n, _ := me.owner.hWnd.SendMessage(co.CB_GETCOUNT, 0, 0) 58 | return uint(n) 59 | } 60 | 61 | // Deletes all items with [CB_RESETCONTENT]. 62 | // 63 | // [CB_RESETCONTENT]: https://learn.microsoft.com/en-us/windows/win32/controls/cb-resetcontent 64 | func (me *CollectionComboBoxItems) DeleteAll() { 65 | me.owner.hWnd.SendMessage(co.CB_RESETCONTENT, 0, 0) 66 | } 67 | 68 | // Returns the item at the given index with [CB_GETLBTEXT]. 69 | // 70 | // Panics if the index is not valid. 71 | // 72 | // [CB_GETLBTEXT]: https://learn.microsoft.com/en-us/windows/win32/controls/cb-getlbtext 73 | func (me *CollectionComboBoxItems) Get(index int) string { 74 | buf := wstr.NewBuf[wstr.Stack64]() 75 | nChars, _ := me.owner.hWnd.SendMessage(co.CB_GETLBTEXTLEN, win.WPARAM(index), 0) 76 | if int32(nChars) == -1 { // CB_ERR 77 | panic(fmt.Sprintf("Invalid ComboBox index: %d", index)) 78 | } 79 | 80 | buf.Resize(uint(nChars) + 1) 81 | me.owner.hWnd.SendMessage(co.CB_GETLBTEXT, 82 | win.WPARAM(index), win.LPARAM(buf.UnsafePtr())) 83 | return wstr.WstrSliceToStr(buf.HotSlice()) 84 | } 85 | 86 | // Returns the last item with [CB_GETLBTEXT]. 87 | // 88 | // Panics if empty. 89 | // 90 | // [CB_GETLBTEXT]: https://learn.microsoft.com/en-us/windows/win32/controls/cb-getlbtext 91 | func (me *CollectionComboBoxItems) Last() string { 92 | return me.Get(int(me.Count()) - 1) 93 | } 94 | 95 | // Selects the given item with [CB_SETCURSEL]. 96 | // 97 | // If index is -1, selection is cleared. 98 | // 99 | // [CB_SETCURSEL]: https://learn.microsoft.com/en-us/windows/win32/controls/cb-setcursel 100 | func (me *CollectionComboBoxItems) Select(index int) { 101 | me.owner.hWnd.SendMessage(co.CB_SETCURSEL, win.WPARAM(index), 0) 102 | } 103 | 104 | // Retrieves the selected index with [CB_GETCURSEL]. 105 | // 106 | // If no item is selected, returns -1. 107 | // 108 | // [CB_GETCURSEL]: https://learn.microsoft.com/en-us/windows/win32/controls/cb-getcursel 109 | func (me *CollectionComboBoxItems) Selected() int { 110 | n, _ := me.owner.hWnd.SendMessage(co.CB_GETCURSEL, 0, 0) 111 | return int(n) 112 | } 113 | 114 | // Returns the string at the given position with [CB_GETLBTEXT]. 115 | // 116 | // Panics on error. 117 | // 118 | // [CB_GETLBTEXT]: https://learn.microsoft.com/en-us/windows/win32/controls/cb-getlbtext 119 | func (me *CollectionComboBoxItems) Text(index uint) string { 120 | nChars, _ := me.owner.hWnd.SendMessage(co.CB_GETLBTEXTLEN, win.WPARAM(index), 0) 121 | if int32(nChars) == -1 { 122 | panic(fmt.Sprintf("CB_GETLBTEXTLEN failed at item %d.", index)) 123 | } 124 | 125 | buf := wstr.NewBufSized[wstr.Stack64](uint(nChars) + 1) // room for terminating null 126 | 127 | me.owner.hWnd.SendMessage(co.CB_GETLBTEXT, 128 | win.WPARAM(index), win.LPARAM(buf.UnsafePtr())) 129 | return wstr.WstrSliceToStr(buf.HotSlice()) 130 | } 131 | -------------------------------------------------------------------------------- /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/win" 10 | "github.com/rodrigocfd/windigo/win/co" 11 | "github.com/rodrigocfd/windigo/win/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 | text16 := wstr.NewBufWith[wstr.Stack20](text, wstr.ALLOW_EMPTY) 30 | hdi := win.HDITEM{ 31 | Mask: co.HDI_TEXT | co.HDI_WIDTH, 32 | Cxy: int32(width), 33 | } 34 | hdi.SetPszText(text16.HotSlice()) 35 | 36 | newIdxRet, err := me.owner.hWnd.SendMessage(co.HDM_INSERTITEM, 37 | 0xffff, win.LPARAM(unsafe.Pointer(&hdi))) 38 | newIdx := int(newIdxRet) 39 | if err != nil || newIdx == -1 { 40 | panic(fmt.Sprintf("HDM_INSERTITEM \"%s\" failed.", text)) 41 | } 42 | 43 | return me.Get(newIdx) 44 | } 45 | 46 | // Returns all items. 47 | func (me *CollectionHeaderItems) All() []HeaderItem { 48 | nItems := me.Count() 49 | items := make([]HeaderItem, 0, nItems) 50 | for i := uint(0); i < nItems; i++ { 51 | items = append(items, me.Get(int(i))) 52 | } 53 | return items 54 | } 55 | 56 | // Sends [HDM_GETORDERARRAY] to retrieve the items in the current order. 57 | // 58 | // [HDM_GETORDERARRAY]: https://learn.microsoft.com/en-us/windows/win32/controls/hdm-getorderarray 59 | func (me *CollectionHeaderItems) AllOrdered() []HeaderItem { 60 | nItems := me.Count() 61 | indexes := make([]int32, nItems) 62 | 63 | me.owner.hWnd.SendMessage(co.HDM_GETORDERARRAY, 64 | win.WPARAM(nItems), win.LPARAM(unsafe.Pointer(&indexes[0]))) 65 | 66 | items := make([]HeaderItem, 0, nItems) 67 | for _, index := range indexes { 68 | items = append(items, me.Get(int(index))) 69 | } 70 | return items 71 | } 72 | 73 | // Retrieves the number of items with [HDM_GETITEMCOUNT]. 74 | // 75 | // Panics on error. 76 | // 77 | // [HDM_GETITEMCOUNT]: https://learn.microsoft.com/en-us/windows/win32/controls/hdm-getitemcount 78 | func (me *CollectionHeaderItems) Count() uint { 79 | countRet, err := me.owner.hWnd.SendMessage(co.HDM_GETITEMCOUNT, 0, 0) 80 | count := int(countRet) 81 | if err != nil || count == -1 { 82 | panic("HDM_GETITEMCOUNT failed.") 83 | } 84 | return uint(count) 85 | } 86 | 87 | // Returns the item at the given index. 88 | func (me *CollectionHeaderItems) Get(index int) HeaderItem { 89 | return HeaderItem{ 90 | owner: me.owner, 91 | index: int32(index), 92 | } 93 | } 94 | 95 | // Sends [HDM_ORDERTOINDEX] to retrieve the item at the given order. 96 | // 97 | // [HDM_ORDERTOINDEX]: https://learn.microsoft.com/en-us/windows/win32/controls/hdm-ordertoindex 98 | func (me *CollectionHeaderItems) GetByOrder(order uint) HeaderItem { 99 | idx, _ := me.owner.hWnd.SendMessage(co.HDM_ORDERTOINDEX, win.WPARAM(order), 0) 100 | return me.Get(int(idx)) 101 | } 102 | 103 | // Returns the last item. 104 | func (me *CollectionHeaderItems) Last() HeaderItem { 105 | return me.Get(int(me.Count()) - 1) 106 | } 107 | 108 | // Sends a [HDM_SETORDERARRAY] to reorder the items with the given order. 109 | // 110 | // [HDM_SETORDERARRAY]: https://learn.microsoft.com/en-us/windows/win32/controls/hdm-setorderarray 111 | func (me *CollectionHeaderItems) Reorder(indexes []int) { 112 | buf := make([]int32, 0, len(indexes)) 113 | for _, index := range indexes { 114 | buf = append(buf, int32(index)) 115 | } 116 | 117 | me.owner.hWnd.SendMessage(co.HDM_SETORDERARRAY, 118 | win.WPARAM(len(buf)), win.LPARAM(unsafe.Pointer(&buf[0]))) 119 | } 120 | -------------------------------------------------------------------------------- /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/win" 10 | "github.com/rodrigocfd/windigo/win/co" 11 | "github.com/rodrigocfd/windigo/win/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 | title16 := wstr.NewBufWith[wstr.Stack20](title, wstr.ALLOW_EMPTY) 36 | lvc := win.LVCOLUMN{ 37 | Mask: co.LVCF_TEXT | co.LVCF_WIDTH, 38 | Cx: int32(width), 39 | } 40 | lvc.SetPszText(title16.HotSlice()) 41 | 42 | newIdxRet, err := me.owner.hWnd.SendMessage(co.LVM_INSERTCOLUMN, 43 | 0xffff, win.LPARAM(unsafe.Pointer(&lvc))) 44 | newIdx := int(newIdxRet) 45 | if err != nil || newIdx == -1 { 46 | panic(fmt.Sprintf("LVM_INSERTCOLUMN \"%s\" failed.", title)) 47 | } 48 | 49 | return me.Get(newIdx) 50 | } 51 | 52 | // Returns all columns. 53 | func (me *CollectionListViewCols) All() []ListViewCol { 54 | nCols := me.Count() 55 | cols := make([]ListViewCol, 0, nCols) 56 | for i := uint(0); i < nCols; i++ { 57 | cols = append(cols, me.Get(int(i))) 58 | } 59 | return cols 60 | } 61 | 62 | // Retrieves the number of columns with [HDM_GETITEMCOUNT]. 63 | // 64 | // Panics if the list view has no header. 65 | // 66 | // [HDM_GETITEMCOUNT]: https://learn.microsoft.com/en-us/windows/win32/controls/hdm-getitemcount 67 | func (me *CollectionListViewCols) Count() uint { 68 | if me.owner.Header() == nil { 69 | panic("This ListView has no header.") 70 | } 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(int(me.Count()) - 1) 86 | } 87 | -------------------------------------------------------------------------------- /ui/ctl_StatusBar.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ui 4 | 5 | import ( 6 | "unsafe" 7 | 8 | "github.com/rodrigocfd/windigo/internal/utl" 9 | "github.com/rodrigocfd/windigo/win" 10 | "github.com/rodrigocfd/windigo/win/co" 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.Wm(parent.base().wndTy.initMsg(), func(_ Wm) uintptr { 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 | return 0 // ignored 43 | }) 44 | 45 | parent.base().beforeUserEvents.WmSize(func(p WmSize) { 46 | me.Parts.resizeToFitParent(p) 47 | }) 48 | 49 | return me 50 | } 51 | 52 | // Exposes all the control notifications the can be handled. 53 | // 54 | // Panics if called after the control has been created. 55 | func (me *StatusBar) On() *EventsStatusBar { 56 | me.panicIfAddingEventAfterCreated() 57 | return &me.events 58 | } 59 | 60 | // Native [status bar] control events. 61 | // 62 | // You cannot create this object directly, it will be created automatically 63 | // by the owning control. 64 | // 65 | // [status bar]: https://learn.microsoft.com/en-us/windows/win32/controls/status-bars 66 | type EventsStatusBar struct { 67 | ctrlId uint16 68 | parentEvents *EventsWindow 69 | } 70 | 71 | // [NM_CLICK] message handler. 72 | // 73 | // [NM_CLICK]: https://learn.microsoft.com/en-us/windows/win32/controls/nm-click-status-bar 74 | func (me *EventsStatusBar) NmClick(fun func(p *win.NMMOUSE) bool) { 75 | me.parentEvents.WmNotify(me.ctrlId, co.NM_CLICK, func(p unsafe.Pointer) uintptr { 76 | return utl.BoolToUintptr(fun((*win.NMMOUSE)(p))) 77 | }) 78 | } 79 | 80 | // [NM_DBLCLK] message handler. 81 | // 82 | // [NM_DBLCLK]: https://learn.microsoft.com/en-us/windows/win32/controls/nm-dblclk-status-bar 83 | func (me *EventsStatusBar) NmDblClk(fun func(p *win.NMMOUSE) bool) { 84 | me.parentEvents.WmNotify(me.ctrlId, co.NM_DBLCLK, func(p unsafe.Pointer) uintptr { 85 | return utl.BoolToUintptr(fun((*win.NMMOUSE)(p))) 86 | }) 87 | } 88 | 89 | // [NM_RCLICK] message handler. 90 | // 91 | // [NM_RCLICK]: https://learn.microsoft.com/en-us/windows/win32/controls/nm-rclick-status-bar 92 | func (me *EventsStatusBar) NmRClick(fun func(p *win.NMMOUSE) bool) { 93 | me.parentEvents.WmNotify(me.ctrlId, co.NM_RCLICK, func(p unsafe.Pointer) uintptr { 94 | return utl.BoolToUintptr(fun((*win.NMMOUSE)(p))) 95 | }) 96 | } 97 | 98 | // [NM_RDBLCLK] message handler. 99 | // 100 | // [NM_RDBLCLK]: https://learn.microsoft.com/en-us/windows/win32/controls/nm-rdblclk-status-bar 101 | func (me *EventsStatusBar) NmRDblClk(fun func(p *win.NMMOUSE) bool) { 102 | me.parentEvents.WmNotify(me.ctrlId, co.NM_RDBLCLK, func(p unsafe.Pointer) uintptr { 103 | return utl.BoolToUintptr(fun((*win.NMMOUSE)(p))) 104 | }) 105 | } 106 | 107 | // [SBN_SIMPLEMODECHANGE] message handler. 108 | // 109 | // [SBN_SIMPLEMODECHANGE]: https://learn.microsoft.com/en-us/windows/win32/controls/sbn-simplemodechange 110 | func (me *EventsStatusBar) SbnSimpleModeChange(fun func(p *win.NMMOUSE)) { 111 | me.parentEvents.WmNotify(me.ctrlId, co.SBN_SIMPLEMODECHANGE, func(p unsafe.Pointer) uintptr { 112 | fun((*win.NMMOUSE)(p)) 113 | return me.parentEvents.defProcVal 114 | }) 115 | } 116 | -------------------------------------------------------------------------------- /ui/ctl_StatusBarPart.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ui 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/rodrigocfd/windigo/win" 9 | "github.com/rodrigocfd/windigo/win/co" 10 | "github.com/rodrigocfd/windigo/win/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(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 | text16 := wstr.NewBufWith[wstr.Stack20](text, wstr.ALLOW_EMPTY) 58 | ret, _ := me.owner.hWnd.SendMessage(co.SB_SETTEXT, 59 | win.MAKEWPARAM(win.MAKEWORD(uint8(me.index), 0), 0), 60 | win.LPARAM(text16.UnsafePtr())) 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, win.WPARAM(me.index), 0) 73 | len := uint(nLen) 74 | if len == 0 { 75 | return "" 76 | } 77 | 78 | buf := wstr.NewBufSized[wstr.Stack64](len + 1) // room for terminating null 79 | me.owner.hWnd.SendMessage(co.SB_GETTEXT, 80 | win.WPARAM(me.index), win.LPARAM(buf.UnsafePtr())) 81 | return wstr.WstrSliceToStr(buf.HotSlice()) 82 | } 83 | -------------------------------------------------------------------------------- /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/win" 10 | "github.com/rodrigocfd/windigo/win/co" 11 | "github.com/rodrigocfd/windigo/win/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), 39 | uint(rcTab.Right-rcTab.Left), uint(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(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 | text16 := wstr.NewBufWith[wstr.Stack20](text, wstr.ALLOW_EMPTY) 68 | tci := win.TCITEM{ 69 | Mask: co.TCIF_TEXT, 70 | } 71 | tci.SetPszText(text16.HotSlice()) 72 | 73 | ret, err := me.owner.hWnd.SendMessage(co.TCM_SETITEM, 74 | win.WPARAM(me.index), win.LPARAM(unsafe.Pointer(&tci))) 75 | if err != nil || ret == 0 { 76 | panic(fmt.Sprintf("TCM_SETITEM %d to \"%s\" failed.", me.index, text)) 77 | } 78 | 79 | return me 80 | } 81 | 82 | // Retrieves the text with [TCM_GETITEM]. 83 | // 84 | // Panics on error. 85 | // 86 | // [TCM_GETITEM]: https://learn.microsoft.com/en-us/windows/win32/controls/tcm-getitem 87 | func (me TabItem) Text() string { 88 | var textBuf [64]uint16 // arbitrary 89 | 90 | tci := win.TCITEM{ 91 | Mask: co.TCIF_TEXT, 92 | } 93 | tci.SetPszText(textBuf[:]) 94 | 95 | ret, err := me.owner.hWnd.SendMessage(co.TCM_GETITEM, 96 | win.WPARAM(me.index), win.LPARAM(unsafe.Pointer(&tci))) 97 | if err != nil || ret == 0 { 98 | panic(fmt.Sprintf("TCM_GETITEM %d failed.", me.index)) 99 | } 100 | 101 | return wstr.WstrSliceToStr(textBuf[:]) 102 | } 103 | -------------------------------------------------------------------------------- /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/win" 10 | "github.com/rodrigocfd/windigo/win/co" 11 | "github.com/rodrigocfd/windigo/win/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 | title16 := wstr.NewBufWith[wstr.Stack20](title, wstr.ALLOW_EMPTY) 24 | tci := win.TCITEM{ 25 | Mask: co.TCIF_TEXT, 26 | } 27 | tci.SetPszText(title16.HotSlice()) 28 | 29 | newIdxRet, err := me.owner.hWnd.SendMessage(co.TCM_INSERTITEM, 30 | 0x0fff_ffff, win.LPARAM(unsafe.Pointer(&tci))) 31 | newIdx := int(newIdxRet) 32 | if err != nil || newIdx == -1 { 33 | panic(fmt.Sprintf("TCM_INSERTITEM \"%s\" failed.", title)) 34 | } 35 | 36 | return me.Get(newIdx) 37 | } 38 | 39 | // Retrieves the number of items with [TCM_GETITEMCOUNT]. 40 | // 41 | // Panics on error. 42 | // 43 | // [TCM_GETITEMCOUNT]: https://learn.microsoft.com/en-us/windows/win32/controls/tcm-getitemcount 44 | func (me *CollectionTabItems) Count() uint { 45 | countRet, err := me.owner.hWnd.SendMessage(co.TCM_GETITEMCOUNT, 0, 0) 46 | count := int(countRet) 47 | if err != nil || count == -1 { 48 | panic("TCM_GETITEMCOUNT failed.") 49 | } 50 | return uint(count) 51 | } 52 | 53 | // Retrieves the focused item with [TCM_GETCURFOCUS], if any 54 | // 55 | // [TCM_GETCURFOCUS]: https://learn.microsoft.com/en-us/windows/win32/controls/tcm-getcurfocus 56 | func (me *CollectionTabItems) Focused() (TabItem, bool) { 57 | idxRet, _ := me.owner.hWnd.SendMessage(co.TCM_GETCURFOCUS, 0, 0) 58 | idx := int(idxRet) 59 | if idx == -1 { 60 | return TabItem{}, false 61 | } 62 | return me.Get(idx), true 63 | } 64 | 65 | // Returns the item at the given index. 66 | func (me *CollectionTabItems) Get(index int) TabItem { 67 | return TabItem{ 68 | owner: me.owner, 69 | index: int32(index), 70 | } 71 | } 72 | 73 | // Retrieves the selected item with [TCM_GETCURSEL], if any 74 | // 75 | // [TCM_GETCURSEL]: https://learn.microsoft.com/en-us/windows/win32/controls/tcm-getcursel 76 | func (me *CollectionTabItems) Selected() (TabItem, bool) { 77 | idxRet, _ := me.owner.hWnd.SendMessage(co.TCM_GETCURSEL, 0, 0) 78 | idx := int(idxRet) 79 | if idx == -1 { 80 | return TabItem{}, false 81 | } 82 | return me.Get(idx), true 83 | } 84 | -------------------------------------------------------------------------------- /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/win" 10 | "github.com/rodrigocfd/windigo/win/co" 11 | "github.com/rodrigocfd/windigo/win/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 | tbb := win.TBBUTTON{ 30 | IBitmap: int32(iconIndex), 31 | IdCommand: int32(cmdId), 32 | FsStyle: co.BTNS_AUTOSIZE, 33 | FsState: co.TBSTATE_ENABLED, 34 | IString: wstr.StrToWstrPtr(text), 35 | } 36 | 37 | ret, _ := me.owner.hWnd.SendMessage(co.TB_ADDBUTTONS, 38 | 1, win.LPARAM(unsafe.Pointer(&tbb))) 39 | if ret == 0 { 40 | panic(fmt.Sprintf("TB_ADDBUTTONS \"%s\" failed.", text)) 41 | } 42 | 43 | me.owner.hWnd.SendMessage(co.TB_AUTOSIZE, 0, 0) 44 | } 45 | 46 | // Retrieves the number of buttons with [TB_BUTTONCOUNT]. 47 | // 48 | // [TB_BUTTONCOUNT]: https://learn.microsoft.com/en-us/windows/win32/controls/tb-buttoncount 49 | func (me *CollectionToolbarButtons) Count() uint { 50 | ret, _ := me.owner.hWnd.SendMessage(co.TB_BUTTONCOUNT, 0, 0) 51 | return uint(ret) 52 | } 53 | -------------------------------------------------------------------------------- /ui/ctl_TreeViewItems.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ui 4 | 5 | import ( 6 | "github.com/rodrigocfd/windigo/win" 7 | "github.com/rodrigocfd/windigo/win/co" 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/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 | // Cannot be called after the window was 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 | -------------------------------------------------------------------------------- /ui/wnd_BaseDlg.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ui 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/win" 10 | "github.com/rodrigocfd/windigo/win/co" 11 | ) 12 | 13 | // Base to all dialog-based windows created with CreateDialogParam and 14 | // DialogBoxParam. 15 | type _BaseDlg struct { 16 | _BaseContainer 17 | dlgId uint16 18 | } 19 | 20 | // Constructor. 21 | func newBaseDlg(dlgId uint16) _BaseDlg { 22 | if dlgId == 0 { 23 | panic("Dialog ID must be specified.") 24 | } 25 | 26 | return _BaseDlg{ 27 | _BaseContainer: newBaseContainer(_WNDTY_DLG), 28 | dlgId: dlgId, 29 | } 30 | } 31 | 32 | func (me *_BaseDlg) createDialogParam(hInst win.HINSTANCE, hParent win.HWND) { 33 | if me.hWnd != 0 { 34 | panic("Cannot create dialog twice.") 35 | } 36 | dlgProcCallback() 37 | 38 | // The hWnd member is saved in WM_INITDIALOG processing in dlgProc. 39 | _, err := hInst.CreateDialogParam(win.ResIdInt(me.dlgId), hParent, dlgProcCallback(), 40 | win.LPARAM(unsafe.Pointer(me))) // pass pointer to object itself 41 | if err != nil { 42 | panic(err) 43 | } 44 | } 45 | 46 | func (me *_BaseDlg) dialogBoxParam(hInst win.HINSTANCE, hParent win.HWND) { 47 | if me.hWnd != 0 { 48 | panic("Cannot create dialog twice.") 49 | } 50 | 51 | // The hWnd member is saved in WM_INITDIALOG processing in dlgProc. 52 | _, err := hInst.DialogBoxParam(win.ResIdInt(me.dlgId), hParent, dlgProcCallback(), 53 | win.LPARAM(unsafe.Pointer(me))) // pass pointer to object itself 54 | if err != nil { 55 | panic(err) 56 | } 57 | } 58 | 59 | func (me *_BaseDlg) setIcon(hInst win.HINSTANCE, iconId uint16) error { 60 | hGdiobjIcon, err := hInst.LoadImage(win.ResIdInt(iconId), 61 | co.IMAGE_ICON, 16, 16, co.LR_DEFAULTCOLOR|co.LR_SHARED) 62 | if err != nil { 63 | return err 64 | } 65 | hGdi32, err := hInst.LoadImage(win.ResIdInt(iconId), 66 | co.IMAGE_ICON, 32, 32, co.LR_DEFAULTCOLOR|co.LR_SHARED) 67 | if err != nil { 68 | return err 69 | } 70 | hIcon16, hIcon32 := win.HICON(hGdiobjIcon), win.HICON(hGdi32) 71 | 72 | me.hWnd.SendMessage(co.WM_SETICON, win.WPARAM(co.ICON_SZ_SMALL), win.LPARAM(hIcon16)) 73 | me.hWnd.SendMessage(co.WM_SETICON, win.WPARAM(co.ICON_SZ_BIG), win.LPARAM(hIcon32)) 74 | return nil 75 | } 76 | 77 | var _dlgProcCallback uintptr 78 | 79 | func dlgProcCallback() uintptr { 80 | if _dlgProcCallback == 0 { 81 | _dlgProcCallback = syscall.NewCallback( 82 | func(hDlg win.HWND, uMsg co.WM, wParam win.WPARAM, lParam win.LPARAM) uintptr { 83 | var pMe *_BaseDlg 84 | 85 | if uMsg == co.WM_INITDIALOG { 86 | pMe = (*_BaseDlg)(unsafe.Pointer(lParam)) 87 | pMe.hWnd = hDlg 88 | hDlg.SetWindowLongPtr(co.GWLP_DWLP_USER, uintptr(unsafe.Pointer(pMe))) 89 | } else { 90 | ptr, _ := hDlg.GetWindowLongPtr(co.GWLP_DWLP_USER) // retrieve 91 | pMe = (*_BaseDlg)(unsafe.Pointer(ptr)) 92 | } 93 | 94 | // If no pointer stored, then no processing is done. 95 | // Prevents processing before WM_INITDIALOG and after WM_NCDESTROY. 96 | if pMe == nil { 97 | return 0 // FALSE 98 | } 99 | 100 | // Execute before-user closures, keep track if at least one was executed. 101 | msg := Wm{uMsg, wParam, lParam} 102 | atLeastOneBeforeUser := pMe.beforeUserEvents.processAllMessages(msg) 103 | 104 | // Execute user closure, if any. 105 | userRet, hasUserRet := pMe.userEvents.processLastMessage(msg) 106 | 107 | // Execute post-user closures, keep track if at least one was executed. 108 | atLeastOneAfterUser := pMe.afterUserEvents.processAllMessages(msg) 109 | 110 | if uMsg == co.WM_INITDIALOG { 111 | pMe.removeWmCreateInitdialog() // will release all memory in these closures 112 | } else if uMsg == co.WM_NCDESTROY { // always check 113 | hDlg.SetWindowLongPtr(co.GWLP_DWLP_USER, 0) 114 | pMe.hWnd = win.HWND(0) 115 | pMe.clearMessages() 116 | } 117 | 118 | if hasUserRet { 119 | return userRet 120 | } else if atLeastOneBeforeUser || atLeastOneAfterUser { 121 | return 1 // TRUE 122 | } else { 123 | return 0 // FALSE 124 | } 125 | }, 126 | ) 127 | } 128 | return _dlgProcCallback 129 | } 130 | -------------------------------------------------------------------------------- /ui/wnd_Control.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ui 4 | 5 | import ( 6 | "github.com/rodrigocfd/windigo/win" 7 | "github.com/rodrigocfd/windigo/win/co" 8 | ) 9 | 10 | // Custom control. 11 | // 12 | // Implements: 13 | // - [Window] 14 | // - [ChildControl] 15 | // - [Parent] 16 | type Control struct { 17 | raw *_ControlRaw 18 | dlg *_ControlDlg 19 | parent Parent 20 | } 21 | 22 | // Creates a new custom control with [CreateWindowEx]. 23 | // 24 | // [CreateWindowEx]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw 25 | func NewControl(parent Parent, opts *VarOptsControl) *Control { 26 | return &Control{ 27 | raw: newControlRaw(parent, opts), 28 | parent: parent, 29 | } 30 | } 31 | 32 | // Creates a new dialog-based custom control with [CreateDialogParam]. 33 | // 34 | // [CreateDialogParam]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createdialogparamw 35 | func NewControlDlg(parent Parent, opts *VarOptsControlDlg) *Control { 36 | return &Control{ 37 | dlg: newControlDlg(parent, opts), 38 | parent: parent, 39 | } 40 | } 41 | 42 | // Returns the parent container of this control. 43 | func (me *Control) Parent() Parent { 44 | return me.parent 45 | } 46 | 47 | // Returns the underlying HWND handle of this window. 48 | // 49 | // Implements [Window]. 50 | // 51 | // Note that this handle is initially zero, existing only after window creation. 52 | func (me *Control) Hwnd() win.HWND { 53 | if me.raw != nil { 54 | return me.raw.hWnd 55 | } else { 56 | return me.dlg.hWnd 57 | } 58 | } 59 | 60 | // Returns the control ID, unique within the same Parent. 61 | // 62 | // Implements [ChildControl]. 63 | func (me *Control) CtrlId() uint16 { 64 | if me.raw != nil { 65 | return me.raw.ctrlId 66 | } else { 67 | return me.dlg.ctrlId 68 | } 69 | } 70 | 71 | // If parent is a dialog, sets the focus by sending [WM_NEXTDLGCTL]. This 72 | // draws the borders correctly in some undefined controls, like buttons. 73 | // Otherwise, calls [SetFocus]. 74 | // 75 | // Implements [ChildControl]. 76 | // 77 | // [WM_NEXTDLGCTL]: https://learn.microsoft.com/en-us/windows/win32/dlgbox/wm-nextdlgctl 78 | // [SetFocus]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setfocus 79 | func (me *Control) Focus() { 80 | hParent, _ := me.Hwnd().GetAncestor(co.GA_PARENT) 81 | isDialog, _ := hParent.IsDialog() 82 | if isDialog { 83 | hParent.SendMessage(co.WM_NEXTDLGCTL, win.WPARAM(me.Hwnd()), 1) 84 | } else { 85 | me.Hwnd().SetFocus() 86 | } 87 | } 88 | 89 | // Exposes all the window notifications the can be handled. 90 | // 91 | // Implements [Parent]. 92 | // 93 | // Panics if called after the window has been created. 94 | func (me *Control) On() *EventsWindow { 95 | if me.Hwnd() != 0 { 96 | panic("Cannot add event handling after the window has been created.") 97 | } 98 | 99 | if me.raw != nil { 100 | return &me.raw.userEvents 101 | } else { 102 | return &me.dlg.userEvents 103 | } 104 | } 105 | 106 | // This method is analog to [SendMessage] (synchronous), but intended to be 107 | // called from another thread, so a callback function can, tunelled by 108 | // [WNDPROC], run in the original thread of the window, thus allowing GUI 109 | // updates. With this, the user doesn't have to deal with a custom WM_ message. 110 | // 111 | // Implements [Parent]. 112 | // 113 | // # Example 114 | // 115 | // var wnd *ui.WindowModal // initialized somewhere 116 | // 117 | // wnd.On().WmCreate(func(p WmCreate) int { 118 | // go func() { 119 | // // process to be done in a parallel goroutine... 120 | // 121 | // wnd.UiThread(func() { 122 | // // update the UI in the original UI thread... 123 | // }) 124 | // }() 125 | // return 0 126 | // }) 127 | // 128 | // [SendMessage]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendmessagew 129 | // [WNDPROC]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nc-winuser-wndproc 130 | func (me *Control) UiThread(fun func()) { 131 | if me.raw != nil { 132 | me.raw.uiThread(fun) 133 | } else { 134 | me.dlg.uiThread(fun) 135 | } 136 | } 137 | 138 | // Implements [Parent]. 139 | func (me *Control) base() *_BaseContainer { 140 | if me.raw != nil { 141 | return &me.raw._BaseContainer 142 | } else { 143 | return &me.dlg._BaseContainer 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /ui/wnd_ControlDlg.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ui 4 | 5 | import ( 6 | "github.com/rodrigocfd/windigo/win" 7 | "github.com/rodrigocfd/windigo/win/co" 8 | ) 9 | 10 | type _ControlDlg struct { 11 | _BaseDlg 12 | ctrlId uint16 13 | } 14 | 15 | // Constructor. 16 | func newControlDlg(parent Parent, opts *VarOptsControlDlg) *_ControlDlg { 17 | setUniqueCtrlId(&opts.ctrlId) 18 | me := &_ControlDlg{ 19 | _BaseDlg: newBaseDlg(opts.dlgId), 20 | ctrlId: opts.ctrlId, 21 | } 22 | 23 | parent.base().beforeUserEvents.Wm(parent.base().wndTy.initMsg(), func(_ Wm) uintptr { 24 | hInst, _ := parent.Hwnd().HInstance() 25 | me.createDialogParam(hInst, parent.Hwnd()) 26 | me.hWnd.SetWindowLongPtr(co.GWLP_ID, uintptr(opts.ctrlId)) // give the control its ID 27 | me.hWnd.SetWindowPos(win.HWND(0), 28 | int(opts.position.X), int(opts.position.Y), 0, 0, 29 | co.SWP_NOZORDER|co.SWP_NOSIZE) 30 | parent.base().layout.Add(parent, me.hWnd, opts.layout) 31 | return 0 // ignored 32 | }) 33 | 34 | me.defaultMessageHandlers() 35 | return me 36 | } 37 | 38 | func (me *_ControlDlg) 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_NONE_NONE. 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 | -------------------------------------------------------------------------------- /ui/wnd_MainDlg.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ui 4 | 5 | import ( 6 | "github.com/rodrigocfd/windigo/win" 7 | "github.com/rodrigocfd/windigo/win/co" 8 | ) 9 | 10 | type _MainDlg struct { 11 | _BaseDlg 12 | iconId uint16 13 | accelTableId uint16 14 | } 15 | 16 | // Constructor. 17 | func newMainDlg(opts *VarOptsMainDlg) *_MainDlg { 18 | me := &_MainDlg{ 19 | _BaseDlg: newBaseDlg(opts.dlgId), 20 | iconId: opts.iconId, 21 | accelTableId: opts.accelTableId, 22 | } 23 | me.defaultMessageHandlers() 24 | return me 25 | } 26 | 27 | func (me *_MainDlg) runAsMain(hInst win.HINSTANCE) int { 28 | me.createDialogParam(hInst, win.HWND(0)) 29 | 30 | if me.iconId != 0 { 31 | me.setIcon(hInst, me.iconId) 32 | } 33 | 34 | var hAccel win.HACCEL 35 | if me.accelTableId != 0 { 36 | var err error 37 | hAccel, err = hInst.LoadAccelerators(win.ResIdInt(me.accelTableId)) 38 | if err != nil { 39 | panic(err) 40 | } 41 | } 42 | 43 | me.hWnd.ShowWindow(co.SW_SHOW) 44 | return me.runMainLoop(hAccel, true) 45 | } 46 | 47 | func (me *_MainDlg) defaultMessageHandlers() { 48 | me._BaseDlg._BaseContainer.defaultMessageHandlers() 49 | 50 | me.userEvents.WmClose(func() { 51 | me.hWnd.DestroyWindow() 52 | }) 53 | 54 | me.userEvents.WmNcDestroy(func() { 55 | win.PostQuitMessage(0) 56 | }) 57 | } 58 | 59 | // Options for [NewMainDlg]; returned by [OptsMainDlg]. 60 | type VarOptsMainDlg struct { 61 | dlgId uint16 62 | iconId uint16 63 | accelTableId uint16 64 | } 65 | 66 | // Options for [NewMainDlg]. 67 | func OptsMainDlg() *VarOptsMainDlg { 68 | return &VarOptsMainDlg{} 69 | } 70 | 71 | // Dialog resource ID passed to [CreateDialogParam]. 72 | // 73 | // Panics if not informed. 74 | // 75 | // [CreateDialogParam]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createdialogparamw 76 | func (o *VarOptsMainDlg) DlgId(id uint16) *VarOptsMainDlg { o.dlgId = id; return o } 77 | 78 | // Dialog icon ID passed to [WM_SETICON]. 79 | // 80 | // Defaults to none. 81 | // 82 | // [WM_SETICON]: https://learn.microsoft.com/en-us/windows/win32/winmsg/wm-seticon 83 | func (o *VarOptsMainDlg) IconId(id uint16) *VarOptsMainDlg { o.iconId = id; return o } 84 | 85 | // Accelerator table ID passed to [LoadAccelerators]. 86 | // 87 | // Defaults to none. 88 | // 89 | // [LoadAccelerators]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-loadacceleratorsw 90 | func (o *VarOptsMainDlg) AccelTableId(id uint16) *VarOptsMainDlg { o.accelTableId = id; return o } 91 | -------------------------------------------------------------------------------- /ui/wnd_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 *_ModalRaw 16 | dlg *_ModalDlg 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/wnd_ModalDlg.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ui 4 | 5 | import ( 6 | "github.com/rodrigocfd/windigo/win" 7 | "github.com/rodrigocfd/windigo/win/co" 8 | ) 9 | 10 | type _ModalDlg struct { 11 | _BaseDlg 12 | parent Parent 13 | } 14 | 15 | func newModalDlg(parent Parent, dlgId uint16) *_ModalDlg { 16 | me := &_ModalDlg{ 17 | _BaseDlg: newBaseDlg(dlgId), 18 | parent: parent, 19 | } 20 | me.defaultMessageHandlers() 21 | return me 22 | } 23 | 24 | func (me *_ModalDlg) showModal() { 25 | hInst, _ := me.parent.Hwnd().HInstance() 26 | me.dialogBoxParam(hInst, me.parent.Hwnd()) 27 | } 28 | 29 | func (me *_ModalDlg) defaultMessageHandlers() { 30 | me._BaseDlg._BaseContainer.defaultMessageHandlers() 31 | 32 | me.beforeUserEvents.WmInitDialog(func(_ WmInitDialog) bool { 33 | rcModal, _ := me.hWnd.GetWindowRect() 34 | rcParent, _ := me.parent.Hwnd().GetWindowRect() 35 | 36 | x := rcParent.Left + ((rcParent.Right - rcParent.Left) / 2) - (rcModal.Right-rcModal.Left)/2 37 | y := rcParent.Top + ((rcParent.Bottom - rcParent.Top) / 2) - (rcModal.Bottom-rcModal.Top)/2 38 | 39 | me.hWnd.SetWindowPos(win.HWND(0), int(x), int(y), 0, 0, co.SWP_NOSIZE|co.SWP_NOZORDER) 40 | 41 | return true // ignored 42 | }) 43 | 44 | me.userEvents.WmClose(func() { 45 | me.hWnd.EndDialog(0) 46 | }) 47 | } 48 | -------------------------------------------------------------------------------- /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(_RegDisablePredefinedCache.Addr()) 17 | return utl.ZeroAsSysError(ret) 18 | } 19 | 20 | var _RegDisablePredefinedCache = dll.Advapi32.NewProc("RegDisablePredefinedCache") 21 | 22 | // [RegDisablePredefinedCacheEx] function. 23 | // 24 | // [RegDisablePredefinedCacheEx]: https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regdisablepredefinedcacheex 25 | func RegDisablePredefinedCacheEx() error { 26 | ret, _, _ := syscall.SyscallN(_RegDisablePredefinedCacheEx.Addr()) 27 | return utl.ZeroAsSysError(ret) 28 | } 29 | 30 | var _RegDisablePredefinedCacheEx = dll.Advapi32.NewProc("RegDisablePredefinedCacheEx") 31 | -------------------------------------------------------------------------------- /win/advapi_structs.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "unsafe" 7 | 8 | "github.com/rodrigocfd/windigo/win/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/co/dwmapi.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package co 4 | 5 | // [DWMWA_GET_CLOAKED] return values. 6 | // 7 | // [DWMWA_GET_CLOAKED]: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute 8 | type DWM_CLOAKED uint32 9 | 10 | const ( 11 | DWM_CLOAKED_APP DWM_CLOAKED = 0x0000_0001 12 | DWM_CLOAKED_SHELL DWM_CLOAKED = 0x0000_0002 13 | DWM_CLOAKED_INHERITED DWM_CLOAKED = 0x0000_0004 14 | ) 15 | 16 | // [DwmSetIconicLivePreviewBitmap] dwSITFlags. 17 | // 18 | // [DwmSetIconicLivePreviewBitmap]: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmseticoniclivepreviewbitmap 19 | type DWM_SIT uint32 20 | 21 | const ( 22 | DWM_SIT_NONE DWM_SIT = 0 23 | DWM_SIT_DISPLAYFRAME DWM_SIT = 0x0000_0001 24 | ) 25 | 26 | // [DWMFLIP3DWINDOWPOLICY] enumeration. 27 | // 28 | // [DWMFLIP3DWINDOWPOLICY]: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmflip3dwindowpolicy 29 | type DWMFLIP3D uint32 30 | 31 | const ( 32 | DWMFLIP3D_DEFAULT = iota 33 | DWMFLIP3D_EXCLUDEBELOW 34 | DWMFLIP3D_EXCLUDEABOVE 35 | ) 36 | 37 | // [DWMNCRENDERINGPOLICY] enumeration. 38 | // 39 | // [DWMNCRENDERINGPOLICY]: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmncrenderingpolicy 40 | type DWMNCRP uint32 41 | 42 | const ( 43 | DWMNCRP_USEWINDOWSTYLE DWMNCRP = iota 44 | DWMNCRP_DISABLED 45 | DWMNCRP_ENABLED 46 | ) 47 | 48 | // [DWM_SYSTEMBACKDROP_TYPE] enumeration. 49 | // 50 | // [DWM_SYSTEMBACKDROP_TYPE]: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_systembackdrop_type 51 | type DWMSBT uint32 52 | 53 | const ( 54 | DWMSBT_AUTO DWMSBT = iota 55 | DWMSBT_NONE 56 | DWMSBT_MAINWINDOW 57 | DWMSBT_TRANSIENTWINDOW 58 | DWMSBT_TABBEDWINDOW 59 | ) 60 | 61 | // [DWMWINDOWATTRIBUTE] enumeration. 62 | // 63 | // [DWMWINDOWATTRIBUTE]: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute 64 | type DWMWA uint32 65 | 66 | const ( 67 | DWMWA_NCRENDERING_ENABLED DWMWA = 1 68 | DWMWA_NCRENDERING_POLICY DWMWA = 2 69 | DWMWA_TRANSITIONS_FORCEDISABLED DWMWA = 3 70 | DWMWA_ALLOW_NCPAINT DWMWA = 4 71 | DWMWA_CAPTION_BUTTON_BOUNDS DWMWA = 5 72 | DWMWA_NONCLIENT_RTL_LAYOUT DWMWA = 6 73 | DWMWA_FORCE_ICONIC_REPRESENTATION DWMWA = 7 74 | DWMWA_FLIP3D_POLICY DWMWA = 8 75 | DWMWA_EXTENDED_FRAME_BOUNDS DWMWA = 9 76 | DWMWA_HAS_ICONIC_BITMAP DWMWA = 10 77 | DWMWA_DISALLOW_PEEK DWMWA = 11 78 | DWMWA_EXCLUDED_FROM_PEEK DWMWA = 12 79 | DWMWA_CLOAK DWMWA = 13 80 | DWMWA_CLOAKED DWMWA = 14 81 | DWMWA_FREEZE_REPRESENTATION DWMWA = 15 82 | DWMWA_PASSIVE_UPDATE_MODE DWMWA = 16 83 | DWMWA_USE_HOSTBACKDROPBRUSH DWMWA = 17 // Windows 11 Build 22000. 84 | DWMWA_USE_IMMERSIVE_DARK_MODE DWMWA = 20 // Windows 11 Build 22000. 85 | DWMWA_WINDOW_CORNER_PREFERENCE DWMWA = 33 // Windows 11 Build 22000. 86 | DWMWA_BORDER_COLOR DWMWA = 34 // Windows 11 Build 22000. 87 | DWMWA_CAPTION_COLOR DWMWA = 35 // Windows 11 Build 22000. 88 | DWMWA_TEXT_COLOR DWMWA = 36 // Windows 11 Build 22000. 89 | DWMWA_VISIBLE_FRAME_BORDER_THICKNESS DWMWA = 37 // Windows 11 Build 22000. 90 | DWMWA_SYSTEMBACKDROP_TYPE DWMWA = 38 // Windows 11 Build 22621. 91 | ) 92 | 93 | // [DWM_WINDOW_CORNER_PREFERENCE] enumeration. 94 | // 95 | // [DWM_WINDOW_CORNER_PREFERENCE]: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_window_corner_preference 96 | type DWMWCP uint32 97 | 98 | const ( 99 | DWMWCP_DEFAULT DWMWCP = iota 100 | DWMWCP_DONOTROUND 101 | DWMWCP_ROUND 102 | DWMWCP_ROUNDSMALL 103 | ) 104 | -------------------------------------------------------------------------------- /win/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 with GWL prefix. 14 | GWLP_HINSTANCE GWLP = -6 // Originally with GWL prefix. 15 | GWLP_HWNDPARENT GWLP = -8 // Originally with GWL prefix. 16 | GWLP_ID GWLP = -12 // Originally with GWL prefix. 17 | GWLP_STYLE GWLP = -16 // Originally with GWL prefix. 18 | GWLP_EXSTYLE GWLP = -20 // Originally with GWL prefix. 19 | GWLP_USERDATA GWLP = -21 // Originally with GWL prefix. 20 | 21 | GWLP_DWLP_MSGRESULT GWLP = 0 // Originally with DWL prefix. 22 | GWLP_DWLP_DLGPROC GWLP = 4 // Originally with DWL prefix. 23 | GWLP_DWLP_USER GWLP = 8 // Originally with DWL prefix. 24 | ) 25 | -------------------------------------------------------------------------------- /win/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 with GWL prefix. 18 | GWLP_EXSTYLE GWLP = -20 // Originally with GWL prefix. 19 | GWLP_USERDATA GWLP = -21 20 | 21 | GWLP_DWLP_MSGRESULT GWLP = 0 // Originally with DWLP prefix. 22 | GWLP_DWLP_DLGPROC = GWLP_DWLP_MSGRESULT + 8 // Originally with DWLP prefix. 23 | GWLP_DWLP_USER = GWLP_DWLP_DLGPROC + 8 // Originally with DWLP prefix. 24 | ) 25 | -------------------------------------------------------------------------------- /win/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/comctl_hwnd.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 | "github.com/rodrigocfd/windigo/win/co" 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(_DefSubclassProc.Addr(), 19 | uintptr(hWnd), 20 | uintptr(msg), 21 | uintptr(wParam), 22 | uintptr(lParam)) 23 | return ret 24 | } 25 | 26 | var _DefSubclassProc = dll.Comctl32.NewProc("DefSubclassProc") 27 | 28 | // [ImageList_DragEnter] function. 29 | // 30 | // [ImageList_DragEnter]: https://learn.microsoft.com/en-us/windows/win32/api/commctrl/nf-commctrl-imagelist_dragenter 31 | func (hWnd HWND) ImageListDragEnter(x, y int) error { 32 | ret, _, _ := syscall.SyscallN(_ImageList_DragEnter.Addr(), 33 | uintptr(hWnd), 34 | uintptr(int32(x)), 35 | uintptr(int32(y))) 36 | return utl.ZeroAsSysInvalidParm(ret) 37 | } 38 | 39 | var _ImageList_DragEnter = dll.Comctl32.NewProc("ImageList_DragEnter") 40 | 41 | // [ImageList_DragLeave] function. 42 | // 43 | // [ImageList_DragLeave]: https://learn.microsoft.com/en-us/windows/win32/api/commctrl/nf-commctrl-imagelist_dragleave 44 | func (hWnd HWND) ImageListDragLeave() error { 45 | ret, _, _ := syscall.SyscallN(_ImageList_DragLeave.Addr(), 46 | uintptr(hWnd)) 47 | return utl.ZeroAsSysInvalidParm(ret) 48 | } 49 | 50 | var _ImageList_DragLeave = dll.Comctl32.NewProc("ImageList_DragLeave") 51 | 52 | // [RemoveWindowSubclass] function. 53 | // 54 | // [RemoveWindowSubclass]: https://learn.microsoft.com/en-us/windows/win32/api/commctrl/nf-commctrl-removewindowsubclass 55 | func (hWnd HWND) RemoveWindowSubclass(subclassProc uintptr, idSubclass uint32) error { 56 | ret, _, _ := syscall.SyscallN(_RemoveWindowSubclass.Addr(), 57 | uintptr(hWnd), 58 | subclassProc, 59 | uintptr(idSubclass)) 60 | return utl.ZeroAsSysInvalidParm(ret) 61 | } 62 | 63 | var _RemoveWindowSubclass = dll.Comctl32.NewProc("RemoveWindowSubclass") 64 | 65 | // [SetWindowSubclass] function. 66 | // 67 | // [SetWindowSubclass]: https://learn.microsoft.com/en-us/windows/win32/api/commctrl/nf-commctrl-setwindowsubclass 68 | func (hWnd HWND) SetWindowSubclass( 69 | subclassProc uintptr, 70 | idSubclass uint32, 71 | refData unsafe.Pointer, 72 | ) error { 73 | ret, _, _ := syscall.SyscallN(_SetWindowSubclass.Addr(), 74 | uintptr(hWnd), 75 | subclassProc, 76 | uintptr(idSubclass), 77 | uintptr(refData)) 78 | return utl.ZeroAsSysInvalidParm(ret) 79 | } 80 | 81 | var _SetWindowSubclass = dll.Comctl32.NewProc("SetWindowSubclass") 82 | -------------------------------------------------------------------------------- /win/comctl_unions.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "github.com/rodrigocfd/windigo/win/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 | var ( 30 | _TdcIconTag_none _TdcIconTag = 0x0 31 | _TdcIconTag_hIcon _TdcIconTag = 0x1 32 | _TdcIconTag_id _TdcIconTag = 0x2 33 | _TdcIconTag_tdIcon _TdcIconTag = 0x3 34 | ) 35 | 36 | // Creates 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 | // Creates 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 | // Creates 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 | // Creates 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 | -------------------------------------------------------------------------------- /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/internal/dll" 10 | "github.com/rodrigocfd/windigo/internal/utl" 11 | "github.com/rodrigocfd/windigo/win/co" 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(_DwmEnableMMCSS.Addr(), 19 | utl.BoolToUintptr(enable)) 20 | return utl.ErrorAsHResult(ret) 21 | } 22 | 23 | var _DwmEnableMMCSS = dll.Dwmapi.NewProc("DwmEnableMMCSS") 24 | 25 | // [DwmFlush] function. 26 | // 27 | // [DwmFlush]: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmflush 28 | func DwmFlush() error { 29 | ret, _, _ := syscall.SyscallN(_DwmFlush.Addr()) 30 | return utl.ErrorAsHResult(ret) 31 | } 32 | 33 | var _DwmFlush = dll.Dwmapi.NewProc("DwmFlush") 34 | 35 | // [DwmGetColorizationColor] function. 36 | // 37 | // [DwmGetColorizationColor]: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmgetcolorizationcolor 38 | func DwmGetColorizationColor() (color COLORREF, isOpaqueBlend bool, hr error) { 39 | var clr COLORREF 40 | var bOpaque int32 // BOOL 41 | 42 | ret, _, _ := syscall.SyscallN(_DwmGetColorizationColor.Addr(), 43 | uintptr(unsafe.Pointer(&clr)), 44 | uintptr(unsafe.Pointer(&bOpaque))) 45 | if hr = co.HRESULT(ret); hr != co.HRESULT_S_OK { 46 | return COLORREF(0), false, hr 47 | } 48 | return clr, bOpaque != 0, nil 49 | } 50 | 51 | var _DwmGetColorizationColor = dll.Dwmapi.NewProc("DwmGetColorizationColor") 52 | 53 | // [DwmIsCompositionEnabled] function. 54 | // 55 | // [DwmIsCompositionEnabled]: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmiscompositionenabled 56 | func DwmIsCompositionEnabled() (bool, error) { 57 | var pfEnabled int32 // BOOL 58 | ret, _, _ := syscall.SyscallN(_DwmIsCompositionEnabled.Addr(), 59 | uintptr(unsafe.Pointer(&pfEnabled))) 60 | if hr := co.HRESULT(ret); hr != co.HRESULT_S_OK { 61 | panic(hr) 62 | } 63 | return pfEnabled != 0, nil 64 | } 65 | 66 | var _DwmIsCompositionEnabled = dll.Dwmapi.NewProc("DwmIsCompositionEnabled") 67 | -------------------------------------------------------------------------------- /win/gdi_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/win/co" 10 | "github.com/rodrigocfd/windigo/win/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) (uint, error) { 17 | name16 := wstr.NewBufWith[wstr.Stack20](name, wstr.ALLOW_EMPTY) 18 | ret, _, _ := syscall.SyscallN(_AddFontResourceExW.Addr(), 19 | uintptr(name16.UnsafePtr()), 20 | uintptr(fl), 21 | 0) 22 | if ret == 0 { 23 | return 0, co.ERROR_INVALID_PARAMETER 24 | } 25 | return uint(ret), nil 26 | } 27 | 28 | var _AddFontResourceExW = dll.Gdi32.NewProc("AddFontResourceExW") 29 | 30 | // [GdiFlush] function. 31 | // 32 | // [GdiFlush]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-gdiflush 33 | func GdiFlush() bool { 34 | ret, _, _ := syscall.SyscallN(_GdiFlush.Addr()) 35 | return ret == 0 36 | } 37 | 38 | var _GdiFlush = dll.Gdi32.NewProc("GdiFlush") 39 | -------------------------------------------------------------------------------- /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/internal/dll" 10 | "github.com/rodrigocfd/windigo/win/co" 11 | ) 12 | 13 | // Handle to a [bitmap]. 14 | // 15 | // [bitmap]: https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#hbitmap 16 | type HBITMAP HGDIOBJ 17 | 18 | // [CreateBitmap] function. 19 | // 20 | // ⚠️ You must defer [HBITMAP.DeleteObject]. 21 | // 22 | // [CreateBitmap]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createbitmap 23 | func CreateBitmap(width, height int, numPlanes, bitCount uint, bits []byte) (HBITMAP, error) { 24 | ret, _, _ := syscall.SyscallN(_CreateBitmap.Addr(), 25 | uintptr(int32(width)), 26 | uintptr(int32(height)), 27 | uintptr(uint32(numPlanes)), 28 | uintptr(uint32(bitCount)), 29 | uintptr(unsafe.Pointer(&bits[0]))) 30 | if ret == 0 { 31 | return HBITMAP(0), co.ERROR_INVALID_PARAMETER 32 | } 33 | return HBITMAP(ret), nil 34 | } 35 | 36 | var _CreateBitmap = dll.Gdi32.NewProc("CreateBitmap") 37 | 38 | // [CreateBitmapIndirect] function. 39 | // 40 | // ⚠️ You must defer [HBITMAP.DeleteObject]. 41 | // 42 | // [CreateBitmapIndirect]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createbitmapindirect 43 | func CreateBitmapIndirect(bm *BITMAP) (HBITMAP, error) { 44 | ret, _, _ := syscall.SyscallN(_CreateBitmapIndirect.Addr(), 45 | uintptr(unsafe.Pointer(bm))) 46 | if ret == 0 { 47 | return HBITMAP(0), co.ERROR_INVALID_PARAMETER 48 | } 49 | return HBITMAP(ret), nil 50 | } 51 | 52 | var _CreateBitmapIndirect = dll.Gdi32.NewProc("CreateBitmapIndirect") 53 | 54 | // [DeleteObject] function. 55 | // 56 | // [DeleteObject]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-deleteobject 57 | func (hBmp HBITMAP) DeleteObject() error { 58 | return HGDIOBJ(hBmp).DeleteObject() 59 | } 60 | 61 | // [GetObject] function. 62 | // 63 | // [GetObject]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-getobject 64 | func (hBmp HBITMAP) GetObject() (BITMAP, error) { 65 | var bmp BITMAP 66 | if err := HGDIOBJ(hBmp).GetObject(unsafe.Sizeof(bmp), unsafe.Pointer(&bmp)); err != nil { 67 | return BITMAP{}, err 68 | } else { 69 | return bmp, nil 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /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/internal/dll" 10 | "github.com/rodrigocfd/windigo/win/co" 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(_CreateBrushIndirect.Addr(), 25 | uintptr(unsafe.Pointer(lb))) 26 | if ret == 0 { 27 | return HBRUSH(0), co.ERROR_INVALID_PARAMETER 28 | } 29 | return HBRUSH(ret), nil 30 | } 31 | 32 | var _CreateBrushIndirect = dll.Gdi32.NewProc("CreateBrushIndirect") 33 | 34 | // [CreatePatternBrush] function. 35 | // 36 | // ⚠️ You must defer [HBRUSH.DeleteObject]. 37 | // 38 | // [CreatePatternBrush]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createpatternbrush 39 | func CreatePatternBrush(hbm HBITMAP) (HBRUSH, error) { 40 | ret, _, _ := syscall.SyscallN(_CreatePatternBrush.Addr(), 41 | uintptr(hbm)) 42 | if ret == 0 { 43 | return HBRUSH(0), co.ERROR_INVALID_PARAMETER 44 | } 45 | return HBRUSH(ret), nil 46 | } 47 | 48 | var _CreatePatternBrush = dll.Gdi32.NewProc("CreatePatternBrush") 49 | 50 | // [DeleteObject] function. 51 | // 52 | // [DeleteObject]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-deleteobject 53 | func (hBrush HBRUSH) DeleteObject() error { 54 | return HGDIOBJ(hBrush).DeleteObject() 55 | } 56 | 57 | // [GetObject] function. 58 | // 59 | // [GetObject]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-getobject 60 | func (hBrush HBRUSH) GetObject() (LOGBRUSH, error) { 61 | var lb LOGBRUSH 62 | if err := HGDIOBJ(hBrush).GetObject(unsafe.Sizeof(lb), unsafe.Pointer(&lb)); err != nil { 63 | return LOGBRUSH{}, err 64 | } else { 65 | return lb, nil 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /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/internal/dll" 10 | "github.com/rodrigocfd/windigo/internal/utl" 11 | "github.com/rodrigocfd/windigo/win/co" 12 | "github.com/rodrigocfd/windigo/win/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 int, 27 | width int, 28 | escapement int, 29 | orientation int, 30 | weight int, 31 | italic bool, 32 | underline bool, 33 | strikeOut bool, 34 | charSet co.CHARSET, 35 | outPrecision co.OUT_PRECIS, 36 | clipPrecision co.CLIP_PRECIS, 37 | quality co.QUALITY, 38 | pitchAndFamily co.FF, 39 | faceName string, 40 | ) (HFONT, error) { 41 | faceName16 := wstr.NewBufWith[wstr.Stack20](faceName, wstr.EMPTY_IS_NIL) 42 | ret, _, _ := syscall.SyscallN(_CreateFontW.Addr(), 43 | uintptr(int32(height)), 44 | uintptr(int32(width)), 45 | uintptr(int32(escapement)), 46 | uintptr(int32(orientation)), 47 | uintptr(int32(height)), 48 | utl.BoolToUintptr(italic), 49 | utl.BoolToUintptr(underline), 50 | utl.BoolToUintptr(strikeOut), 51 | uintptr(charSet), 52 | uintptr(outPrecision), 53 | uintptr(clipPrecision), 54 | uintptr(quality), 55 | uintptr(pitchAndFamily), 56 | uintptr(faceName16.UnsafePtr())) 57 | if ret == 0 { 58 | return HFONT(0), co.ERROR_INVALID_PARAMETER 59 | } 60 | return HFONT(ret), nil 61 | } 62 | 63 | var _CreateFontW = dll.Gdi32.NewProc("CreateFontW") 64 | 65 | // [CreateFontIndirect] function. 66 | // 67 | // ⚠️ You must defer [HFONT.DeleteObject]. 68 | // 69 | // [CreateFontIndirect]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createfontindirectw 70 | func CreateFontIndirect(lf *LOGFONT) (HFONT, error) { 71 | ret, _, _ := syscall.SyscallN(_CreateFontIndirectW.Addr(), 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 = dll.Gdi32.NewProc("CreateFontIndirectW") 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 | -------------------------------------------------------------------------------- /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/internal/dll" 10 | "github.com/rodrigocfd/windigo/internal/utl" 11 | "github.com/rodrigocfd/windigo/win/co" 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(_GetStockObject.Addr(), 29 | uintptr(ty)) 30 | if ret == 0 { 31 | return HGDIOBJ(0), co.ERROR_INVALID_PARAMETER 32 | } 33 | return HGDIOBJ(ret), nil 34 | } 35 | 36 | var _GetStockObject = dll.Gdi32.NewProc("GetStockObject") 37 | 38 | // [DeleteObject] function. 39 | // 40 | // [DeleteObject]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-deleteobject 41 | func (hGdiObj HGDIOBJ) DeleteObject() error { 42 | ret, _, _ := syscall.SyscallN(_DeleteObject.Addr(), 43 | uintptr(hGdiObj)) 44 | return utl.ZeroAsSysInvalidParm(ret) 45 | } 46 | 47 | var _DeleteObject = dll.Gdi32.NewProc("DeleteObject") 48 | 49 | // [GetObject] function. 50 | // 51 | // [GetObject]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-getobject 52 | func (hGdiObj HGDIOBJ) GetObject(szBuf uintptr, buf unsafe.Pointer) error { 53 | ret, _, _ := syscall.SyscallN(_GetObjectW.Addr(), 54 | uintptr(hGdiObj), 55 | uintptr(int32(szBuf)), 56 | uintptr(buf)) 57 | return utl.ZeroAsSysInvalidParm(ret) 58 | } 59 | 60 | var _GetObjectW = dll.Gdi32.NewProc("GetObjectW") 61 | 62 | // [SelectObject] function. 63 | // 64 | // [SelectObject]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-selectobject 65 | func (hdc HDC) SelectObject(hGdiObj HGDIOBJ) (HGDIOBJ, error) { 66 | ret, _, _ := syscall.SyscallN(_SelectObject.Addr(), 67 | uintptr(hdc), 68 | uintptr(hGdiObj)) 69 | if ret == 0 { 70 | return HGDIOBJ(0), co.ERROR_INVALID_PARAMETER 71 | } 72 | return HGDIOBJ(ret), nil 73 | } 74 | 75 | var _SelectObject = dll.Gdi32.NewProc("SelectObject") 76 | -------------------------------------------------------------------------------- /win/gdi_hpalette.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | // Handle to a 6 | // [palette](https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#hpalette). 7 | type HPALETTE HANDLE 8 | 9 | // [DeleteObject] function. 10 | // 11 | // [DeleteObject]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-deleteobject 12 | func (hPal HPALETTE) DeleteObject() error { 13 | return HGDIOBJ(hPal).DeleteObject() 14 | } 15 | -------------------------------------------------------------------------------- /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/internal/dll" 10 | "github.com/rodrigocfd/windigo/win/co" 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 uint, color COLORREF) (HPEN, error) { 24 | ret, _, _ := syscall.SyscallN(_CreatePen.Addr(), 25 | uintptr(style), 26 | uintptr(int32(width)), 27 | uintptr(color)) 28 | if ret == 0 { 29 | return HPEN(0), co.ERROR_INVALID_PARAMETER 30 | } 31 | return HPEN(ret), nil 32 | } 33 | 34 | var _CreatePen = dll.Gdi32.NewProc("CreatePen") 35 | 36 | // [CreatePenIndirect] function. 37 | // 38 | // ⚠️ You must defer [HPEN.DeleteObject]. 39 | // 40 | // [CreatePenIndirect]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createpenindirect 41 | func CreatePenIndirect(lp *LOGPEN) (HPEN, error) { 42 | ret, _, _ := syscall.SyscallN(_CreatePenIndirect.Addr(), 43 | uintptr(unsafe.Pointer(lp))) 44 | if ret == 0 { 45 | return HPEN(0), co.ERROR_INVALID_PARAMETER 46 | } 47 | return HPEN(ret), nil 48 | } 49 | 50 | var _CreatePenIndirect = dll.Gdi32.NewProc("CreatePenIndirect") 51 | 52 | // [ExtCreatePen] function. 53 | // 54 | // ⚠️ You must defer [HPEN.DeleteObject]. 55 | // 56 | // [ExtCreatePen]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-extcreatepen 57 | func ExtCreatePen( 58 | penType co.PS_TYPE, 59 | penStyle co.PS_STYLE, 60 | endCap co.PS_ENDCAP, 61 | width uint, 62 | brush *LOGBRUSH, 63 | styleLengths []uint, 64 | ) (HPEN, error) { 65 | var nLens int 66 | var pLens unsafe.Pointer 67 | if styleLengths != nil { 68 | nLens = len(styleLengths) 69 | pLens = unsafe.Pointer(&styleLengths[0]) 70 | } 71 | 72 | ret, _, _ := syscall.SyscallN(_ExtCreatePen.Addr(), 73 | uintptr(uint32(penType)|uint32(penStyle)|uint32(endCap)), 74 | uintptr(width), 75 | uintptr(unsafe.Pointer(brush)), 76 | uintptr(nLens), 77 | uintptr(pLens)) 78 | if ret == 0 { 79 | return HPEN(0), co.ERROR_INVALID_PARAMETER 80 | } 81 | return HPEN(ret), nil 82 | } 83 | 84 | var _ExtCreatePen = dll.Gdi32.NewProc("ExtCreatePen") 85 | 86 | // [DeleteObject] function. 87 | // 88 | // [DeleteObject]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-deleteobject 89 | func (hPen HPEN) DeleteObject() error { 90 | return HGDIOBJ(hPen).DeleteObject() 91 | } 92 | 93 | // [GetObject] function. 94 | // 95 | // [GetObject]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-getobject 96 | func (hPen HPEN) GetObject() (LOGPEN, error) { 97 | var lp LOGPEN 98 | if err := HGDIOBJ(hPen).GetObject(unsafe.Sizeof(lp), unsafe.Pointer(&lp)); err != nil { 99 | return LOGPEN{}, err 100 | } else { 101 | return lp, nil 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /win/gdi_hrgn.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 | "github.com/rodrigocfd/windigo/win/co" 12 | ) 13 | 14 | // Handle to a [region]. 15 | // 16 | // [region]: https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#hrgn 17 | type HRGN HGDIOBJ 18 | 19 | // [CreateRectRgnIndirect] function. 20 | // 21 | // ⚠️ You must defer [HRGN.DeleteObject]. 22 | // 23 | // [CreateRectRgnIndirect]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createrectrgnindirect 24 | func CreateRectRgnIndirect(bounds RECT) (HRGN, error) { 25 | ret, _, _ := syscall.SyscallN(_CreateRectRgnIndirect.Addr(), 26 | uintptr(unsafe.Pointer(&bounds))) 27 | if ret == 0 { 28 | return HRGN(0), co.ERROR_INVALID_PARAMETER 29 | } 30 | return HRGN(ret), nil 31 | } 32 | 33 | var _CreateRectRgnIndirect = dll.Gdi32.NewProc("CreateRectRgnIndirect") 34 | 35 | // [CombineRgn] function. 36 | // 37 | // [CombineRgn]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-combinergn 38 | func (hRgn HRGN) CombineRgn(src1, src2 HRGN, mode co.RGN) (co.REGION, error) { 39 | ret, _, _ := syscall.SyscallN(_CombineRgn.Addr(), 40 | uintptr(hRgn), 41 | uintptr(src1), 42 | uintptr(src2), 43 | uintptr(mode)) 44 | if ret == 0 { 45 | return co.REGION(0), co.ERROR_INVALID_PARAMETER 46 | } 47 | return co.REGION(ret), nil 48 | } 49 | 50 | var _CombineRgn = dll.Gdi32.NewProc("CombineRgn") 51 | 52 | // [DeleteObject] function. 53 | // 54 | // [DeleteObject]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-deleteobject 55 | func (hRgn HRGN) DeleteObject() error { 56 | return HGDIOBJ(hRgn).DeleteObject() 57 | } 58 | 59 | // [EqualRgn] function. 60 | // 61 | // [EqualRgn]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-equalrgn 62 | func (hRgn HRGN) EqualRgn(other HRGN) bool { 63 | ret, _, _ := syscall.SyscallN(_EqualRgn.Addr(), 64 | uintptr(hRgn), 65 | uintptr(other)) 66 | return ret != 0 67 | } 68 | 69 | var _EqualRgn = dll.Gdi32.NewProc("EqualRgn") 70 | 71 | // [GetRgnBox] function. 72 | // 73 | // [GetRgnBox]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-getrgnbox 74 | func (hRgn HRGN) GetRgnBox() (RECT, co.REGION, error) { 75 | var rc RECT 76 | ret, _, _ := syscall.SyscallN(_GetRgnBox.Addr(), 77 | uintptr(hRgn), 78 | uintptr(unsafe.Pointer(&rc))) 79 | if ret == utl.REGION_ERROR { 80 | return RECT{}, co.REGION(0), co.ERROR_INVALID_PARAMETER 81 | } 82 | return rc, co.REGION(ret), nil 83 | } 84 | 85 | var _GetRgnBox = dll.Gdi32.NewProc("GetRgnBox") 86 | 87 | // [OffsetClipRgn] function. 88 | // 89 | // [OffsetClipRgn]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-offsetcliprgn 90 | func (hRgn HRGN) OffsetClipRgn(x, y int32) (co.REGION, error) { 91 | ret, _, _ := syscall.SyscallN(_OffsetClipRgn.Addr(), 92 | uintptr(hRgn), 93 | uintptr(int32(x)), 94 | uintptr(int32(y))) 95 | if ret == 0 { 96 | return co.REGION(0), co.ERROR_INVALID_PARAMETER 97 | } 98 | return co.REGION(ret), nil 99 | } 100 | 101 | var _OffsetClipRgn = dll.Gdi32.NewProc("OffsetClipRgn") 102 | 103 | // [OffsetRgn] function. 104 | // 105 | // [OffsetRgn]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-offsetrgn 106 | func (hRgn HRGN) OffsetRgn(x, y int32) (co.REGION, error) { 107 | ret, _, _ := syscall.SyscallN(_OffsetRgn.Addr(), 108 | uintptr(hRgn), 109 | uintptr(int32(x)), 110 | uintptr(int32(y))) 111 | if ret == 0 { 112 | return co.REGION(0), co.ERROR_INVALID_PARAMETER 113 | } 114 | return co.REGION(ret), nil 115 | } 116 | 117 | var _OffsetRgn = dll.Gdi32.NewProc("OffsetRgn") 118 | 119 | // [PtInRegion] function. 120 | // 121 | // [PtInRegion]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-ptinregion 122 | func (hRgn HRGN) PtInRegion(x, y int32) bool { 123 | ret, _, _ := syscall.SyscallN(_PtInRegion.Addr(), 124 | uintptr(hRgn), 125 | uintptr(int32(x)), 126 | uintptr(int32(y))) 127 | return ret != 0 128 | } 129 | 130 | var _PtInRegion = dll.Gdi32.NewProc("PtInRegion") 131 | 132 | // [RectInRegion] function. 133 | // 134 | // [RectInRegion]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-rectinregion 135 | func (hRgn HRGN) RectInRegion(rc RECT) bool { 136 | ret, _, _ := syscall.SyscallN(_RectInRegion.Addr(), 137 | uintptr(hRgn), 138 | uintptr(unsafe.Pointer(&rc))) 139 | return ret != 0 140 | } 141 | 142 | var _RectInRegion = dll.Gdi32.NewProc("RectInRegion") 143 | -------------------------------------------------------------------------------- /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/internal/dll" 10 | "github.com/rodrigocfd/windigo/internal/utl" 11 | "github.com/rodrigocfd/windigo/win/co" 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(_VerifyVersionInfoW.Addr(), 22 | uintptr(unsafe.Pointer(ovi)), 23 | uintptr(typeMask), 24 | uintptr(cMaskLo), 25 | uintptr(cMaskHi)) 26 | 27 | if wErr := co.ERROR(err); ret == 0 && wErr == co.ERROR_OLD_WIN_VERSION { 28 | return false, nil 29 | } else if ret == 0 { 30 | return false, wErr // actual error 31 | } else { 32 | return true, nil 33 | } 34 | } 35 | 36 | var _VerifyVersionInfoW = dll.Kernel32.NewProc("VerifyVersionInfoW") 37 | 38 | // [VerSetConditionMask] function. 39 | // 40 | // [VerSetConditionMask]: https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-versetconditionmask 41 | func VerSetConditionMask(conditionMask uint64, typeMask co.VER, condition co.VER_COND) uint64 { 42 | cMaskLo, cMaskHi := utl.Break64(conditionMask) 43 | retLo, retHi, _ := syscall.SyscallN(_VerSetConditionMask.Addr(), 44 | uintptr(cMaskLo), 45 | uintptr(cMaskHi), 46 | uintptr(typeMask), 47 | uintptr(condition)) 48 | return utl.Make64(uint32(retLo), uint32(retHi)) 49 | } 50 | 51 | var _VerSetConditionMask = dll.Kernel32.NewProc("VerSetConditionMask") 52 | -------------------------------------------------------------------------------- /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/internal/dll" 10 | "github.com/rodrigocfd/windigo/win/co" 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(_VerifyVersionInfoW.Addr(), 20 | uintptr(unsafe.Pointer(ovi)), 21 | uintptr(typeMask), 22 | uintptr(conditionMask)) 23 | 24 | if wErr := co.ERROR(err); ret == 0 && wErr == co.ERROR_OLD_WIN_VERSION { 25 | return false, nil 26 | } else if ret == 0 { 27 | return false, wErr // actual error 28 | } else { 29 | return true, nil 30 | } 31 | } 32 | 33 | var _VerifyVersionInfoW = dll.Kernel32.NewProc("VerifyVersionInfoW") 34 | 35 | // [VerSetConditionMask] function. 36 | // 37 | // [VerSetConditionMask]: https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-versetconditionmask 38 | func VerSetConditionMask(conditionMask uint64, typeMask co.VER, condition co.VER_COND) uint64 { 39 | ret, _, _ := syscall.SyscallN(_VerSetConditionMask.Addr(), 40 | uintptr(conditionMask), 41 | uintptr(typeMask), 42 | uintptr(condition)) 43 | return uint64(ret) 44 | } 45 | 46 | var _VerSetConditionMask = dll.Kernel32.NewProc("VerSetConditionMask") 47 | -------------------------------------------------------------------------------- /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 | // A [handle] to an object. This generic handle is used throughout the whole 13 | // API, with different meanings. 14 | // 15 | // [handle]: 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(_CloseHandle.Addr(), 23 | uintptr(h)) 24 | return utl.ZeroAsGetLastError(ret, err) 25 | } 26 | 27 | var _CloseHandle = dll.Kernel32.NewProc("CloseHandle") 28 | -------------------------------------------------------------------------------- /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/internal/dll" 10 | "github.com/rodrigocfd/windigo/win/co" 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(_SetFilePointer.Addr(), 22 | uintptr(hFile), 23 | uintptr(int32(distanceToMove)), 24 | uintptr(unsafe.Pointer(&newOff32)), 25 | uintptr(moveMethod)) 26 | 27 | if wErr = co.ERROR(err); ret == 0 && wErr != co.ERROR_SUCCESS { 28 | return 0, wErr 29 | } else { 30 | return int(newOff32), nil 31 | } 32 | return 33 | } 34 | 35 | var _SetFilePointer = dll.Kernel32.NewProc("SetFilePointer") 36 | -------------------------------------------------------------------------------- /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/internal/dll" 10 | "github.com/rodrigocfd/windigo/win/co" 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(_SetFilePointerEx.Addr(), 22 | uintptr(hFile), 23 | uintptr(int64(distanceToMove)), 24 | uintptr(unsafe.Pointer(&newOff64)), 25 | uintptr(moveMethod)) 26 | 27 | if wErr = co.ERROR(err); ret == 0 && wErr != co.ERROR_SUCCESS { 28 | return 0, wErr 29 | } else { 30 | return int(newOff64), nil 31 | } 32 | return 33 | } 34 | 35 | var _SetFilePointerEx = dll.Kernel32.NewProc("SetFilePointerEx") 36 | -------------------------------------------------------------------------------- /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/internal/dll" 10 | "github.com/rodrigocfd/windigo/internal/utl" 11 | "github.com/rodrigocfd/windigo/win/co" 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 | // ⚠️ You must defer [HFILEMAPVIEW.UnmapViewOfFile]. 34 | // 35 | // [MapViewOfFile]: https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-mapviewoffile 36 | func (hMap HFILEMAP) MapViewOfFile( 37 | desiredAccess co.FILE_MAP, 38 | offset uint64, 39 | numBytesToMap uint, 40 | ) (HFILEMAPVIEW, error) { 41 | si := GetSystemInfo() 42 | if (offset % uint64(si.DwAllocationGranularity)) != 0 { 43 | offset -= offset % uint64(si.DwAllocationGranularity) 44 | } 45 | 46 | ret, _, err := syscall.SyscallN(_MapViewOfFileFromApp.Addr(), 47 | uintptr(hMap), 48 | uintptr(desiredAccess), 49 | uintptr(offset), 50 | uintptr(numBytesToMap)) 51 | if ret == 0 { 52 | return HFILEMAPVIEW(0), co.ERROR(err) 53 | } 54 | return HFILEMAPVIEW(ret), nil 55 | } 56 | 57 | var _MapViewOfFileFromApp = dll.Kernel32.NewProc("MapViewOfFileFromApp") 58 | 59 | // Handle to the memory block of a memory-mapped [file]. Actually, this is the 60 | // starting address of the mapped view. 61 | // 62 | // [file]: https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-mapviewoffile 63 | type HFILEMAPVIEW HANDLE 64 | 65 | // Returns a pointer to the beginning of the mapped memory block. 66 | func (hMem HFILEMAPVIEW) Ptr() *byte { 67 | return (*byte)(unsafe.Pointer(hMem)) 68 | } 69 | 70 | // [FlushViewOfFile] function. 71 | // 72 | // [FlushViewOfFile]: https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-flushviewoffile 73 | func (hMem HFILEMAPVIEW) FlushViewOfFile(numBytes uint) error { 74 | ret, _, err := syscall.SyscallN(_FlushViewOfFile.Addr(), 75 | uintptr(hMem), 76 | uintptr(numBytes)) 77 | return utl.ZeroAsGetLastError(ret, err) 78 | } 79 | 80 | var _FlushViewOfFile = dll.Kernel32.NewProc("FlushViewOfFile") 81 | 82 | // [UnmapViewOfFile] function. 83 | // 84 | // Paired with [HFILEMAP.MapViewOfFile]. 85 | // 86 | // [UnmapViewOfFile]: https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-unmapviewoffile 87 | func (hMem HFILEMAPVIEW) UnmapViewOfFile() error { 88 | ret, _, err := syscall.SyscallN(_UnmapViewOfFile.Addr(), 89 | uintptr(hMem)) 90 | return utl.ZeroAsGetLastError(ret, err) 91 | } 92 | 93 | var _UnmapViewOfFile = dll.Kernel32.NewProc("UnmapViewOfFile") 94 | -------------------------------------------------------------------------------- /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/internal/dll" 10 | "github.com/rodrigocfd/windigo/internal/utl" 11 | "github.com/rodrigocfd/windigo/win/co" 12 | "github.com/rodrigocfd/windigo/win/wstr" 13 | ) 14 | 15 | // A handle returned by [FindFirstFile] function. 16 | // 17 | // [FindFirstFile]: 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 [EnumFiles] or [EnumFilesDeep]. 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 | fileName16 := wstr.NewBufWith[wstr.Stack20](fileName, wstr.EMPTY_IS_NIL) 31 | 32 | ret, _, err := syscall.SyscallN(_FindFirstFileW.Addr(), 33 | uintptr(fileName16.UnsafePtr()), 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 = dll.Kernel32.NewProc("FindFirstFileW") 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(_FindClose.Addr(), 55 | uintptr(hFind)) 56 | return utl.ZeroAsGetLastError(ret, err) 57 | } 58 | 59 | var _FindClose = dll.Kernel32.NewProc("FindClose") 60 | 61 | // [FindNextFile] function. 62 | // 63 | // Returns true if a file was found. 64 | // 65 | // This is a low-level function, prefer using [EnumFiles] or [EnumFilesDeep]. 66 | // 67 | // [FindNextFile]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findnextfilew 68 | func (hFind HFIND) FindNextFile(findFileData *WIN32_FIND_DATA) (bool, error) { 69 | ret, _, err := syscall.SyscallN(_FindNextFileW.Addr(), 70 | uintptr(hFind), 71 | uintptr(unsafe.Pointer(findFileData))) 72 | 73 | if ret == 0 { 74 | if wErr := co.ERROR(err); wErr == co.ERROR_NO_MORE_FILES { 75 | return false, nil // not an error, search ended 76 | } else { 77 | return false, wErr 78 | } 79 | } 80 | return true, nil // a file was found 81 | } 82 | 83 | var _FindNextFileW = dll.Kernel32.NewProc("FindNextFileW") 84 | -------------------------------------------------------------------------------- /win/kernel_hinstance.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/win/co" 11 | "github.com/rodrigocfd/windigo/win/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 | moduleName16 := wstr.NewBufWith[wstr.Stack20](moduleName, wstr.EMPTY_IS_NIL) 30 | ret, _, err := syscall.SyscallN(_GetModuleHandleW.Addr(), 31 | uintptr(moduleName16.UnsafePtr())) 32 | if ret == 0 { 33 | return HINSTANCE(0), co.ERROR(err) 34 | } 35 | return HINSTANCE(ret), nil 36 | } 37 | 38 | var _GetModuleHandleW = dll.Kernel32.NewProc("GetModuleHandleW") 39 | 40 | // [LoadLibrary] function. 41 | // 42 | // ⚠️ You must defer [HINSTANCE.FreeLibrary]. 43 | // 44 | // [LoadLibrary]: https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryw 45 | func LoadLibrary(libFileName string) (HINSTANCE, error) { 46 | libFileName16 := wstr.NewBufWith[wstr.Stack20](libFileName, wstr.EMPTY_IS_NIL) 47 | ret, _, err := syscall.SyscallN(_LoadLibraryW.Addr(), 48 | uintptr(libFileName16.UnsafePtr())) 49 | if ret == 0 { 50 | return HINSTANCE(0), co.ERROR(err) 51 | } 52 | return HINSTANCE(ret), nil 53 | } 54 | 55 | var _LoadLibraryW = dll.Kernel32.NewProc("LoadLibraryW") 56 | 57 | // [FreeLibrary] function. 58 | // 59 | // Paired with [LoadLibrary]. 60 | // 61 | // [FreeLibrary]: https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-freelibrary 62 | func (hInst HINSTANCE) FreeLibrary() error { 63 | ret, _, err := syscall.SyscallN(_FreeLibrary.Addr(), 64 | uintptr(hInst)) 65 | return utl.ZeroAsGetLastError(ret, err) 66 | } 67 | 68 | var _FreeLibrary = dll.Kernel32.NewProc("FreeLibrary") 69 | 70 | // [GetModuleFileName] function. 71 | // 72 | // # Example 73 | // 74 | // Retrieving own .exe path: 75 | // 76 | // exePath, _ := win.HINSTANCE(0).GetModuleFileName() 77 | // println(exePath) 78 | // 79 | // [GetModuleFileName]: https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulefilenamew 80 | func (hInst HINSTANCE) GetModuleFileName() (string, error) { 81 | buf := wstr.NewBufSized[wstr.Stack260](260) // start allocating on the stack 82 | 83 | for { 84 | ret, _, err := syscall.SyscallN(_GetModuleFileNameW.Addr(), 85 | uintptr(hInst), 86 | uintptr(buf.UnsafePtr()), 87 | uintptr(uint32(buf.Len()))) 88 | if ret == 0 { 89 | return "", co.ERROR(err) 90 | } 91 | chCopied := uint(ret) + 1 // plus terminating null count 92 | 93 | if chCopied < buf.Len() { // to break, must have at least 1 char gap 94 | return wstr.WstrSliceToStr(buf.HotSlice()), nil 95 | } 96 | 97 | buf.Resize(buf.Len() + 64) // increase buffer size to try again 98 | } 99 | } 100 | 101 | var _GetModuleFileNameW = dll.Kernel32.NewProc("GetModuleFileNameW") 102 | -------------------------------------------------------------------------------- /win/ole/IBindCtx.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ole 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/internal/utl" 10 | "github.com/rodrigocfd/windigo/win/co" 11 | ) 12 | 13 | // [IBindCtx] COM interface. 14 | // 15 | // Implements [ComObj] and [ComResource]. 16 | // 17 | // # Example 18 | // 19 | // rel := ole.NewReleaser() 20 | // defer rel.Release() 21 | // 22 | // bindCtx, _ := ole.CreateBindCtx(rel) 23 | // 24 | // [IBindCtx]: https://learn.microsoft.com/en-us/windows/win32/api/objidl/nn-objidl-ibindctx 25 | type IBindCtx struct{ IUnknown } 26 | 27 | // Returns the unique COM [interface ID]. 28 | // 29 | // [interface ID]: https://learn.microsoft.com/en-us/office/client-developer/outlook/mapi/iid 30 | func (*IBindCtx) IID() co.IID { 31 | return co.IID_IBindCtx 32 | } 33 | 34 | // [EnumObjectParam] method. 35 | // 36 | // [EnumObjectParam]: https://learn.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-ibindctx-enumobjectparam 37 | func (me *IBindCtx) EnumObjectParam(releaser *Releaser) (*IEnumString, error) { 38 | var ppvtQueried **IUnknownVt 39 | ret, _, _ := syscall.SyscallN( 40 | (*_IBindCtxVt)(unsafe.Pointer(*me.Ppvt())).EnumObjectParam, 41 | uintptr(unsafe.Pointer(me.Ppvt())), 42 | uintptr(unsafe.Pointer(&ppvtQueried))) 43 | 44 | if hr := co.HRESULT(ret); hr == co.HRESULT_S_OK { 45 | var pObj *IEnumString 46 | utl.ComCreateObj(&pObj, unsafe.Pointer(ppvtQueried)) 47 | releaser.Add(pObj) 48 | return pObj, nil 49 | } else { 50 | return nil, hr 51 | } 52 | } 53 | 54 | // [RegisterObjectBound] method. 55 | // 56 | // [RegisterObjectBound]: https://learn.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-ibindctx-registerobjectbound 57 | func (me *IBindCtx) RegisterObjectBound(obj *IUnknown) error { 58 | ret, _, _ := syscall.SyscallN( 59 | (*_IBindCtxVt)(unsafe.Pointer(*me.Ppvt())).RegisterObjectBound, 60 | uintptr(unsafe.Pointer(me.Ppvt())), 61 | uintptr(unsafe.Pointer(obj.Ppvt()))) 62 | return utl.ErrorAsHResult(ret) 63 | } 64 | 65 | // [ReleaseBoundObjects] method. 66 | // 67 | // [ReleaseBoundObjects]: https://learn.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-ibindctx-releaseboundobjects 68 | func (me *IBindCtx) ReleaseBoundObjects() error { 69 | ret, _, _ := syscall.SyscallN( 70 | (*_IBindCtxVt)(unsafe.Pointer(*me.Ppvt())).ReleaseBoundObjects, 71 | uintptr(unsafe.Pointer(me.Ppvt()))) 72 | return utl.ErrorAsHResult(ret) 73 | } 74 | 75 | // [RevokeObjectBound] method. 76 | // 77 | // [RevokeObjectBound]: https://learn.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-ibindctx-revokeobjectbound 78 | func (me *IBindCtx) RevokeObjectBound(obj *IUnknown) error { 79 | ret, _, _ := syscall.SyscallN( 80 | (*_IBindCtxVt)(unsafe.Pointer(*me.Ppvt())).RevokeObjectBound, 81 | uintptr(unsafe.Pointer(me.Ppvt())), 82 | uintptr(unsafe.Pointer(obj.Ppvt()))) 83 | return utl.ErrorAsHResult(ret) 84 | } 85 | 86 | type _IBindCtxVt struct { 87 | IUnknownVt 88 | RegisterObjectBound uintptr 89 | RevokeObjectBound uintptr 90 | ReleaseBoundObjects uintptr 91 | SetBindOptions uintptr 92 | GetBindOptions uintptr 93 | GetRunningObjectTable uintptr 94 | RegisterObjectParam uintptr 95 | GetObjectParam uintptr 96 | EnumObjectParam uintptr 97 | RevokeObjectParam uintptr 98 | } 99 | -------------------------------------------------------------------------------- /win/ole/IDataObject.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ole 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/internal/utl" 10 | "github.com/rodrigocfd/windigo/win/co" 11 | ) 12 | 13 | // [IDataObject] COM interface. 14 | // 15 | // Implements [ComObj] and [ComResource]. 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 | -------------------------------------------------------------------------------- /win/ole/IEnumString.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ole 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/internal/utl" 10 | "github.com/rodrigocfd/windigo/win/co" 11 | "github.com/rodrigocfd/windigo/win/wstr" 12 | ) 13 | 14 | // [IEnumString] COM interface. 15 | // 16 | // Implements [ComObj] and [ComResource]. 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 *Releaser) (*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 | var pObj *IEnumString 40 | utl.ComCreateObj(&pObj, unsafe.Pointer(ppvtQueried)) 41 | releaser.Add(pObj) 42 | return pObj, nil 43 | } else { 44 | return nil, hr 45 | } 46 | } 47 | 48 | // Returns all objects by calling [IEnumString.Next]. 49 | func (me *IEnumString) Enum() ([]string, error) { 50 | strs := make([]string, 0) 51 | var s string 52 | var hr error 53 | 54 | for { 55 | s, hr = me.Next() 56 | if hr != nil { // actual error 57 | return nil, hr 58 | } else if s == "" { // no more items to fetch 59 | return strs, nil 60 | } else { // item fetched 61 | strs = append(strs, s) 62 | } 63 | } 64 | } 65 | 66 | // [Next] method. 67 | // 68 | // [Next]: https://learn.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-ienumstring-next 69 | func (me *IEnumString) Next() (string, error) { 70 | var pv uintptr 71 | var numFetched uint32 72 | 73 | ret, _, _ := syscall.SyscallN( 74 | (*_IEnumStringVt)(unsafe.Pointer(*me.Ppvt())).Next, 75 | uintptr(unsafe.Pointer(me.Ppvt())), 76 | 1, 77 | uintptr(unsafe.Pointer(&pv)), 78 | uintptr(unsafe.Pointer(&numFetched))) 79 | 80 | if hr := co.HRESULT(ret); hr == co.HRESULT_S_OK { 81 | defer HTASKMEM(pv).CoTaskMemFree() 82 | name := wstr.WstrPtrToStr((*uint16)(unsafe.Pointer(pv))) 83 | return name, nil 84 | } else if hr == co.HRESULT_S_FALSE { 85 | return "", nil 86 | } else { 87 | return "", hr 88 | } 89 | } 90 | 91 | // [Reset] method. 92 | // 93 | // [Reset]: https://learn.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-ienumstring-reset 94 | func (me *IEnumString) Reset() error { 95 | ret, _, _ := syscall.SyscallN( 96 | (*_IEnumStringVt)(unsafe.Pointer(*me.Ppvt())).Reset, 97 | uintptr(unsafe.Pointer(me.Ppvt()))) 98 | return utl.ErrorAsHResult(ret) 99 | } 100 | 101 | // [Skip] method. 102 | // 103 | // [Skip]: https://learn.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-ienumstring-skip 104 | func (me *IEnumString) Skip(count uint) error { 105 | ret, _, _ := syscall.SyscallN( 106 | (*_IEnumStringVt)(unsafe.Pointer(*me.Ppvt())).Skip, 107 | uintptr(unsafe.Pointer(me.Ppvt())), 108 | uintptr(uint32(count))) 109 | return utl.ErrorAsHResult(ret) 110 | } 111 | 112 | type _IEnumStringVt struct { 113 | IUnknownVt 114 | Next uintptr 115 | Skip uintptr 116 | Reset uintptr 117 | Clone uintptr 118 | } 119 | -------------------------------------------------------------------------------- /win/ole/ISequentialStream.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ole 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/win/co" 10 | ) 11 | 12 | // [ISequentialStream] COM interface. 13 | // 14 | // Implements [ComObj] and [ComResource]. 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 uint32, hr error) { 33 | ret, _, _ := syscall.SyscallN( 34 | (*_ISequentialStreamVt)(unsafe.Pointer(*me.Ppvt())).Read, 35 | uintptr(unsafe.Pointer(me.Ppvt())), 36 | uintptr(unsafe.Pointer(&buffer[0])), 37 | uintptr(uint32(len(buffer))), 38 | uintptr(unsafe.Pointer(&numBytesRead))) 39 | 40 | if hr = co.HRESULT(ret); hr == co.HRESULT_S_OK { 41 | hr = nil 42 | } else { 43 | numBytesRead = 0 44 | } 45 | return 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 uint32, hr error) { 52 | ret, _, _ := syscall.SyscallN( 53 | (*_ISequentialStreamVt)(unsafe.Pointer(*me.Ppvt())).Write, 54 | uintptr(unsafe.Pointer(me.Ppvt())), 55 | uintptr(unsafe.Pointer(&data[0])), 56 | uintptr(uint32(len(data))), 57 | uintptr(unsafe.Pointer(&numBytesWritten))) 58 | 59 | if hr = co.HRESULT(ret); hr == co.HRESULT_S_OK { 60 | hr = nil 61 | } else { 62 | numBytesWritten = 0 63 | } 64 | return 65 | } 66 | 67 | type _ISequentialStreamVt struct { 68 | IUnknownVt 69 | Read uintptr 70 | Write uintptr 71 | } 72 | -------------------------------------------------------------------------------- /win/ole/Releaser.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ole 4 | 5 | import ( 6 | "github.com/rodrigocfd/windigo/internal/utl" 7 | ) 8 | 9 | // A [COM] object whose lifetime can be managed by an ole.Releaser, automating the 10 | // cleanup. 11 | // 12 | // [COM]: https://learn.microsoft.com/en-us/windows/win32/com/component-object-model--com--portal 13 | type ComResource interface { 14 | // Frees the resources of the object immediately. 15 | // 16 | // You usually don't need to call this method directly, since every function 17 | // which returns a [COM] object will require a Releaser to manage the 18 | // object's lifetime. 19 | // 20 | // [COM]: https://learn.microsoft.com/en-us/windows/win32/com/component-object-model--com--portal 21 | Release() 22 | } 23 | 24 | // Stores multiple [COM] resources, releasing all them at once. 25 | // 26 | // Every function which returns a COM resource will require a Releaser to manage 27 | // the object's lifetime. 28 | // 29 | // # Example 30 | // 31 | // rel := ole.NewReleaser() 32 | // defer rel.Release() 33 | // 34 | // [COM]: https://learn.microsoft.com/en-us/windows/win32/com/component-object-model--com--portal 35 | type Releaser struct { 36 | objs []ComResource 37 | } 38 | 39 | // Creates a new [Releaser] to store multiple [COM] resources, releasing them 40 | // all at once. 41 | // 42 | // Every function which returns a COM resource will require a Releaser to manage 43 | // the object's lifetime. 44 | // 45 | // ⚠️ You must defer Releaser.Release(). 46 | // 47 | // # Example 48 | // 49 | // rel := ole.NewReleaser() 50 | // defer rel.Release() 51 | // 52 | // [COM]: https://learn.microsoft.com/en-us/windows/win32/com/component-object-model--com--portal 53 | func NewReleaser() *Releaser { 54 | return new(Releaser) 55 | } 56 | 57 | // Adds a new [COM] resource to have its lifetime managed by the Releaser. 58 | func (me *Releaser) Add(objs ...ComResource) { 59 | me.objs = append(me.objs, objs...) 60 | } 61 | 62 | // Releases all added [COM] resource, in the reverse order they were added. 63 | // 64 | // # Example 65 | // 66 | // rel := ole.NewReleaser() 67 | // defer rel.Release() 68 | // 69 | // [COM]: https://learn.microsoft.com/en-us/windows/win32/com/component-object-model--com--portal 70 | func (me *Releaser) Release() { 71 | for i := len(me.objs) - 1; i >= 0; i-- { 72 | me.objs[i].Release() 73 | } 74 | me.objs = nil 75 | } 76 | 77 | // Releases the specific [COM] resources, if present, immediately. 78 | // 79 | // These objects will be removed from the internal list, thus not being released 80 | // when [Releaser.Release] is further called. 81 | func (me *Releaser) ReleaseNow(objs ...ComResource) { 82 | numToRelease := 0 83 | for _, passedObj := range objs { 84 | if !utl.IsNil(passedObj) { // obj passed by the user is not nil 85 | for _, ourObj := range me.objs { 86 | if ourObj == passedObj { // we found this object in our list 87 | numToRelease++ 88 | } 89 | } 90 | } 91 | } 92 | 93 | if numToRelease == 0 { 94 | return // no objects to be released 95 | } 96 | 97 | newSlice := make([]ComResource, 0, len(me.objs)-numToRelease) 98 | for _, ourObj := range me.objs { 99 | for _, passedObj := range objs { 100 | if passedObj == ourObj { 101 | ourObj.Release() 102 | } else { 103 | newSlice = append(newSlice, ourObj) 104 | } 105 | } 106 | } 107 | me.objs = newSlice 108 | } 109 | -------------------------------------------------------------------------------- /win/ole/htaskmem.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ole 4 | 5 | import ( 6 | "syscall" 7 | 8 | "github.com/rodrigocfd/windigo/internal/dll" 9 | "github.com/rodrigocfd/windigo/win" 10 | "github.com/rodrigocfd/windigo/win/co" 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 win.HANDLE 17 | 18 | // [CoTaskMemAlloc] function. 19 | // 20 | // ⚠️ You must defer [HTASKMEM.CoTaskMemFree]. 21 | // 22 | // # Example 23 | // 24 | // hMem, _ := ole.CoTaskMemAlloc(uint(unsafe.Sizeof(win.MSG{}))) 25 | // defer hMem.CoTaskMemFree() 26 | // 27 | // pMsg := (*win.MSG)(unsafe.Pointer(hMem)) 28 | // 29 | // [CoTaskMemAlloc]: https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cotaskmemalloc 30 | func CoTaskMemAlloc(numBytes uint) (HTASKMEM, error) { 31 | ret, _, _ := syscall.SyscallN(_CoTaskMemAlloc.Addr(), 32 | uintptr(numBytes)) 33 | if ret == 0 { 34 | return HTASKMEM(0), co.HRESULT_E_OUTOFMEMORY 35 | } 36 | return HTASKMEM(ret), nil 37 | } 38 | 39 | var _CoTaskMemAlloc = dll.Ole32.NewProc("CoTaskMemAlloc") 40 | 41 | // [CoTaskMemFree] function. 42 | // 43 | // This method is safe to be called if hMem is zero. 44 | // 45 | // Paired with [CoTaskMemAlloc] and [HTASKMEM.CoTaskMemRealloc]. 46 | // 47 | // [CoTaskMemFree]: https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cotaskmemfree 48 | func (hMem HTASKMEM) CoTaskMemFree() { 49 | if hMem != 0 { 50 | syscall.SyscallN(_CoTaskMemFree.Addr(), 51 | uintptr(hMem)) 52 | } 53 | } 54 | 55 | var _CoTaskMemFree = dll.Ole32.NewProc("CoTaskMemFree") 56 | 57 | // [CoTaskMemRealloc] function. 58 | // 59 | // ⚠️ You must defer [HTASKMEM.CoTaskMemFree]. 60 | // 61 | // [CoTaskMemRealloc]: https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cotaskmemrealloc 62 | func (hMem HTASKMEM) CoTaskMemRealloc(numBytes uint) (HTASKMEM, error) { 63 | ret, _, _ := syscall.SyscallN(_CoTaskMemRealloc.Addr(), 64 | uintptr(hMem), 65 | uintptr(numBytes)) 66 | if ret == 0 { 67 | return HTASKMEM(0), co.HRESULT_E_OUTOFMEMORY 68 | } 69 | return HTASKMEM(ret), nil 70 | } 71 | 72 | var _CoTaskMemRealloc = dll.Ole32.NewProc("CoTaskMemRealloc") 73 | -------------------------------------------------------------------------------- /win/ole/oleaut/ITypeLib.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package oleaut 4 | 5 | import ( 6 | "github.com/rodrigocfd/windigo/win/co" 7 | "github.com/rodrigocfd/windigo/win/ole" 8 | ) 9 | 10 | // [ITypeLib] COM interface. 11 | // 12 | // Implements [ole.ComObj] and [ole.ComResource]. 13 | // 14 | // [ITypeLib]: https://learn.microsoft.com/en-us/windows/win32/api/oaidl/nn-oaidl-itypelib 15 | type ITypeLib struct{ ole.IUnknown } 16 | 17 | // Returns the unique COM [interface ID]. 18 | // 19 | // [interface ID]: https://learn.microsoft.com/en-us/office/client-developer/outlook/mapi/iid 20 | func (*ITypeLib) IID() co.IID { 21 | return co.IID_ITypeLib 22 | } 23 | 24 | type _ITypeLibVt struct { 25 | GetTypeInfoCount uintptr 26 | GetTypeInfo uintptr 27 | GetTypeInfoType uintptr 28 | GetTypeInfoOfGuid uintptr 29 | GetLibAttr uintptr 30 | GetTypeComp uintptr 31 | GetDocumentation uintptr 32 | IsName uintptr 33 | FindName uintptr 34 | ReleaseTLibAttr uintptr 35 | } 36 | -------------------------------------------------------------------------------- /win/ole/oleaut/bstr.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package oleaut 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/internal/dll" 10 | "github.com/rodrigocfd/windigo/win/co" 11 | "github.com/rodrigocfd/windigo/win/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 := ole.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 | s16 := wstr.NewBufWith[wstr.Stack20](s, wstr.ALLOW_EMPTY) 31 | ret, _, _ := syscall.SyscallN(_SysAllocString.Addr(), 32 | uintptr(s16.UnsafePtr())) 33 | if ret == 0 { 34 | return BSTR(0), co.HRESULT_E_OUTOFMEMORY 35 | } 36 | return BSTR(ret), nil 37 | } 38 | 39 | var _SysAllocString = dll.Oleaut32.NewProc("SysAllocString") 40 | 41 | // [SysFreeString] function. 42 | // 43 | // [SysFreeString]: https://learn.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-sysfreestring 44 | func (bstr BSTR) SysFreeString() { 45 | if bstr != 0 { 46 | syscall.SyscallN(_SysFreeString.Addr(), 47 | uintptr(bstr)) 48 | } 49 | } 50 | 51 | var _SysFreeString = dll.Oleaut32.NewProc("SysFreeString") 52 | 53 | // [SysReAllocString] function. 54 | // 55 | // # Example 56 | // 57 | // bstr := ole.SysAllocString("hello") 58 | // defer bstr.SysFreeString() 59 | // 60 | // bstr = bstr.SysReAllocString("another") 61 | // 62 | // [SysReAllocString]: https://learn.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-sysreallocstring 63 | func (bstr BSTR) SysReAllocString(s string) (BSTR, error) { 64 | s16 := wstr.NewBufWith[wstr.Stack20](s, wstr.ALLOW_EMPTY) 65 | ret, _, _ := syscall.SyscallN(_SysReAllocString.Addr(), 66 | uintptr(bstr), 67 | uintptr(s16.UnsafePtr())) 68 | if ret == 0 { 69 | return BSTR(0), co.HRESULT_E_OUTOFMEMORY 70 | } 71 | return BSTR(ret), nil 72 | } 73 | 74 | var _SysReAllocString = dll.Oleaut32.NewProc("SysReAllocString") 75 | 76 | // Converts the BSTR pointer to a string. 77 | func (bstr BSTR) String() string { 78 | return wstr.WstrPtrToStr((*uint16)(unsafe.Pointer(bstr))) 79 | } 80 | -------------------------------------------------------------------------------- /win/ole/oleaut/funcs.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package oleaut 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/internal/dll" 10 | "github.com/rodrigocfd/windigo/internal/utl" 11 | "github.com/rodrigocfd/windigo/win" 12 | "github.com/rodrigocfd/windigo/win/co" 13 | "github.com/rodrigocfd/windigo/win/ole" 14 | "github.com/rodrigocfd/windigo/win/wstr" 15 | ) 16 | 17 | // [OleLoadPicture] function. 18 | // 19 | // Pass size = 0 to read all the bytes from the stream. 20 | // 21 | // The bytes are copied, so [ole.IStream] can be released after this function 22 | // returns. 23 | // 24 | // # Example 25 | // 26 | // rel := ole.NewReleaser() 27 | // defer rel.Release() 28 | // 29 | // data := []byte{0x10, 0x11, 0x12} 30 | // defer runtime.KeepAlive(data) 31 | // 32 | // stream, _ := ole.SHCreateMemStream(rel, data) 33 | // pic, _ := oleaut.OleLoadPicture(rel, &stream, 0, true) 34 | // 35 | // [OleLoadPicture]: https://learn.microsoft.com/en-us/windows/win32/api/olectl/nf-olectl-oleloadpicture 36 | func OleLoadPicture( 37 | releaser *ole.Releaser, 38 | stream *ole.IStream, 39 | size uint, 40 | keepOriginalFormat bool, 41 | ) (*IPicture, error) { 42 | var ppvtQueried **ole.IUnknownVt 43 | guid := win.GuidFrom(co.IID_IPicture) 44 | 45 | ret, _, _ := syscall.SyscallN(_OleLoadPicture.Addr(), 46 | uintptr(unsafe.Pointer(stream.Ppvt())), 47 | uintptr(int32(size)), 48 | utl.BoolToUintptr(!keepOriginalFormat), // note: reversed 49 | uintptr(unsafe.Pointer(&guid)), 50 | uintptr(unsafe.Pointer(&ppvtQueried))) 51 | 52 | if hr := co.HRESULT(ret); hr == co.HRESULT_S_OK { 53 | var pObj *IPicture 54 | utl.ComCreateObj(&pObj, unsafe.Pointer(ppvtQueried)) 55 | releaser.Add(pObj) 56 | return pObj, nil 57 | } else { 58 | return nil, hr 59 | } 60 | } 61 | 62 | var _OleLoadPicture = dll.Oleaut32.NewProc("OleLoadPicture") 63 | 64 | // [OleLoadPicturePath] function. 65 | // 66 | // The picture must be in the following formats: 67 | // - BMP (bitmap) 68 | // - JPEG 69 | // - WMF (metafile) 70 | // - ICO (icon) 71 | // - GIF 72 | // 73 | // [OleLoadPicturePath]: https://learn.microsoft.com/en-us/windows/win32/api/olectl/nf-olectl-oleloadpicturepath 74 | func OleLoadPicturePath( 75 | releaser *ole.Releaser, 76 | path string, 77 | transparentColor win.COLORREF, 78 | ) (*IPicture, error) { 79 | path16 := wstr.NewBufWith[wstr.Stack20](path, wstr.EMPTY_IS_NIL) 80 | var ppvtQueried **ole.IUnknownVt 81 | guid := win.GuidFrom(co.IID_IPicture) 82 | 83 | ret, _, _ := syscall.SyscallN(_OleLoadPicturePath.Addr(), 84 | uintptr(path16.UnsafePtr()), 85 | 0, 0, 86 | uintptr(transparentColor), 87 | uintptr(unsafe.Pointer(&guid)), 88 | uintptr(unsafe.Pointer(&ppvtQueried))) 89 | 90 | if hr := co.HRESULT(ret); hr == co.HRESULT_S_OK { 91 | var pObj *IPicture 92 | utl.ComCreateObj(&pObj, unsafe.Pointer(ppvtQueried)) 93 | releaser.Add(pObj) 94 | return pObj, nil 95 | } else { 96 | return nil, hr 97 | } 98 | } 99 | 100 | var _OleLoadPicturePath = dll.Oleaut32.NewProc("OleLoadPicturePath") 101 | -------------------------------------------------------------------------------- /win/ole/shell/IFileOpenDialog.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package shell 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/internal/utl" 10 | "github.com/rodrigocfd/windigo/win/co" 11 | "github.com/rodrigocfd/windigo/win/ole" 12 | ) 13 | 14 | // [IFileOpenDialog] COM interface. 15 | // 16 | // Implements [ole.ComObj] and [ole.ComResource]. 17 | // 18 | // # Example 19 | // 20 | // var hWnd win.HWND // initialized somewhere 21 | // 22 | // ole.CoInitializeEx( 23 | // co.COINIT_APARTMENTTHREADED | co.COINIT_DISABLE_OLE1DDE) 24 | // defer ole.CoUninitialize() 25 | // 26 | // rel := ole.NewReleaser() 27 | // defer rel.Release() 28 | // 29 | // var fod *shell.IFileOpenDialog 30 | // ole.CoCreateInstance( 31 | // rel, 32 | // co.CLSID_FileOpenDialog, 33 | // nil, 34 | // co.CLSCTX_INPROC_SERVER, 35 | // &fod, 36 | // ) 37 | // 38 | // defOpts, _ := fod.GetOptions() 39 | // fod.SetOptions(defOpts | 40 | // co.FOS_FORCEFILESYSTEM | 41 | // co.FOS_FILEMUSTEXIST, 42 | // ) 43 | // 44 | // fod.SetFileTypes([]shell.COMDLG_FILTERSPEC{ 45 | // {Name: "Text files", Spec: "*.txt"}, 46 | // {Name: "All files", Spec: "*.*"}, 47 | // }) 48 | // fod.SetFileTypeIndex(1) 49 | // 50 | // if ok, _ := fod.Show(hWnd); ok { 51 | // item, _ := fod.GetResult(rel) 52 | // fileName, _ := item.GetDisplayName(co.SIGDN_FILESYSPATH) 53 | // println(fileName) 54 | // } 55 | // 56 | // [IFileOpenDialog]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-ifileopendialog 57 | type IFileOpenDialog struct{ IFileDialog } 58 | 59 | // Returns the unique COM [interface ID]. 60 | // 61 | // [interface ID]: https://learn.microsoft.com/en-us/office/client-developer/outlook/mapi/iid 62 | func (*IFileOpenDialog) IID() co.IID { 63 | return co.IID_IFileOpenDialog 64 | } 65 | 66 | // [GetResults] method. 67 | // 68 | // Returns the selected items after user confirmation, for multi-selection 69 | // dialogs – those with [co.FOS_ALLOWMULTISELECT] option. 70 | // 71 | // For single-selection dialogs, use [IFileDialog.GetResult]. 72 | // 73 | // [GetResults]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ifileopendialog-getresults 74 | func (me *IFileOpenDialog) GetResults(releaser *ole.Releaser) (*IShellItemArray, error) { 75 | var ppvtQueried **ole.IUnknownVt 76 | ret, _, _ := syscall.SyscallN( 77 | (*_IFileOpenDialogVt)(unsafe.Pointer(*me.Ppvt())).GetResults, 78 | uintptr(unsafe.Pointer(me.Ppvt())), 79 | uintptr(unsafe.Pointer(&ppvtQueried))) 80 | 81 | if hr := co.HRESULT(ret); hr == co.HRESULT_S_OK { 82 | var pObj *IShellItemArray 83 | utl.ComCreateObj(&pObj, unsafe.Pointer(ppvtQueried)) 84 | releaser.Add(pObj) 85 | return pObj, nil 86 | } else { 87 | return nil, hr 88 | } 89 | } 90 | 91 | // [GetSelectedItems] method. 92 | // 93 | // [GetSelectedItems]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ifileopendialog-getselecteditems 94 | func (me *IFileOpenDialog) GetSelectedItems(releaser *ole.Releaser) (*IShellItemArray, error) { 95 | var ppvtQueried **ole.IUnknownVt 96 | ret, _, _ := syscall.SyscallN( 97 | (*_IFileOpenDialogVt)(unsafe.Pointer(*me.Ppvt())).GetSelectedItems, 98 | uintptr(unsafe.Pointer(me.Ppvt())), 99 | uintptr(unsafe.Pointer(&ppvtQueried))) 100 | 101 | if hr := co.HRESULT(ret); hr == co.HRESULT_S_OK { 102 | var pObj *IShellItemArray 103 | utl.ComCreateObj(&pObj, unsafe.Pointer(ppvtQueried)) 104 | releaser.Add(pObj) 105 | return pObj, nil 106 | } else { 107 | return nil, hr 108 | } 109 | } 110 | 111 | type _IFileOpenDialogVt struct { 112 | _IFileDialogVt 113 | GetResults uintptr 114 | GetSelectedItems uintptr 115 | } 116 | -------------------------------------------------------------------------------- /win/ole/shell/IFileSaveDialog.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package shell 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/internal/utl" 10 | "github.com/rodrigocfd/windigo/win/co" 11 | ) 12 | 13 | // [IFileSaveDialog] COM interface. 14 | // 15 | // Implements [ole.ComObj] and [ole.ComResource]. 16 | // 17 | // # Example 18 | // 19 | // var hWnd win.HWND // initialized somewhere 20 | // 21 | // rel := ole.NewReleaser() 22 | // defer rel.Release() 23 | // 24 | // var fsd *shell.IFileSaveDialog 25 | // ole.CoCreateInstance( 26 | // rel, 27 | // co.CLSID_FileSaveDialog, 28 | // nil, 29 | // co.CLSCTX_INPROC_SERVER, 30 | // &fsd, 31 | // ) 32 | // 33 | // fsd.SetFileTypes([]shell.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 | // [SetSaveAsItem] method. 58 | // 59 | // [SetSaveAsItem]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ifilesavedialog-setsaveasitem 60 | func (me *IFileSaveDialog) SetSaveAsItem(si *IShellItem) error { 61 | ret, _, _ := syscall.SyscallN( 62 | (*_IFileSaveDialogVt)(unsafe.Pointer(*me.Ppvt())).SetSaveAsItem, 63 | uintptr(unsafe.Pointer(me.Ppvt())), 64 | uintptr(unsafe.Pointer(si.Ppvt()))) 65 | return utl.ErrorAsHResult(ret) 66 | } 67 | 68 | type _IFileSaveDialogVt struct { 69 | _IFileDialogVt 70 | SetSaveAsItem uintptr 71 | SetProperties uintptr 72 | SetCollectedProperties uintptr 73 | GetProperties uintptr 74 | ApplyProperties uintptr 75 | } 76 | -------------------------------------------------------------------------------- /win/ole/shell/IModalWindow.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package shell 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/win" 10 | "github.com/rodrigocfd/windigo/win/co" 11 | "github.com/rodrigocfd/windigo/win/ole" 12 | ) 13 | 14 | // [IModalWindow] COM interface. 15 | // 16 | // Implements [ole.ComObj] and [ole.ComResource]. 17 | // 18 | // [IModalWindow]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-imodalwindow 19 | type IModalWindow struct{ ole.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 (*IModalWindow) IID() co.IID { 25 | return co.IID_IModalWindow 26 | } 27 | 28 | // [Show] method. 29 | // 30 | // Returns false if user cancelled. 31 | // 32 | // [Show]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-imodalwindow-show 33 | func (me *IModalWindow) Show(hwndOwner win.HWND) (bool, error) { 34 | ret, _, _ := syscall.SyscallN( 35 | (*_IModalWindowVt)(unsafe.Pointer(*me.Ppvt())).Show, 36 | uintptr(unsafe.Pointer(me.Ppvt())), 37 | uintptr(hwndOwner)) 38 | 39 | if wErr := co.ERROR(ret); wErr == co.ERROR_SUCCESS { 40 | return true, nil 41 | } else if wErr == co.ERROR_CANCELLED { 42 | return false, nil 43 | } else { 44 | return false, wErr.ToHresult() 45 | } 46 | } 47 | 48 | type _IModalWindowVt struct { 49 | ole.IUnknownVt 50 | Show uintptr 51 | } 52 | -------------------------------------------------------------------------------- /win/ole/shell/IShellItemArray.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package shell 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/internal/utl" 10 | "github.com/rodrigocfd/windigo/win/co" 11 | "github.com/rodrigocfd/windigo/win/ole" 12 | ) 13 | 14 | // [IShellItemArray] COM interface. 15 | // 16 | // Implements [ole.ComObj] and [ole.ComResource]. 17 | // 18 | // [IShellItemArray]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-ishellitemarray 19 | type IShellItemArray struct{ ole.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 (*IShellItemArray) IID() co.IID { 25 | return co.IID_IShellItemArray 26 | } 27 | 28 | // Returns the path names of each [IShellItem] object by calling 29 | // [IShellItemArray.GetCount], [IShellItemArray.GetItemAt] and 30 | // [IShellItem.GetDisplayName]. 31 | // 32 | // # Example 33 | // 34 | // var arr shell.IShellItemArray // initialized somewhere 35 | // 36 | // names, _ := arr.EnumDisplayNames(co.SIGDN_FILESYSPATH) 37 | // for _, fullPath := range names { 38 | // println(fullPath) 39 | // } 40 | func (me *IShellItemArray) EnumDisplayNames(sigdnName co.SIGDN) ([]string, error) { 41 | localRel := ole.NewReleaser() 42 | defer localRel.Release() 43 | 44 | count, err := me.GetCount() 45 | if err != nil { 46 | return nil, err 47 | } 48 | 49 | names := make([]string, 0, count) 50 | 51 | for i := uint(0); i < count; i++ { 52 | shellItem, err := me.GetItemAt(localRel, i) 53 | if err != nil { 54 | return nil, err 55 | } 56 | 57 | name, err := shellItem.GetDisplayName(sigdnName) 58 | if err != nil { 59 | return nil, err 60 | } 61 | names = append(names, name) 62 | } 63 | return names, nil 64 | } 65 | 66 | // Returns all [IShellItem] objects by calling [IShellItemArray.GetCount] and 67 | // [IShellItemArray.GetItemAt]. 68 | // 69 | // If you just want to retrieve the paths, prefer using 70 | // [IShellItemArray.EnumDisplayNames]. 71 | // 72 | // # Example 73 | // 74 | // var arr shell.IShellItemArray // initialized somewhere 75 | // 76 | // rel := ole.NewReleaser() 77 | // defer rel.Release() 78 | // 79 | // items, _ := arr.EnumItems(rel) 80 | // for _, item := range items { 81 | // fullPath, _ := item.GetDisplayName(co.SIGDN_FILESYSPATH) 82 | // println(fullPath) 83 | // } 84 | func (me *IShellItemArray) EnumItems(releaser *ole.Releaser) ([]*IShellItem, error) { 85 | count, err := me.GetCount() 86 | if err != nil { 87 | return nil, err 88 | } 89 | 90 | items := make([]*IShellItem, 0, count) 91 | 92 | for i := uint(0); i < count; i++ { 93 | shellItem, err := me.GetItemAt(releaser, i) 94 | if err != nil { 95 | return nil, err // stop immediately 96 | } 97 | items = append(items, shellItem) 98 | } 99 | return items, nil 100 | } 101 | 102 | // [GetCount] method. 103 | // 104 | // [GetCount]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ishellitemarray-getcount 105 | func (me *IShellItemArray) GetCount() (uint, error) { 106 | var count uint32 107 | ret, _, _ := syscall.SyscallN( 108 | (*_IShellItemArrayVt)(unsafe.Pointer(*me.Ppvt())).GetCount, 109 | uintptr(unsafe.Pointer(me.Ppvt())), 110 | uintptr(unsafe.Pointer(&count)), 111 | 0) 112 | 113 | if hr := co.HRESULT(ret); hr == co.HRESULT_S_OK { 114 | return uint(count), nil 115 | } else { 116 | return 0, hr 117 | } 118 | } 119 | 120 | // [GetItemAt] method. 121 | // 122 | // [GetItemAt]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ishellitemarray-getitemat 123 | func (me *IShellItemArray) GetItemAt(releaser *ole.Releaser, index uint) (*IShellItem, error) { 124 | var ppvtQueried **ole.IUnknownVt 125 | ret, _, _ := syscall.SyscallN( 126 | (*_IShellItemArrayVt)(unsafe.Pointer(*me.Ppvt())).GetItemAt, 127 | uintptr(unsafe.Pointer(me.Ppvt())), 128 | uintptr(uint32(index)), 129 | uintptr(unsafe.Pointer(&ppvtQueried))) 130 | 131 | if hr := co.HRESULT(ret); hr == co.HRESULT_S_OK { 132 | var pObj *IShellItem 133 | utl.ComCreateObj(&pObj, unsafe.Pointer(ppvtQueried)) 134 | releaser.Add(pObj) 135 | return pObj, nil 136 | } else { 137 | return nil, hr 138 | } 139 | } 140 | 141 | type _IShellItemArrayVt struct { 142 | ole.IUnknownVt 143 | BindToHandler uintptr 144 | GetPropertyStore uintptr 145 | GetPropertyDescriptionList uintptr 146 | GetAttributes uintptr 147 | GetCount uintptr 148 | GetItemAt uintptr 149 | EnumItems uintptr 150 | } 151 | -------------------------------------------------------------------------------- /win/ole/shell/ITaskbarList.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package shell 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/internal/utl" 10 | "github.com/rodrigocfd/windigo/win" 11 | "github.com/rodrigocfd/windigo/win/co" 12 | "github.com/rodrigocfd/windigo/win/ole" 13 | ) 14 | 15 | // [ITaskbarList] COM interface. 16 | // 17 | // Implements [ole.ComObj] and [ole.ComResource]. 18 | // 19 | // # Example 20 | // 21 | // rel := ole.NewReleaser() 22 | // defer rel.Release() 23 | // 24 | // var taskbl *shell.ITaskbarList 25 | // ole.CoCreateInstance( 26 | // rel, 27 | // co.CLSID_TaskbarList, 28 | // nil, 29 | // co.CLSCTX_INPROC_SERVER, 30 | // &taskbl, 31 | // ) 32 | // 33 | // [ITaskbarList]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-itaskbarlist 34 | type ITaskbarList struct{ ole.IUnknown } 35 | 36 | // Returns the unique COM [interface ID]. 37 | // 38 | // [interface ID]: https://learn.microsoft.com/en-us/office/client-developer/outlook/mapi/iid 39 | func (*ITaskbarList) IID() co.IID { 40 | return co.IID_ITaskbarList 41 | } 42 | 43 | // [ActivateTab] method. 44 | // 45 | // [ActivateTab]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-itaskbarlist-activatetab 46 | func (me *ITaskbarList) ActivateTab(hWnd win.HWND) error { 47 | ret, _, _ := syscall.SyscallN( 48 | (*_ITaskbarListVt)(unsafe.Pointer(*me.Ppvt())).ActivateTab, 49 | uintptr(unsafe.Pointer(me.Ppvt())), 50 | uintptr(hWnd)) 51 | return utl.ErrorAsHResult(ret) 52 | } 53 | 54 | // [AddTab] method. 55 | // 56 | // [AddTab]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-itaskbarlist-addtab 57 | func (me *ITaskbarList) AddTab(hWnd win.HWND) error { 58 | ret, _, _ := syscall.SyscallN( 59 | (*_ITaskbarListVt)(unsafe.Pointer(*me.Ppvt())).AddTab, 60 | uintptr(unsafe.Pointer(me.Ppvt())), 61 | uintptr(hWnd)) 62 | return utl.ErrorAsHResult(ret) 63 | } 64 | 65 | // [DeleteTab] method. 66 | // 67 | // [DeleteTab]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-itaskbarlist-deletetab 68 | func (me *ITaskbarList) DeleteTab(hWnd win.HWND) error { 69 | ret, _, _ := syscall.SyscallN( 70 | (*_ITaskbarListVt)(unsafe.Pointer(*me.Ppvt())).DeleteTab, 71 | uintptr(unsafe.Pointer(me.Ppvt())), 72 | uintptr(hWnd)) 73 | return utl.ErrorAsHResult(ret) 74 | } 75 | 76 | // [HrInit] method. 77 | // 78 | // [HrInit]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-itaskbarlist-hrinit 79 | func (me *ITaskbarList) HrInit() error { 80 | ret, _, _ := syscall.SyscallN( 81 | (*_ITaskbarListVt)(unsafe.Pointer(*me.Ppvt())).HrInit, 82 | uintptr(unsafe.Pointer(me.Ppvt()))) 83 | return utl.ErrorAsHResult(ret) 84 | } 85 | 86 | // [SetActiveAlt] method. 87 | // 88 | // [SetActiveAlt]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-itaskbarlist-setactivealt 89 | func (me *ITaskbarList) SetActiveAlt(hWnd win.HWND) error { 90 | ret, _, _ := syscall.SyscallN( 91 | (*_ITaskbarListVt)(unsafe.Pointer(*me.Ppvt())).SetActiveAlt, 92 | uintptr(unsafe.Pointer(me.Ppvt())), 93 | uintptr(hWnd)) 94 | return utl.ErrorAsHResult(ret) 95 | } 96 | 97 | type _ITaskbarListVt struct { 98 | ole.IUnknownVt 99 | HrInit uintptr 100 | AddTab uintptr 101 | DeleteTab uintptr 102 | ActivateTab uintptr 103 | SetActiveAlt uintptr 104 | } 105 | -------------------------------------------------------------------------------- /win/ole/shell/ITaskbarList2.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package shell 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/internal/utl" 10 | "github.com/rodrigocfd/windigo/win" 11 | "github.com/rodrigocfd/windigo/win/co" 12 | ) 13 | 14 | // [ITaskbarList2] COM interface. 15 | // 16 | // Implements [ole.ComObj] and [ole.ComResource]. 17 | // 18 | // # Example 19 | // 20 | // rel := ole.NewReleaser() 21 | // defer rel.Release() 22 | // 23 | // var taskbl *shell.ITaskbarList2 24 | // ole.CoCreateInstance( 25 | // rel, 26 | // co.CLSID_TaskbarList, 27 | // nil, 28 | // co.CLSCTX_INPROC_SERVER, 29 | // &taskbl, 30 | // ) 31 | // 32 | // [ITaskbarList2]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-itaskbarlist2 33 | type ITaskbarList2 struct{ ITaskbarList } 34 | 35 | // Returns the unique COM [interface ID]. 36 | // 37 | // [interface ID]: https://learn.microsoft.com/en-us/office/client-developer/outlook/mapi/iid 38 | func (*ITaskbarList2) IID() co.IID { 39 | return co.IID_ITaskbarList2 40 | } 41 | 42 | // [MarkFullscreenWindow] method. 43 | // 44 | // [MarkFullscreenWindow]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-itaskbarlist2-markfullscreenwindow 45 | func (me *ITaskbarList2) MarkFullscreenWindow(hwnd win.HWND, fullScreen bool) error { 46 | ret, _, _ := syscall.SyscallN( 47 | (*_ITaskbarList2Vt)(unsafe.Pointer(*me.Ppvt())).MarkFullscreenWindow, 48 | uintptr(unsafe.Pointer(me.Ppvt())), 49 | uintptr(hwnd), 50 | utl.BoolToUintptr(fullScreen)) 51 | return utl.ErrorAsHResult(ret) 52 | } 53 | 54 | type _ITaskbarList2Vt struct { 55 | _ITaskbarListVt 56 | MarkFullscreenWindow uintptr 57 | } 58 | -------------------------------------------------------------------------------- /win/ole/shell/ITaskbarList4.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package shell 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/internal/utl" 10 | "github.com/rodrigocfd/windigo/win" 11 | "github.com/rodrigocfd/windigo/win/co" 12 | ) 13 | 14 | // [ITaskbarList4] COM interface. 15 | // 16 | // Implements [ole.ComObj] and [ole.ComResource]. 17 | // 18 | // # Example 19 | // 20 | // rel := ole.NewReleaser() 21 | // defer rel.Release() 22 | // 23 | // var taskbl *shell.ITaskbarList4 24 | // ole.CoCreateInstance( 25 | // rel, 26 | // co.CLSID_TaskbarList, 27 | // nil, 28 | // co.CLSCTX_INPROC_SERVER, 29 | // &taskbl, 30 | // ) 31 | // 32 | // [ITaskbarList4]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-itaskbarlist4 33 | type ITaskbarList4 struct{ ITaskbarList3 } 34 | 35 | // Returns the unique COM [interface ID]. 36 | // 37 | // [interface ID]: https://learn.microsoft.com/en-us/office/client-developer/outlook/mapi/iid 38 | func (*ITaskbarList4) IID() co.IID { 39 | return co.IID_ITaskbarList4 40 | } 41 | 42 | // [SetProperties] method. 43 | // 44 | // [SetProperties]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-itaskbarlist4-settabproperties 45 | func (me *ITaskbarList4) SetProperties(hwndTab win.HWND, flags co.STPFLAG) error { 46 | ret, _, _ := syscall.SyscallN( 47 | (*_ITaskbarList4Vt)(unsafe.Pointer(*me.Ppvt())).SetTabProperties, 48 | uintptr(unsafe.Pointer(me.Ppvt())), 49 | uintptr(hwndTab), 50 | uintptr(flags)) 51 | return utl.ErrorAsHResult(ret) 52 | } 53 | 54 | type _ITaskbarList4Vt struct { 55 | _ITaskbarList3Vt 56 | SetTabProperties uintptr 57 | } 58 | -------------------------------------------------------------------------------- /win/ole/shell/structs.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package shell 4 | 5 | import ( 6 | "strings" 7 | "unsafe" 8 | 9 | "github.com/rodrigocfd/windigo/win" 10 | "github.com/rodrigocfd/windigo/win/co" 11 | "github.com/rodrigocfd/windigo/win/ole" 12 | "github.com/rodrigocfd/windigo/win/wstr" 13 | ) 14 | 15 | // [COMDLG_FILTERSPEC] struct syntactic sugar. 16 | // 17 | // When the native syscall is made, this struct is converted into the raw 18 | // struct. 19 | // 20 | // [COMDLG_FILTERSPEC]: https://learn.microsoft.com/en-us/windows/win32/api/shtypes/ns-shtypes-comdlg_filterspec 21 | type COMDLG_FILTERSPEC struct { 22 | Name string 23 | Spec string 24 | } 25 | 26 | // [COMDLG_FILTERSPEC] struct. 27 | // 28 | // [COMDLG_FILTERSPEC]: https://learn.microsoft.com/en-us/windows/win32/api/shtypes/ns-shtypes-comdlg_filterspec 29 | type _COMDLG_FILTERSPEC struct { 30 | PszName *uint16 31 | PszSpec *uint16 32 | } 33 | 34 | // [ITEMIDLIST] struct. 35 | // 36 | // Implements [ole.ComResource]. 37 | // 38 | // [ITEMIDLIST]: https://learn.microsoft.com/en-us/windows/win32/api/shtypes/ns-shtypes-itemidlist 39 | type ITEMIDLIST uintptr 40 | 41 | // Calls [ole.HTASKMEM.CoTaskMemFree]. 42 | // 43 | // You usually don't need to call this method directly, since every function 44 | // which returns a [COM] object will require an [ole.Releaser] to manage the 45 | // object's lifetime. 46 | // 47 | // [COM]: https://learn.microsoft.com/en-us/windows/win32/com/component-object-model--com--portal 48 | func (il *ITEMIDLIST) Release() { 49 | if *il != 0 { 50 | ole.HTASKMEM(*il).CoTaskMemFree() 51 | *il = 0 52 | } 53 | } 54 | 55 | // [PROPERTYKEY] struct. 56 | // 57 | // [PROPERTYKEY]: https://learn.microsoft.com/en-us/windows/win32/api/wtypes/ns-wtypes-propertykey 58 | type PROPERTYKEY struct { 59 | data [20]byte // packed 60 | } 61 | 62 | // Creates a [PROPERTYKEY] from a string representation. 63 | func PropertykeyFrom(pkey co.PKEY) PROPERTYKEY { 64 | parts := strings.SplitN(string(pkey), " ", 2) 65 | fmtId := win.GuidFrom(parts[0]) 66 | pId := wstr.ParseUint(parts[1]) 67 | 68 | var out PROPERTYKEY 69 | out.SetFmdId(fmtId) 70 | out.SetPId(uint32(pId)) 71 | return out 72 | } 73 | 74 | func (pk *PROPERTYKEY) FmtId() win.GUID { 75 | return *(*win.GUID)(unsafe.Pointer(&pk.data[0])) 76 | } 77 | func (pk *PROPERTYKEY) SetFmdId(fmtId win.GUID) { 78 | *(*win.GUID)(unsafe.Pointer(&pk.data[0])) = fmtId 79 | } 80 | 81 | func (pk *PROPERTYKEY) PId() uint32 { 82 | return *(*uint32)(unsafe.Pointer(&pk.data[16])) 83 | } 84 | func (pk *PROPERTYKEY) SetPId(pId uint32) { 85 | *(*uint32)(unsafe.Pointer(&pk.data[16])) = pId 86 | } 87 | 88 | // [THUMBBUTTON] struct. 89 | // 90 | // [THUMBBUTTON]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/ns-shobjidl_core-thumbbutton 91 | type THUMBBUTTON struct { 92 | DwMask co.THB 93 | IId uint32 94 | IBitmap uint32 95 | HIcon win.HICON 96 | szTip [260]uint16 97 | DwFlags co.THBF 98 | } 99 | 100 | func (tb *THUMBBUTTON) SzTip() string { 101 | return wstr.WstrSliceToStr(tb.szTip[:]) 102 | } 103 | func (tb *THUMBBUTTON) SetSzTip(val string) { 104 | wstr.StrToWstrBuf(wstr.SubstrRunes(val, 0, uint(len(tb.szTip)-1)), tb.szTip[:]) 105 | } 106 | -------------------------------------------------------------------------------- /win/ole/structs.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package ole 4 | 5 | import ( 6 | "unsafe" 7 | 8 | "github.com/rodrigocfd/windigo/internal/utl" 9 | "github.com/rodrigocfd/windigo/win" 10 | "github.com/rodrigocfd/windigo/win/co" 11 | "github.com/rodrigocfd/windigo/win/wstr" 12 | ) 13 | 14 | // [DVTARGETDEVICE] struct. 15 | // 16 | // ⚠️ You must call [DVTARGETDEVICE.SetTdSize] to initialize the struct. 17 | // 18 | // # Example 19 | // 20 | // var dvt ole.DVTARGETDEVICE 21 | // dvt.SetTdSize() 22 | // 23 | // [DVTARGETDEVICE]: https://learn.microsoft.com/en-us/windows/win32/api/objidl/ns-objidl-dvtargetdevice 24 | type DVTARGETDEVICE struct { 25 | tdSize uint32 26 | tdDriverNameOffset uint16 27 | tdDeviceNameOffset uint16 28 | tdPortNameOffset uint16 29 | tdExtDevmodeOffset uint16 30 | tdData [1]byte 31 | } 32 | 33 | // Sets the tdSize field to the size of the struct, correctly initializing it. 34 | func (dvt *DVTARGETDEVICE) SetTdSize() { 35 | dvt.tdSize = uint32(unsafe.Sizeof(*dvt)) 36 | } 37 | 38 | func (dvt *DVTARGETDEVICE) DriverName() string { 39 | ptr := unsafe.Pointer(dvt) 40 | ptr = unsafe.Add(ptr, dvt.tdDriverNameOffset) 41 | return wstr.WstrPtrToStr((*uint16)(ptr)) 42 | } 43 | 44 | func (dvt *DVTARGETDEVICE) DeviceName() string { 45 | ptr := unsafe.Pointer(dvt) 46 | ptr = unsafe.Add(ptr, dvt.tdDeviceNameOffset) 47 | return wstr.WstrPtrToStr((*uint16)(ptr)) 48 | } 49 | 50 | func (dvt *DVTARGETDEVICE) PortName() string { 51 | ptr := unsafe.Pointer(dvt) 52 | ptr = unsafe.Add(ptr, dvt.tdPortNameOffset) 53 | return wstr.WstrPtrToStr((*uint16)(ptr)) 54 | } 55 | 56 | // [FORMATETC] struct. 57 | // 58 | // [FORMATETC]: https://learn.microsoft.com/en-us/windows/win32/api/objidl/ns-objidl-formatetc 59 | type FORMATETC struct { 60 | CfFormat co.CF 61 | Ptd *DVTARGETDEVICE 62 | Aspect co.DVASPECT 63 | Lindex int32 64 | Tymed co.TYMED 65 | } 66 | 67 | // [STATSTG] struct. 68 | // 69 | // [STATSTG]: https://learn.microsoft.com/en-us/windows/win32/api/objidl/ns-objidl-statstg 70 | type STATSTG struct { 71 | PwcsName *uint16 72 | Type co.STGTY 73 | CbSize uint64 74 | MTime win.FILETIME 75 | CTime win.FILETIME 76 | ATime win.FILETIME 77 | GrfMode uint32 78 | GrfLocksSupported co.LOCKTYPE 79 | ClsId win.GUID 80 | GrfStateBits uint32 81 | reserved uint32 82 | } 83 | 84 | // [STGMEDIUM] struct. 85 | // 86 | // If you received this struct from a COM call, you'll have to free the memory 87 | // with [ReleaseStgMedium]. 88 | // 89 | // [STGMEDIUM]: https://learn.microsoft.com/en-us/windows/win32/api/objidl/ns-objidl-ustgmedium-r1 90 | type STGMEDIUM struct { 91 | tymed co.TYMED 92 | data uintptr // union 93 | PUnkForRelease IUnknown 94 | } 95 | 96 | func (stg *STGMEDIUM) Tymed() co.TYMED { 97 | return stg.tymed 98 | } 99 | 100 | func (stg *STGMEDIUM) HBitmap() (win.HBITMAP, bool) { 101 | if stg.tymed == co.TYMED_GDI { 102 | return win.HBITMAP(stg.data), true 103 | } 104 | return win.HBITMAP(0), false 105 | } 106 | 107 | func (stg *STGMEDIUM) HGlobal() (win.HGLOBAL, bool) { 108 | if stg.tymed == co.TYMED_HGLOBAL { 109 | return win.HGLOBAL(stg.data), true 110 | } 111 | return win.HGLOBAL(0), false 112 | } 113 | 114 | func (stg *STGMEDIUM) FileName() (string, bool) { 115 | if stg.tymed == co.TYMED_FILE { 116 | return wstr.WstrPtrToStr((*uint16)(unsafe.Pointer(stg.data))), true 117 | } 118 | return "", false 119 | } 120 | 121 | func (stg *STGMEDIUM) IStream(releaser *Releaser) (*IStream, bool) { 122 | if stg.tymed == co.TYMED_ISTREAM { 123 | ppvt := (**IUnknownVt)(unsafe.Pointer(stg.data)) 124 | var pCurrent *IStream 125 | utl.ComCreateObj(&pCurrent, unsafe.Pointer(ppvt)) 126 | 127 | var pCloned *IStream 128 | pCurrent.AddRef(releaser, &pCloned) // clone, because we'll release it independently 129 | return pCloned, true 130 | } 131 | return nil, false 132 | } 133 | -------------------------------------------------------------------------------- /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/internal/dll" 10 | "github.com/rodrigocfd/windigo/win/co" 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(_K32GetPerformanceInfo.Addr(), 21 | uintptr(unsafe.Pointer(&pi)), 22 | uintptr(unsafe.Sizeof(pi))) 23 | if ret == 0 { 24 | return PERFORMANCE_INFORMATION{}, co.ERROR(err) 25 | } 26 | return pi, nil 27 | } 28 | 29 | var _K32GetPerformanceInfo = dll.Psapi.NewProc("K32GetPerformanceInfo") 30 | -------------------------------------------------------------------------------- /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 pi win.PERFORMANCE_INFORMATION 57 | // pi.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/shell_funcs.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/win/co" 11 | "github.com/rodrigocfd/windigo/win/wstr" 12 | ) 13 | 14 | // [CommandLineToArgv] function. 15 | // 16 | // Typically used with [GetCommandLine]. 17 | // 18 | // [CommandLineToArgv]: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw 19 | func CommandLineToArgv(cmdLine string) ([]string, error) { 20 | cmdLine16 := wstr.NewBufWith[wstr.Stack20](cmdLine, wstr.EMPTY_IS_NIL) 21 | var pNumArgs int32 22 | 23 | ret, _, err := syscall.SyscallN(_CommandLineToArgvW.Addr(), 24 | uintptr(cmdLine16.UnsafePtr()), 25 | uintptr(unsafe.Pointer(&pNumArgs))) 26 | if ret == 0 { 27 | return nil, co.ERROR(err) 28 | } 29 | 30 | lpPtrs := unsafe.Slice((**uint16)(unsafe.Pointer(ret)), pNumArgs) // []*uint16 31 | strs := make([]string, 0, pNumArgs) 32 | 33 | for _, lpPtr := range lpPtrs { 34 | strs = append(strs, wstr.WstrPtrToStr(lpPtr)) 35 | } 36 | return strs, nil 37 | } 38 | 39 | var _CommandLineToArgvW = dll.Shell32.NewProc("CommandLineToArgvW") 40 | 41 | // [Shell_NotifyIcon] function. 42 | // 43 | // [Shell_NotifyIcon]: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shell_notifyiconw 44 | func Shell_NotifyIcon(message co.NIM, data *NOTIFYICONDATA) error { 45 | ret, _, _ := syscall.SyscallN(_Shell_NotifyIconW.Addr(), 46 | uintptr(message), 47 | uintptr(unsafe.Pointer(data))) 48 | if ret == 0 { 49 | return co.ERROR_INVALID_PARAMETER 50 | } 51 | return nil 52 | } 53 | 54 | var _Shell_NotifyIconW = dll.Shell32.NewProc("Shell_NotifyIconW") 55 | 56 | // [Shell_NotifyIconGetRect] function. 57 | // 58 | // [Shell_NotifyIconGetRect]: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shell_notifyicongetrect 59 | func Shell_NotifyIconGetRect(identifier *NOTIFYICONIDENTIFIER) (RECT, error) { 60 | var rc RECT 61 | ret, _, _ := syscall.SyscallN(_Shell_NotifyIconGetRect.Addr(), 62 | uintptr(unsafe.Pointer(identifier)), 63 | uintptr(unsafe.Pointer(&rc))) 64 | if hr := co.HRESULT(ret); hr != co.HRESULT_S_OK { 65 | return RECT{}, hr 66 | } 67 | return rc, nil 68 | } 69 | 70 | var _Shell_NotifyIconGetRect = dll.Shell32.NewProc("Shell_NotifyIconGetRect") 71 | 72 | // [SHGetFileInfo] function. 73 | // 74 | // ⚠️ You must defer [HICON.DestroyIcon] on the HIcon member of the returned 75 | // struct. 76 | // 77 | // [SHGetFileInfo]: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shgetfileinfow 78 | func SHGetFileInfo(path string, fileAttrs co.FILE_ATTRIBUTE, flags co.SHGFI) (SHFILEINFO, error) { 79 | path16 := wstr.NewBufWith[wstr.Stack20](path, wstr.ALLOW_EMPTY) 80 | var sfi SHFILEINFO 81 | 82 | ret, _, _ := syscall.SyscallN(_SHGetFileInfoW.Addr(), 83 | uintptr(path16.UnsafePtr()), 84 | uintptr(fileAttrs), 85 | uintptr(unsafe.Pointer(&sfi)), 86 | unsafe.Sizeof(sfi), 87 | uintptr(flags)) 88 | 89 | if (flags&co.SHGFI_EXETYPE) == 0 || (flags&co.SHGFI_SYSICONINDEX) == 0 { 90 | if ret == 0 { 91 | return SHFILEINFO{}, co.ERROR_INVALID_PARAMETER 92 | } 93 | } 94 | if (flags & co.SHGFI_EXETYPE) != 0 { 95 | if ret == 0 { 96 | return SHFILEINFO{}, co.ERROR_INVALID_PARAMETER 97 | } 98 | } 99 | 100 | return sfi, nil 101 | } 102 | 103 | var _SHGetFileInfoW = dll.Shell32.NewProc("SHGetFileInfoW") 104 | -------------------------------------------------------------------------------- /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/internal/dll" 10 | "github.com/rodrigocfd/windigo/internal/utl" 11 | "github.com/rodrigocfd/windigo/win/co" 12 | "github.com/rodrigocfd/windigo/win/wstr" 13 | ) 14 | 15 | // Handle to an [internal drop structure]. 16 | // 17 | // [internal drop structure]: https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#hdrop 18 | type HDROP HANDLE 19 | 20 | // [DragFinish] function. 21 | // 22 | // If you're using [RegisterDragDrop], don't call this function. 23 | // 24 | // [DragFinish]: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-dragfinish 25 | // [RegisterDragDrop]: https://learn.microsoft.com/en-us/windows/win32/api/ole/nf-ole-registerdragdrop 26 | func (hDrop HDROP) DragFinish() { 27 | syscall.SyscallN(_DragFinish.Addr(), 28 | uintptr(hDrop)) 29 | } 30 | 31 | var _DragFinish = dll.Shell32.NewProc("DragFinish") 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(_DragQueryFileW.Addr(), 55 | uintptr(hDrop), uintptr(0xffff_ffff), 0, 0) 56 | if ret == 0 { 57 | return nil, co.ERROR_INVALID_PARAMETER 58 | } 59 | 60 | count := uint32(ret) 61 | var pathBuf [utl.MAX_PATH]uint16 // buffer to receive a path 62 | paths := make([]string, 0, count) 63 | 64 | for i := uint32(0); i < count; i++ { 65 | ret, _, _ = syscall.SyscallN(_DragQueryFileW.Addr(), 66 | uintptr(hDrop), 67 | uintptr(i), 68 | uintptr(unsafe.Pointer(&pathBuf[0])), 69 | uintptr(uint32(len(pathBuf)))) 70 | if ret == 0 { 71 | return nil, co.ERROR_INVALID_PARAMETER 72 | } 73 | paths = append(paths, wstr.WstrSliceToStr(pathBuf[:])) 74 | } 75 | 76 | return paths, nil 77 | } 78 | 79 | var _DragQueryFileW = dll.Shell32.NewProc("DragQueryFileW") 80 | 81 | // [DragQueryPoint] function. 82 | // 83 | // Returns true if dropped within client area. 84 | // 85 | // [DragQueryPoint]: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-dragquerypoint 86 | func (hDrop HDROP) DragQueryPoint() (POINT, bool) { 87 | var pt POINT 88 | ret, _, _ := syscall.SyscallN(_DragQueryPoint.Addr(), 89 | uintptr(hDrop), 90 | uintptr(unsafe.Pointer(&pt))) 91 | return pt, ret != 0 92 | } 93 | 94 | var _DragQueryPoint = dll.Shell32.NewProc("DragQueryPoint") 95 | -------------------------------------------------------------------------------- /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/win/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 | app16 := wstr.NewBufWith[wstr.Stack20](app, wstr.ALLOW_EMPTY) 18 | other16 := wstr.NewBufWith[wstr.Stack20](otherStuff, wstr.EMPTY_IS_NIL) 19 | 20 | ret, _, _ := syscall.SyscallN(_ShellAboutW.Addr(), 21 | uintptr(hWnd), 22 | uintptr(app16.UnsafePtr()), 23 | uintptr(other16.UnsafePtr()), 24 | uintptr(hIcon)) 25 | return utl.ZeroAsSysInvalidParm(ret) 26 | } 27 | 28 | var _ShellAboutW = dll.Shell32.NewProc("ShellAboutW") 29 | -------------------------------------------------------------------------------- /win/shell_structs.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "unsafe" 7 | 8 | "github.com/rodrigocfd/windigo/internal/utl" 9 | "github.com/rodrigocfd/windigo/win/co" 10 | "github.com/rodrigocfd/windigo/win/wstr" 11 | ) 12 | 13 | // [NOTIFYICONDATA] struct. 14 | // 15 | // ⚠️ You must call [NOTIFYICONDATA.SetCbSize] to initialize the struct. 16 | // 17 | // # Example 18 | // 19 | // var nid win.NOTIFYICONDATA 20 | // nid.SetCbSize() 21 | // 22 | // [NOTIFYICONDATA]: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-notifyicondataw 23 | type NOTIFYICONDATA struct { 24 | cbSize uint32 25 | HWnd HWND 26 | UID uint32 27 | UFlags co.NIF 28 | UCallbackMessage co.WM 29 | HIcon HICON 30 | szTip [128]uint16 31 | DwState co.NIS 32 | DwStateMask co.NIS 33 | szInfo [256]uint16 34 | uVersion uint32 35 | szInfoTitle [64]uint16 36 | DwInfoFlags co.NIIF 37 | GuidItem GUID 38 | HBalloonIcon HICON 39 | } 40 | 41 | // Sets the cbSize field to the size of the struct, correctly initializing it. 42 | func (nid *NOTIFYICONDATA) SetCbSize() { 43 | nid.cbSize = uint32(unsafe.Sizeof(*nid)) 44 | } 45 | 46 | func (nid *NOTIFYICONDATA) SzTip() string { 47 | return wstr.WstrSliceToStr(nid.szTip[:]) 48 | } 49 | func (nid *NOTIFYICONDATA) SetSzTip(val string) { 50 | wstr.StrToWstrBuf(wstr.SubstrRunes(val, 0, uint(len(nid.szTip)-1)), nid.szTip[:]) 51 | } 52 | 53 | func (nid *NOTIFYICONDATA) SzInfo() string { 54 | return wstr.WstrSliceToStr(nid.szInfo[:]) 55 | } 56 | func (nid *NOTIFYICONDATA) SetSzInfo(val string) { 57 | wstr.StrToWstrBuf(wstr.SubstrRunes(val, 0, uint(len(nid.szInfo)-1)), nid.szInfo[:]) 58 | } 59 | 60 | func (nid *NOTIFYICONDATA) SzInfoTitle() string { 61 | return wstr.WstrSliceToStr(nid.szInfoTitle[:]) 62 | } 63 | func (nid *NOTIFYICONDATA) SetSzInfoTitle(val string) { 64 | wstr.StrToWstrBuf(wstr.SubstrRunes(val, 0, uint(len(nid.szInfoTitle)-1)), nid.szInfoTitle[:]) 65 | } 66 | 67 | // [NOTIFYICONIDENTIFIER] struct. 68 | // 69 | // ⚠️ You must call [NOTIFYICONIDENTIFIER.SetCbSize] to initialize the struct. 70 | // 71 | // # Example 72 | // 73 | // var nii win.NOTIFYICONIDENTIFIER 74 | // nii.SetCbSize() 75 | // 76 | // [NOTIFYICONIDENTIFIER]: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-notifyiconidentifier 77 | type NOTIFYICONIDENTIFIER struct { 78 | cbSize uint32 79 | HWnd HWND 80 | UID uint32 81 | GuidItem GUID 82 | } 83 | 84 | // Sets the cbSize field to the size of the struct, correctly initializing it. 85 | func (nii *NOTIFYICONIDENTIFIER) SetCbSize() { 86 | nii.cbSize = uint32(unsafe.Sizeof(*nii)) 87 | } 88 | 89 | // [SHFILEINFO] struct. 90 | // 91 | // [SHFILEINFO]: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-shfileinfow 92 | type SHFILEINFO struct { 93 | HIcon HICON 94 | IIcon int32 95 | DwAttributes co.SFGAO 96 | szDisplayName [utl.MAX_PATH]uint16 97 | szTypeName [80]uint16 98 | } 99 | 100 | func (shf *SHFILEINFO) SzDisplayName() string { 101 | return wstr.WstrSliceToStr(shf.szDisplayName[:]) 102 | } 103 | func (shf *SHFILEINFO) SetSzDisplayName(val string) { 104 | wstr.StrToWstrBuf(wstr.SubstrRunes(val, 0, uint(len(shf.szDisplayName)-1)), shf.szDisplayName[:]) 105 | } 106 | 107 | func (shf *SHFILEINFO) SzTypeName() string { 108 | return wstr.WstrSliceToStr(shf.szTypeName[:]) 109 | } 110 | func (shf *SHFILEINFO) SetSzTypeName(val string) { 111 | wstr.StrToWstrBuf(wstr.SubstrRunes(val, 0, uint(len(shf.szTypeName)-1)), shf.szTypeName[:]) 112 | } 113 | -------------------------------------------------------------------------------- /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/internal/dll" 10 | "github.com/rodrigocfd/windigo/internal/utl" 11 | "github.com/rodrigocfd/windigo/win/co" 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(_CreateAcceleratorTableW.Addr(), 26 | uintptr(unsafe.Pointer(&accelList[0])), 27 | uintptr(int32(len(accelList)))) 28 | if ret == 0 { 29 | return HACCEL(0), co.ERROR(err) 30 | } 31 | return HACCEL(ret), nil 32 | } 33 | 34 | var _CreateAcceleratorTableW = dll.User32.NewProc("CreateAcceleratorTableW") 35 | 36 | // [CopyAcceleratorTable] function. 37 | // 38 | // [CopyAcceleratorTable]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-copyacceleratortablew 39 | func (hAccel HACCEL) CopyAcceleratorTable() []ACCEL { 40 | szRet, _, _ := syscall.SyscallN(_CopyAcceleratorTableW.Addr(), 41 | uintptr(hAccel), 42 | 0, 0) 43 | if szRet == 0 { 44 | return []ACCEL{} 45 | } 46 | accelList := make([]ACCEL, szRet) 47 | syscall.SyscallN(_CopyAcceleratorTableW.Addr(), 48 | uintptr(hAccel), uintptr(unsafe.Pointer(&accelList[0])), szRet) 49 | return accelList 50 | } 51 | 52 | var _CopyAcceleratorTableW = dll.User32.NewProc("CopyAcceleratorTableW") 53 | 54 | // [DestroyAcceleratorTable] function. 55 | // 56 | // Paired with [CreateAcceleratorTable]. 57 | // 58 | // [DestroyAcceleratorTable]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-destroyacceleratortable 59 | func (hAccel HACCEL) DestroyAcceleratorTable() error { 60 | ret, _, err := syscall.SyscallN(_DestroyAcceleratorTable.Addr(), 61 | uintptr(hAccel)) 62 | return utl.ZeroAsGetLastError(ret, err) 63 | } 64 | 65 | var _DestroyAcceleratorTable = dll.User32.NewProc("DestroyAcceleratorTable") 66 | -------------------------------------------------------------------------------- /win/user_hbrush.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/win/co" 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(_GetSysColorBrush.Addr(), 17 | uintptr(index)) 18 | if ret == 0 { 19 | return HBRUSH(0), co.ERROR_INVALID_PARAMETER 20 | } 21 | return HBRUSH(ret), nil 22 | } 23 | 24 | var _GetSysColorBrush = dll.User32.NewProc("GetSysColorBrush") 25 | -------------------------------------------------------------------------------- /win/user_hcursor.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/win/co" 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(_DestroyCursor.Addr(), 31 | uintptr(hCursor)) 32 | return utl.ZeroAsGetLastError(ret, err) 33 | } 34 | 35 | var _DestroyCursor = dll.User32.NewProc("DestroyCursor") 36 | 37 | // [SetCursor] function. 38 | // 39 | // [SetCursor]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setcursor 40 | func (hCursor HCURSOR) SetCursor() (HCURSOR, error) { 41 | ret, _, err := syscall.SyscallN(_SetCursor.Addr(), 42 | uintptr(hCursor)) 43 | if wErr := co.ERROR(err); wErr != co.ERROR_SUCCESS { 44 | return HCURSOR(0), err 45 | } else { 46 | return HCURSOR(ret), nil 47 | } 48 | } 49 | 50 | var _SetCursor = dll.User32.NewProc("SetCursor") 51 | -------------------------------------------------------------------------------- /win/user_hdc.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "runtime" 7 | "syscall" 8 | "unsafe" 9 | 10 | "github.com/rodrigocfd/windigo/internal/dll" 11 | "github.com/rodrigocfd/windigo/internal/utl" 12 | "github.com/rodrigocfd/windigo/win/co" 13 | ) 14 | 15 | // [DrawIcon] function. 16 | // 17 | // [DrawIcon]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-drawicon 18 | func (hdc HDC) DrawIcon(x, y int, hIcon HICON) error { 19 | ret, _, err := syscall.SyscallN(_DrawIcon.Addr(), 20 | uintptr(hdc), 21 | uintptr(int32(x)), 22 | uintptr(int32(y)), 23 | uintptr(hIcon)) 24 | return utl.ZeroAsGetLastError(ret, err) 25 | } 26 | 27 | var _DrawIcon = dll.User32.NewProc("DrawIcon") 28 | 29 | // [DrawIconEx] function. 30 | // 31 | // [DrawIconEx]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-drawiconex 32 | func (hdc HDC) DrawIconEx( 33 | pos POINT, 34 | hIcon HICON, 35 | size SIZE, 36 | frameIndexIfCursor uint, 37 | hbrFlickerFree HBRUSH, 38 | diFlags co.DI, 39 | ) error { 40 | ret, _, err := syscall.SyscallN(_DrawIconEx.Addr(), 41 | uintptr(hdc), 42 | uintptr(pos.X), 43 | uintptr(pos.Y), 44 | uintptr(hIcon), 45 | uintptr(size.Cx), 46 | uintptr(size.Cy), 47 | uintptr(uint32(frameIndexIfCursor)), 48 | uintptr(hbrFlickerFree), 49 | uintptr(diFlags)) 50 | return utl.ZeroAsGetLastError(ret, err) 51 | } 52 | 53 | var _DrawIconEx = dll.User32.NewProc("DrawIconEx") 54 | 55 | // [EnumDisplayMonitors] function. 56 | // 57 | // [EnumDisplayMonitors]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaymonitors 58 | func (hdc HDC) EnumDisplayMonitors(rcClip *RECT) ([]EnumDisplayMonitorsInfo, error) { 59 | pPack := &_EnumDisplayMonitorsPack{ 60 | arr: make([]EnumDisplayMonitorsInfo, 0), 61 | } 62 | ret, _, _ := syscall.SyscallN(_EnumDisplayMonitors.Addr(), 63 | uintptr(hdc), 64 | uintptr(unsafe.Pointer(rcClip)), 65 | enumDisplayMonitorsCallback(), 66 | uintptr(unsafe.Pointer(pPack))) 67 | runtime.KeepAlive(pPack) 68 | if ret == 0 { 69 | return nil, co.ERROR_INVALID_PARAMETER 70 | } 71 | return pPack.arr, nil 72 | } 73 | 74 | type ( 75 | _EnumDisplayMonitorsPack struct{ arr []EnumDisplayMonitorsInfo } 76 | 77 | // Returned by [HDC.EnumDisplayMonitors]. 78 | EnumDisplayMonitorsInfo struct { 79 | HMon HMONITOR 80 | HdcMon HDC 81 | Rc RECT 82 | } 83 | ) 84 | 85 | var ( 86 | _EnumDisplayMonitors = dll.User32.NewProc("EnumDisplayMonitors") 87 | _enumDisplayMonitorsCallback uintptr 88 | ) 89 | 90 | func enumDisplayMonitorsCallback() uintptr { 91 | if _enumDisplayMonitorsCallback == 0 { 92 | _enumDisplayMonitorsCallback = syscall.NewCallback( 93 | func(hMon HMONITOR, hdcMon HDC, rcMon *RECT, lParam LPARAM) uintptr { 94 | pPack := (*_EnumDisplayMonitorsPack)(unsafe.Pointer(lParam)) 95 | pPack.arr = append(pPack.arr, EnumDisplayMonitorsInfo{hMon, hdcMon, *rcMon}) 96 | return 1 97 | }, 98 | ) 99 | } 100 | return _enumDisplayMonitorsCallback 101 | } 102 | 103 | // [FrameRect] function. 104 | // 105 | // [FrameRect]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-framerect 106 | func (hdc HDC) FrameRect(rc *RECT, hBrush HBRUSH) error { 107 | ret, _, err := syscall.SyscallN(_FrameRect.Addr(), 108 | uintptr(hdc), 109 | uintptr(unsafe.Pointer(rc)), 110 | uintptr(hBrush)) 111 | return utl.ZeroAsGetLastError(ret, err) 112 | } 113 | 114 | var _FrameRect = dll.User32.NewProc("FrameRect") 115 | 116 | // [InvertRect] function. 117 | // 118 | // [InvertRect]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-invertrect 119 | func (hdc HDC) InvertRect(rc *RECT) error { 120 | ret, _, err := syscall.SyscallN(_InvertRect.Addr(), 121 | uintptr(hdc), 122 | uintptr(unsafe.Pointer(rc))) 123 | return utl.ZeroAsGetLastError(ret, err) 124 | } 125 | 126 | var _InvertRect = dll.User32.NewProc("InvertRect") 127 | 128 | // [PaintDesktop] function. 129 | // 130 | // [PaintDesktop]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-paintdesktop 131 | func (hdc HDC) PaintDesktop() error { 132 | ret, _, err := syscall.SyscallN(_PaintDesktop.Addr(), 133 | uintptr(hdc)) 134 | return utl.ZeroAsGetLastError(ret, err) 135 | } 136 | 137 | var _PaintDesktop = dll.User32.NewProc("PaintDesktop") 138 | 139 | // [WindowFromDC] function. 140 | // 141 | // [WindowFromDC]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-windowfromdc 142 | func (hdc HDC) WindowFromDC() HWND { 143 | ret, _, _ := syscall.SyscallN(_WindowFromDC.Addr(), 144 | uintptr(hdc)) 145 | return HWND(ret) 146 | } 147 | 148 | var _WindowFromDC = dll.User32.NewProc("WindowFromDC") 149 | -------------------------------------------------------------------------------- /win/user_hdwp.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/win/co" 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 | // ⚠️ You must defer [HDWP.EndDeferWindowPos]. 21 | // 22 | // [BeginDeferWindowPos]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-begindeferwindowpos 23 | func BeginDeferWindowPos(numWindows uint) (HDWP, error) { 24 | ret, _, err := syscall.SyscallN(_BeginDeferWindowPos.Addr(), 25 | uintptr(numWindows)) 26 | if ret == 0 { 27 | return HDWP(0), co.ERROR(err) 28 | } 29 | return HDWP(ret), nil 30 | } 31 | 32 | var _BeginDeferWindowPos = dll.User32.NewProc("BeginDeferWindowPos") 33 | 34 | // [DeferWindowPos] function. 35 | // 36 | // [DeferWindowPos]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-deferwindowpos 37 | func (hDwp HDWP) DeferWindowPos( 38 | hWnd, hwndInsertAfter HWND, 39 | x, y, cx, cy int, 40 | uFlags co.SWP, 41 | ) (HDWP, error) { 42 | ret, _, err := syscall.SyscallN(_DeferWindowPos.Addr(), 43 | uintptr(hDwp), 44 | uintptr(hWnd), 45 | uintptr(hwndInsertAfter), 46 | uintptr(int32(x)), 47 | uintptr(int32(y)), 48 | uintptr(int32(cx)), 49 | uintptr(int32(cy)), 50 | uintptr(uFlags)) 51 | if ret == 0 { 52 | return HDWP(0), co.ERROR(err) 53 | } 54 | return HDWP(ret), nil 55 | } 56 | 57 | var _DeferWindowPos = dll.User32.NewProc("DeferWindowPos") 58 | 59 | // [EndDeferWindowPos] function. 60 | // 61 | // Paired with [BeginDeferWindowPos]. 62 | // 63 | // [EndDeferWindowPos]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enddeferwindowpos 64 | // [BeginDeferWindowPos]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-begindeferwindowpos 65 | func (hDwp HDWP) EndDeferWindowPos() error { 66 | ret, _, err := syscall.SyscallN(_EndDeferWindowPos.Addr(), 67 | uintptr(hDwp)) 68 | return utl.ZeroAsGetLastError(ret, err) 69 | } 70 | 71 | var _EndDeferWindowPos = dll.User32.NewProc("EndDeferWindowPos") 72 | -------------------------------------------------------------------------------- /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/internal/dll" 10 | "github.com/rodrigocfd/windigo/internal/utl" 11 | "github.com/rodrigocfd/windigo/win/co" 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(_CreateIconIndirect.Addr(), 26 | uintptr(unsafe.Pointer(info))) 27 | if ret == 0 { 28 | return HICON(0), co.ERROR(err) 29 | } 30 | return HICON(ret), nil 31 | } 32 | 33 | var _CreateIconIndirect = dll.User32.NewProc("CreateIconIndirect") 34 | 35 | // [CopyIcon] function. 36 | // 37 | // ⚠️ You must defer [HICON.DestroyIcon]. 38 | // 39 | // [CopyIcon]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-copyicon 40 | func (hIcon HICON) CopyIcon() (HICON, error) { 41 | ret, _, err := syscall.SyscallN(_CopyIcon.Addr(), 42 | uintptr(hIcon)) 43 | if ret == 0 { 44 | return HICON(0), co.ERROR(err) 45 | } 46 | return HICON(ret), nil 47 | } 48 | 49 | var _CopyIcon = dll.User32.NewProc("CopyIcon") 50 | 51 | // [DestroyIcon] function. 52 | // 53 | // [DestroyIcon]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-destroyicon 54 | func (hIcon HICON) DestroyIcon() error { 55 | ret, _, err := syscall.SyscallN(_DestroyIcon.Addr(), 56 | uintptr(hIcon)) 57 | return utl.ZeroAsGetLastError(ret, err) 58 | } 59 | 60 | var _DestroyIcon = dll.User32.NewProc("DestroyIcon") 61 | 62 | // [GetIconInfo] function. 63 | // 64 | // ⚠️ You must defer [HBITMAP.DeleteObject] in HbmMask and HbmColor fields. 65 | // 66 | // [GetIconInfo]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-geticoninfo 67 | func (hIcon HICON) GetIconInfo() (ICONINFO, error) { 68 | var ii ICONINFO 69 | ret, _, err := syscall.SyscallN(_GetIconInfo.Addr(), 70 | uintptr(hIcon), 71 | uintptr(unsafe.Pointer(&ii))) 72 | if ret == 0 { 73 | return ICONINFO{}, co.ERROR(err) 74 | } 75 | return ii, nil 76 | } 77 | 78 | var _GetIconInfo = dll.User32.NewProc("GetIconInfo") 79 | 80 | // [GetIconInfoEx] function. 81 | // 82 | // ⚠️ You must defer [HBITMAP.DeleteObject] in HbmMask and HbmColor fields. 83 | // 84 | // [GetIconInfoEx]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-geticoninfoexw 85 | func (hIcon HICON) GetIconInfoEx() (ICONINFOEX, error) { 86 | var ii ICONINFOEX 87 | ii.SetCbSize() 88 | 89 | ret, _, _ := syscall.SyscallN(_GetIconInfoExW.Addr(), 90 | uintptr(hIcon), 91 | uintptr(unsafe.Pointer(&ii))) 92 | if ret == 0 { 93 | return ICONINFOEX{}, co.ERROR_INVALID_PARAMETER 94 | } 95 | return ii, nil 96 | } 97 | 98 | var _GetIconInfoExW = dll.User32.NewProc("GetIconInfoExW") 99 | -------------------------------------------------------------------------------- /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/internal/dll" 10 | "github.com/rodrigocfd/windigo/internal/utl" 11 | "github.com/rodrigocfd/windigo/win/co" 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(_MonitorFromPoint.Addr(), 24 | uintptr(utl.Make64(uint32(pt.X), uint32(pt.Y))), 25 | uintptr(flags)) 26 | return HMONITOR(ret) 27 | } 28 | 29 | var _MonitorFromPoint = dll.User32.NewProc("MonitorFromPoint") 30 | 31 | // [MonitorFromRect] function. 32 | // 33 | // [MonitorFromRect]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-monitorfromrect 34 | func MonitorFromRect(rc *RECT, flags co.MONITOR) HMONITOR { 35 | ret, _, _ := syscall.SyscallN(_MonitorFromRect.Addr(), 36 | uintptr(unsafe.Pointer(rc)), 37 | uintptr(flags)) 38 | return HMONITOR(ret) 39 | } 40 | 41 | var _MonitorFromRect = dll.User32.NewProc("MonitorFromRect") 42 | -------------------------------------------------------------------------------- /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/internal/dll" 10 | "github.com/rodrigocfd/windigo/internal/utl" 11 | "github.com/rodrigocfd/windigo/win/co" 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(_SetUserObjectInformationW.Addr(), 23 | uintptr(hProcess), 24 | uintptr(index), 25 | uintptr(info), 26 | uintptr(infoLen)) 27 | return utl.ZeroAsGetLastError(ret, err) 28 | } 29 | 30 | var _SetUserObjectInformationW = dll.User32.NewProc("SetUserObjectInformationW") 31 | -------------------------------------------------------------------------------- /win/user_hwnd_386.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/win/co" 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(_GetClassLongW.Addr(), 17 | uintptr(hWnd), 18 | uintptr(index)) 19 | if ret == 0 { 20 | return 0, co.ERROR(err) 21 | } 22 | return ret, nil 23 | } 24 | 25 | var _GetClassLongW = dll.User32.NewProc("GetClassLongW") 26 | 27 | // [GetWindowLong] function. 28 | // 29 | // [GetWindowLong]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowlongw 30 | func (hWnd HWND) GetWindowLongPtr(index co.GWLP) (uintptr, error) { 31 | ret, _, err := syscall.SyscallN(_GetWindowLongW.Addr(), 32 | uintptr(hWnd), 33 | uintptr(index)) 34 | if wErr := co.ERROR(err); ret == 0 && wErr != co.ERROR_SUCCESS { 35 | return 0, wErr 36 | } 37 | return ret, nil 38 | } 39 | 40 | var _GetWindowLongW = dll.User32.NewProc("GetWindowLongW") 41 | 42 | // [SetWindowLong] function. 43 | // 44 | // [SetWindowLong]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowlongw 45 | func (hWnd HWND) SetWindowLongPtr(index co.GWLP, newLong uintptr) (uintptr, error) { 46 | ret, _, err := syscall.SyscallN(_SetWindowLongW.Addr(), 47 | uintptr(hWnd), 48 | uintptr(index), 49 | uintptr(int32(newLong))) 50 | if wErr := co.ERROR(err); ret == 0 && wErr != co.ERROR_SUCCESS { 51 | return 0, wErr 52 | } 53 | return ret, nil 54 | } 55 | 56 | var _SetWindowLongW = dll.User32.NewProc("SetWindowLongW") 57 | -------------------------------------------------------------------------------- /win/user_hwnd_amd64.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/win/co" 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(_GetClassLongPtrW.Addr(), 17 | uintptr(hWnd), 18 | uintptr(index)) 19 | if ret == 0 { 20 | return 0, co.ERROR(err) 21 | } 22 | return ret, nil 23 | } 24 | 25 | var _GetClassLongPtrW = dll.User32.NewProc("GetClassLongPtrW") 26 | 27 | // [GetWindowLongPtr] function. 28 | // 29 | // [GetWindowLongPtr]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowlongptrw 30 | func (hWnd HWND) GetWindowLongPtr(index co.GWLP) (uintptr, error) { 31 | ret, _, err := syscall.SyscallN(_GetWindowLongPtrW.Addr(), 32 | uintptr(hWnd), 33 | uintptr(index)) 34 | if wErr := co.ERROR(err); ret == 0 && wErr != co.ERROR_SUCCESS { 35 | return 0, wErr 36 | } 37 | return ret, nil 38 | } 39 | 40 | var _GetWindowLongPtrW = dll.User32.NewProc("GetWindowLongPtrW") 41 | 42 | // [SetWindowLongPtr] function. 43 | // 44 | // [SetWindowLongPtr]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowlongptrw 45 | func (hWnd HWND) SetWindowLongPtr(index co.GWLP, newLong uintptr) (uintptr, error) { 46 | ret, _, err := syscall.SyscallN(_SetWindowLongPtrW.Addr(), 47 | uintptr(hWnd), 48 | uintptr(index), 49 | newLong) 50 | if wErr := co.ERROR(err); ret == 0 && wErr != co.ERROR_SUCCESS { 51 | return 0, wErr 52 | } 53 | return ret, nil 54 | } 55 | 56 | var _SetWindowLongPtrW = dll.User32.NewProc("SetWindowLongPtrW") 57 | -------------------------------------------------------------------------------- /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/win/co" 10 | "github.com/rodrigocfd/windigo/win/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{}, 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{}, 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.WstrSliceToStr(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/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(_IsAppThemed.Addr()) 16 | return ret != 0 17 | } 18 | 19 | var _IsAppThemed = dll.Uxtheme.NewProc("IsAppThemed") 20 | 21 | // [IsCompositionActive] function. 22 | // 23 | // [IsCompositionActive]: https://learn.microsoft.com/en-us/windows/win32/api/uxtheme/nf-uxtheme-iscompositionactive 24 | func IsCompositionActive() bool { 25 | ret, _, _ := syscall.SyscallN(_IsCompositionActive.Addr()) 26 | return ret != 0 27 | } 28 | 29 | var _IsCompositionActive = dll.Uxtheme.NewProc("IsCompositionActive") 30 | 31 | // [IsThemeActive] function. 32 | // 33 | // [IsThemeActive]: https://learn.microsoft.com/en-us/windows/win32/api/uxtheme/nf-uxtheme-isthemeactive 34 | func IsThemeActive() bool { 35 | ret, _, _ := syscall.SyscallN(_IsThemeActive.Addr()) 36 | return ret != 0 37 | } 38 | 39 | var _IsThemeActive = dll.Uxtheme.NewProc("IsThemeActive") 40 | -------------------------------------------------------------------------------- /win/uxtheme_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/win/co" 10 | "github.com/rodrigocfd/windigo/win/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(_IsThemeDialogTextureEnabled.Addr(), 18 | uintptr(hWnd)) 19 | return ret != 0 20 | } 21 | 22 | var _IsThemeDialogTextureEnabled = dll.Uxtheme.NewProc("IsThemeDialogTextureEnabled") 23 | 24 | // [OpenThemeData] function. 25 | // 26 | // ⚠️ You must defer [HTHEME.CloseThemeData]. 27 | // 28 | // [OpenThemeData]: https://learn.microsoft.com/en-us/windows/win32/api/uxtheme/nf-uxtheme-openthemedata 29 | func (hWnd HWND) OpenThemeData(classNames string) (HTHEME, error) { 30 | classNames16 := wstr.NewBufWith[wstr.Stack20](classNames, wstr.EMPTY_IS_NIL) 31 | ret, _, _ := syscall.SyscallN(_OpenThemeData.Addr(), 32 | uintptr(hWnd), 33 | uintptr(classNames16.UnsafePtr())) 34 | if ret == 0 { 35 | return HTHEME(0), co.HRESULT_E_FAIL 36 | } 37 | return HTHEME(ret), nil 38 | } 39 | 40 | var _OpenThemeData = dll.Uxtheme.NewProc("OpenThemeData") 41 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/internal/dll" 10 | "github.com/rodrigocfd/windigo/internal/utl" 11 | "github.com/rodrigocfd/windigo/win/co" 12 | "github.com/rodrigocfd/windigo/win/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 | fileName16 := wstr.NewBufWith[wstr.Stack20](fileName, wstr.EMPTY_IS_NIL) 33 | ret, _, err := syscall.SyscallN(_GetFileVersionInfoW.Addr(), 34 | uintptr(fileName16.UnsafePtr()), 35 | 0, 36 | uintptr(uint32(len(dest))), 37 | uintptr(unsafe.Pointer(&dest[0]))) 38 | return utl.ZeroAsGetLastError(ret, err) 39 | } 40 | 41 | var _GetFileVersionInfoW = dll.Version.NewProc("GetFileVersionInfoW") 42 | 43 | // [GetFileVersionInfoSize] function. 44 | // 45 | // [GetFileVersionInfoSize]: https://learn.microsoft.com/en-us/windows/win32/api/winver/nf-winver-getfileversioninfosizew 46 | func GetFileVersionInfoSize(fileName string) (uint, error) { 47 | fileName16 := wstr.NewBufWith[wstr.Stack20](fileName, wstr.EMPTY_IS_NIL) 48 | var dummy uint32 49 | 50 | ret, _, err := syscall.SyscallN(_GetFileVersionInfoSizeW.Addr(), 51 | uintptr(fileName16.UnsafePtr()), 52 | uintptr(unsafe.Pointer(&dummy))) 53 | if ret == 0 { 54 | return 0, co.ERROR(err) 55 | } 56 | return uint(ret), nil 57 | } 58 | 59 | var _GetFileVersionInfoSizeW = dll.Version.NewProc("GetFileVersionInfoSizeW") 60 | 61 | // [VerQueryValue] function. 62 | // 63 | // This is a low-level function, prefer using [VersionLoad]. 64 | // 65 | // # Example 66 | // 67 | // hInst, _ := win.GetModuleHandle("") 68 | // exeName, _ := hInst.GetModuleFileName() 69 | // szData, _ := win.GetFileVersionInfoSize(exeName) 70 | // 71 | // data := heap.NewVecSized(szData, byte(0)) 72 | // defer data.Free() 73 | // 74 | // win.GetFileVersionInfo(exeName, data.HotSlice()) 75 | // 76 | // if pNfoRaw, _, ok := win.VerQueryValue(data.HotSlice(), "\\"); ok { 77 | // pNfo := (*win.VS_FIXEDFILEINFO)(pNfoRaw) 78 | // println(pNfo.FileVersion()) 79 | // } 80 | // 81 | // type Block struct { 82 | // LangId win.LANGID 83 | // CodePage co.CP 84 | // } 85 | // 86 | // if pBlocks, count, ok := win.VerQueryValue( 87 | // data.HotSlice(), "\\VarFileInfo\\Translation"); ok { 88 | // 89 | // blocks := unsafe.Slice((*Block)(pBlocks), count) 90 | // for _, block := range blocks { 91 | // if pStr, nChars, ok := win.VerQueryValue(data.HotSlice(), 92 | // fmt.Sprintf("\\StringFileInfo\\%04x%04x\\%s", 93 | // block.LangId, block.CodePage, "ProductName")); ok { 94 | // 95 | // wideStr := unsafe.Slice((*uint16)(pStr), nChars) 96 | // str := wstr.WstrSliceToStr(wideStr) 97 | // println(str) 98 | // } 99 | // } 100 | // } 101 | // 102 | // [VerQueryValue]: https://learn.microsoft.com/en-us/windows/win32/api/winver/nf-winver-verqueryvaluew 103 | func VerQueryValue(block []byte, subBlock string) (unsafe.Pointer, uint, bool) { 104 | subBlock16 := wstr.NewBufWith[wstr.Stack20](subBlock, wstr.ALLOW_EMPTY) 105 | var lplpBuffer uintptr 106 | var puLen uint32 107 | 108 | ret, _, _ := syscall.SyscallN(_VerQueryValueW.Addr(), 109 | uintptr(unsafe.Pointer(&block[0])), 110 | uintptr(subBlock16.UnsafePtr()), 111 | uintptr(unsafe.Pointer(&lplpBuffer)), 112 | uintptr(unsafe.Pointer(&puLen))) 113 | if ret == 0 { 114 | return nil, 0, false 115 | } 116 | return unsafe.Pointer(lplpBuffer), uint(puLen), true 117 | } 118 | 119 | var _VerQueryValueW = dll.Version.NewProc("VerQueryValueW") 120 | -------------------------------------------------------------------------------- /win/version_structs.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package win 4 | 5 | import ( 6 | "github.com/rodrigocfd/windigo/internal/utl" 7 | "github.com/rodrigocfd/windigo/win/co" 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/wstr/Array.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package wstr 4 | 5 | import ( 6 | "unicode/utf8" 7 | "unsafe" 8 | ) 9 | 10 | // Converts multiple Go strings into null-terminated Windows UTF-16 strings, 11 | // stored in a single buffer, with a double terminating null. 12 | // 13 | // Uses short string optimization, allocating either on the stack or on the GC 14 | // heap. 15 | // 16 | // Don't move this object, otherwise you'll invalidate the stack pointer. 17 | // 18 | // Used internally by the library to make syscalls. 19 | // 20 | // [HeapAlloc]: https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapalloc 21 | type Array struct { 22 | buf Buf[Stack20] 23 | count uint // Number of stored strings. 24 | } 25 | 26 | // Converts multiple Go strings into null-terminated Windows UTF-16 strings, 27 | // stored in a single buffer, with a double terminating null. 28 | // 29 | // Uses short string optimization, allocating either on the stack or on the GC 30 | // heap. 31 | // 32 | // Don't move this object, otherwise you'll invalidate the stack pointer. 33 | // 34 | // Used internally by the library to make syscalls. 35 | // 36 | // # Example 37 | // 38 | // buf := wstr.NewArray("abc", "def") 39 | func NewArray(strs ...string) Array { 40 | me := Array{ 41 | buf: NewBuf[Stack20](), 42 | count: 0, 43 | } 44 | me.Append(strs...) 45 | return me 46 | } 47 | 48 | // Appends the given strings to the buffer. 49 | // 50 | // Since the pointers are invalidated when the data is changed, first insert all 51 | // the strings, then grab their pointers. 52 | func (me *Array) Append(strs ...string) { 53 | if len(strs) == 0 { 54 | return 55 | } 56 | 57 | if me.count > 1 { 58 | me.buf.Resize(me.buf.Len() - 1) // if we have more than 1 string, remove double terminating null 59 | } 60 | curSz := me.buf.Len() 61 | 62 | neededSz := uint(0) 63 | for _, str := range strs { 64 | neededSz += uint(utf8.RuneCountInString(str)) + 1 // room for terminating null 65 | } 66 | if me.count+uint(len(strs)) > 1 { 67 | neededSz += 1 // double terminating null if multiple strings 68 | } 69 | me.buf.Resize(me.buf.Len() + neededSz) 70 | 71 | dest := me.buf.HotSlice()[curSz:] // slice to receive the strings 72 | for _, str := range strs { 73 | StrToWstrBuf(str, dest) // empty strings are also added 74 | strLen := utf8.RuneCountInString(str) 75 | dest = dest[strLen+1:] // advance the slice to receive the next string 76 | } 77 | 78 | me.count += uint(len(strs)) 79 | if me.count > 1 { 80 | dest[0] = 0x0000 // double terminating null if we have multiple strings 81 | } 82 | } 83 | 84 | // Returns the number of strings actually stored. 85 | func (me *Array) Count() uint { 86 | return me.count 87 | } 88 | 89 | // Returns the pointer to the given stored string, or nil if out of bounds. 90 | // 91 | // If the data is changed for whathever reason – like by adding an element –, 92 | // the pointer will be no longer valid. 93 | func (me *Array) PtrOf(index uint) *uint16 { 94 | if me.count == 0 { 95 | return nil 96 | } else if index == 0 { // first string is simply the beginning of the block 97 | return me.buf.At(0) 98 | } else { // skip each terminating null until we reach the desired index 99 | iNextStr := uint(1) 100 | for i := uint(0); i < me.buf.Len(); i++ { 101 | if *me.buf.At(i) == 0x0000 { // found a terminating null 102 | if iNextStr == index { // this is the index we're after 103 | return me.buf.At(i + 1) // 1st char past null 104 | } else { 105 | iNextStr++ 106 | } 107 | } 108 | } 109 | return nil // index out of bounds 110 | } 111 | } 112 | 113 | // Returns the pointer to the given stored string, or nil if out of bounds. 114 | // 115 | // If the data is changed for whathever reason – like by adding an element –, 116 | // the pointer will be no longer valid. 117 | func (me *Array) UnsafePtrOf(index uint) unsafe.Pointer { 118 | return unsafe.Pointer(me.PtrOf(index)) 119 | } 120 | --------------------------------------------------------------------------------