go-toast

Send toast notifications in Windows
Log | Files | Refs | README | LICENSE

commit eb13cd60ae70d0644ffb07df9a7b7b3c65a8c57e
parent ec054438dfbb34bc52ea089e49838f42505ccf2b
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date:   Tue,  5 Dec 2023 16:59:50 +0800

winrt: add auto-generated COM definitions

This includes the definitions we need, and is better than hand writing them.

Signed-off-by: Jack Mordaunt <jackmordaunt.dev@gmail.com>

Diffstat:
Ainternal/kernel32/kernel32.go | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainternal/winrt/data/xml/dom/xmldocument.go | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainternal/winrt/ui/notifications/toastnotification.go | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainternal/winrt/ui/notifications/toastnotificationmanager.go | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainternal/winrt/ui/notifications/toastnotificationmanagerforuser.go | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainternal/winrt/ui/notifications/toastnotifier.go | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 424 insertions(+), 0 deletions(-)

diff --git a/internal/kernel32/kernel32.go b/internal/kernel32/kernel32.go @@ -0,0 +1,78 @@ +//go:build windows + +package kernel32 + +import ( + "sync/atomic" + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +type ( + heapHandle = uintptr + win32Error uint32 + heapFlags uint32 +) + +const ( + heapNone heapFlags = 0 + heapZeroMemory heapFlags = 8 // The allocated memory will be initialized to zero. +) + +var ( + libKernel32 = windows.NewLazySystemDLL("kernel32.dll") + + pHeapFree uintptr + pHeapAlloc uintptr + pGetProcessHeap uintptr + + hHeap heapHandle +) + +func init() { + hHeap, _ = getProcessHeap() +} + +// Malloc allocates the given amount of bytes in the heap +func Malloc(size uintptr) unsafe.Pointer { + return heapAlloc(hHeap, heapZeroMemory, size) +} + +// Free releases the given unsafe pointer from the heap +func Free(inst unsafe.Pointer) { + _, _ = heapFree(hHeap, heapNone, inst) +} + +// https://docs.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapalloc +func heapAlloc(hHeap heapHandle, dwFlags heapFlags, dwBytes uintptr) unsafe.Pointer { + addr := getProcAddr(&pHeapAlloc, libKernel32, "HeapAlloc") + allocatedPtr, _, _ := syscall.SyscallN(addr, hHeap, uintptr(dwFlags), dwBytes) + // Since this pointer is allocated in the heap by Windows, it will never be + // GCd by Go, so this is a safe operation. + // But linter thinks it is not (probably because we are not using CGO) and fails. + return unsafe.Pointer(allocatedPtr) //nolint:gosec,govet +} + +// https://docs.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapfree +func heapFree(hHeap heapHandle, dwFlags heapFlags, lpMem unsafe.Pointer) (bool, win32Error) { + addr := getProcAddr(&pHeapFree, libKernel32, "HeapFree") + ret, _, err := syscall.SyscallN(addr, hHeap, uintptr(dwFlags), uintptr(lpMem)) + return ret == 0, win32Error(err) +} + +func getProcessHeap() (heapHandle, win32Error) { + addr := getProcAddr(&pGetProcessHeap, libKernel32, "GetProcessHeap") + ret, _, err := syscall.SyscallN(addr) + return ret, win32Error(err) +} + +func getProcAddr(pAddr *uintptr, lib *windows.LazyDLL, procName string) uintptr { + addr := atomic.LoadUintptr(pAddr) + if addr == 0 { + addr = lib.NewProc(procName).Addr() + atomic.StoreUintptr(pAddr, addr) + } + return addr +} diff --git a/internal/winrt/data/xml/dom/xmldocument.go b/internal/winrt/data/xml/dom/xmldocument.go @@ -0,0 +1,73 @@ +// Code generated by winrt-go-gen. DO NOT EDIT. + +//go:build windows + +//nolint:all +package dom + +import ( + "syscall" + "unsafe" + + "github.com/go-ole/go-ole" +) + +const SignatureXmlDocument string = "rc(Windows.Data.Xml.Dom.XmlDocument;{f7f3a506-1e87-42d6-bcfb-b8c809fa5494})" + +type XmlDocument struct { + ole.IUnknown +} + +func NewXmlDocument() (*XmlDocument, error) { + inspectable, err := ole.RoActivateInstance("Windows.Data.Xml.Dom.XmlDocument") + if err != nil { + return nil, err + } + return (*XmlDocument)(unsafe.Pointer(inspectable)), nil +} + +func (impl *XmlDocument) LoadXml(xml string) error { + itf := impl.MustQueryInterface(ole.NewGUID(GUIDiXmlDocumentIO)) + defer itf.Release() + v := (*iXmlDocumentIO)(unsafe.Pointer(itf)) + return v.LoadXml(xml) +} + +const ( + GUIDiXmlDocumentIO string = "6cd0e74e-ee65-4489-9ebf-ca43e87ba637" + SignatureiXmlDocumentIO string = "{6cd0e74e-ee65-4489-9ebf-ca43e87ba637}" +) + +type iXmlDocumentIO struct { + ole.IInspectable +} + +type iXmlDocumentIOVtbl struct { + ole.IInspectableVtbl + + LoadXml uintptr + LoadXmlWithSettings uintptr + SaveToFileAsync uintptr +} + +func (v *iXmlDocumentIO) VTable() *iXmlDocumentIOVtbl { + return (*iXmlDocumentIOVtbl)(unsafe.Pointer(v.RawVTable)) +} + +func (v *iXmlDocumentIO) LoadXml(xml string) error { + xmlHStr, err := ole.NewHString(xml) + if err != nil { + return err + } + hr, _, _ := syscall.SyscallN( + v.VTable().LoadXml, + uintptr(unsafe.Pointer(v)), // this + uintptr(xmlHStr), // in string + ) + + if hr != 0 { + return ole.NewError(hr) + } + + return nil +} diff --git a/internal/winrt/ui/notifications/toastnotification.go b/internal/winrt/ui/notifications/toastnotification.go @@ -0,0 +1,88 @@ +// Code generated by winrt-go-gen. DO NOT EDIT. + +//go:build windows + +//nolint:all +package notifications + +import ( + "syscall" + "unsafe" + + "git.sr.ht/~jackmordaunt/go-toast/internal/winrt/data/xml/dom" + "github.com/go-ole/go-ole" +) + +const SignatureToastNotification string = "rc(Windows.UI.Notifications.ToastNotification;{997e2675-059e-4e60-8b06-1760917c8b80})" + +func CreateToastNotification(content *dom.XmlDocument) (*ToastNotification, error) { + inspectable, err := ole.RoGetActivationFactory("Windows.UI.Notifications.ToastNotification", ole.NewGUID(GUIDiToastNotificationFactory)) + if err != nil { + return nil, err + } + v := (*iToastNotificationFactory)(unsafe.Pointer(inspectable)) + + var out *ToastNotification + hr, _, _ := syscall.SyscallN( + v.VTable().CreateToastNotification, + 0, // this is a static func, so there's no this + uintptr(unsafe.Pointer(content)), // in dom.XmlDocument + uintptr(unsafe.Pointer(&out)), // out ToastNotification + ) + + if hr != 0 { + return nil, ole.NewError(hr) + } + + return out, nil +} + +type ToastNotification struct { + ole.IUnknown +} + +const ( + GUIDiToastNotification string = "997e2675-059e-4e60-8b06-1760917c8b80" + SignatureiToastNotification string = "{997e2675-059e-4e60-8b06-1760917c8b80}" +) + +type iToastNotification struct { + ole.IInspectable +} + +type iToastNotificationVtbl struct { + ole.IInspectableVtbl + + GetContent uintptr + SetExpirationTime uintptr + GetExpirationTime uintptr + AddDismissed uintptr + RemoveDismissed uintptr + AddActivated uintptr + RemoveActivated uintptr + AddFailed uintptr + RemoveFailed uintptr +} + +func (v *iToastNotification) VTable() *iToastNotificationVtbl { + return (*iToastNotificationVtbl)(unsafe.Pointer(v.RawVTable)) +} + +const ( + GUIDiToastNotificationFactory string = "04124b20-82c6-4229-b109-fd9ed4662b53" + SignatureiToastNotificationFactory string = "{04124b20-82c6-4229-b109-fd9ed4662b53}" +) + +type iToastNotificationFactory struct { + ole.IInspectable +} + +type iToastNotificationFactoryVtbl struct { + ole.IInspectableVtbl + + CreateToastNotification uintptr +} + +func (v *iToastNotificationFactory) VTable() *iToastNotificationFactoryVtbl { + return (*iToastNotificationFactoryVtbl)(unsafe.Pointer(v.RawVTable)) +} diff --git a/internal/winrt/ui/notifications/toastnotificationmanager.go b/internal/winrt/ui/notifications/toastnotificationmanager.go @@ -0,0 +1,53 @@ +// Code generated by winrt-go-gen. DO NOT EDIT. + +//go:build windows + +//nolint:all +package notifications + +import ( + "syscall" + "unsafe" + + "github.com/go-ole/go-ole" +) + +const ( + GUIDiToastNotificationManagerStatics5 string = "d6f5f569-d40d-407c-8989-88cab42cfd14" + SignatureiToastNotificationManagerStatics5 string = "{d6f5f569-d40d-407c-8989-88cab42cfd14}" +) + +type iToastNotificationManagerStatics5 struct { + ole.IInspectable +} + +type iToastNotificationManagerStatics5Vtbl struct { + ole.IInspectableVtbl + + GetDefault uintptr +} + +func (v *iToastNotificationManagerStatics5) VTable() *iToastNotificationManagerStatics5Vtbl { + return (*iToastNotificationManagerStatics5Vtbl)(unsafe.Pointer(v.RawVTable)) +} + +func GetDefault() (*ToastNotificationManagerForUser, error) { + inspectable, err := ole.RoGetActivationFactory("Windows.UI.Notifications.ToastNotificationManager", ole.NewGUID(GUIDiToastNotificationManagerStatics5)) + if err != nil { + return nil, err + } + v := (*iToastNotificationManagerStatics5)(unsafe.Pointer(inspectable)) + + var out *ToastNotificationManagerForUser + hr, _, _ := syscall.SyscallN( + v.VTable().GetDefault, + 0, // this is a static func, so there's no this + uintptr(unsafe.Pointer(&out)), // out ToastNotificationManagerForUser + ) + + if hr != 0 { + return nil, ole.NewError(hr) + } + + return out, nil +} diff --git a/internal/winrt/ui/notifications/toastnotificationmanagerforuser.go b/internal/winrt/ui/notifications/toastnotificationmanagerforuser.go @@ -0,0 +1,68 @@ +// Code generated by winrt-go-gen. DO NOT EDIT. + +//go:build windows + +//nolint:all +package notifications + +import ( + "syscall" + "unsafe" + + "github.com/go-ole/go-ole" +) + +const SignatureToastNotificationManagerForUser string = "rc(Windows.UI.Notifications.ToastNotificationManagerForUser;{79ab57f6-43fe-487b-8a7f-99567200ae94})" + +type ToastNotificationManagerForUser struct { + ole.IUnknown +} + +func (impl *ToastNotificationManagerForUser) CreateToastNotifierWithId(applicationId string) (*ToastNotifier, error) { + itf := impl.MustQueryInterface(ole.NewGUID(GUIDiToastNotificationManagerForUser)) + defer itf.Release() + v := (*iToastNotificationManagerForUser)(unsafe.Pointer(itf)) + return v.CreateToastNotifierWithId(applicationId) +} + +const ( + GUIDiToastNotificationManagerForUser string = "79ab57f6-43fe-487b-8a7f-99567200ae94" + SignatureiToastNotificationManagerForUser string = "{79ab57f6-43fe-487b-8a7f-99567200ae94}" +) + +type iToastNotificationManagerForUser struct { + ole.IInspectable +} + +type iToastNotificationManagerForUserVtbl struct { + ole.IInspectableVtbl + + CreateToastNotifier uintptr + CreateToastNotifierWithId uintptr + GetHistory uintptr + GetUser uintptr +} + +func (v *iToastNotificationManagerForUser) VTable() *iToastNotificationManagerForUserVtbl { + return (*iToastNotificationManagerForUserVtbl)(unsafe.Pointer(v.RawVTable)) +} + +func (v *iToastNotificationManagerForUser) CreateToastNotifierWithId(applicationId string) (*ToastNotifier, error) { + var out *ToastNotifier + applicationIdHStr, err := ole.NewHString(applicationId) + if err != nil { + return nil, err + } + hr, _, _ := syscall.SyscallN( + v.VTable().CreateToastNotifierWithId, + uintptr(unsafe.Pointer(v)), // this + uintptr(applicationIdHStr), // in string + uintptr(unsafe.Pointer(&out)), // out ToastNotifier + ) + + if hr != 0 { + return nil, ole.NewError(hr) + } + + return out, nil +} diff --git a/internal/winrt/ui/notifications/toastnotifier.go b/internal/winrt/ui/notifications/toastnotifier.go @@ -0,0 +1,64 @@ +// Code generated by winrt-go-gen. DO NOT EDIT. + +//go:build windows + +//nolint:all +package notifications + +import ( + "syscall" + "unsafe" + + "github.com/go-ole/go-ole" +) + +const SignatureToastNotifier string = "rc(Windows.UI.Notifications.ToastNotifier;{75927b93-03f3-41ec-91d3-6e5bac1b38e7})" + +type ToastNotifier struct { + ole.IUnknown +} + +func (impl *ToastNotifier) Show(notification *ToastNotification) error { + itf := impl.MustQueryInterface(ole.NewGUID(GUIDiToastNotifier)) + defer itf.Release() + v := (*iToastNotifier)(unsafe.Pointer(itf)) + return v.Show(notification) +} + +const ( + GUIDiToastNotifier string = "75927b93-03f3-41ec-91d3-6e5bac1b38e7" + SignatureiToastNotifier string = "{75927b93-03f3-41ec-91d3-6e5bac1b38e7}" +) + +type iToastNotifier struct { + ole.IInspectable +} + +type iToastNotifierVtbl struct { + ole.IInspectableVtbl + + Show uintptr + Hide uintptr + GetSetting uintptr + AddToSchedule uintptr + RemoveFromSchedule uintptr + GetScheduledToastNotifications uintptr +} + +func (v *iToastNotifier) VTable() *iToastNotifierVtbl { + return (*iToastNotifierVtbl)(unsafe.Pointer(v.RawVTable)) +} + +func (v *iToastNotifier) Show(notification *ToastNotification) error { + hr, _, _ := syscall.SyscallN( + v.VTable().Show, + uintptr(unsafe.Pointer(v)), // this + uintptr(unsafe.Pointer(notification)), // in ToastNotification + ) + + if hr != 0 { + return ole.NewError(hr) + } + + return nil +}