commit 96c735044b149648fbe6051b9137b6db2dd3c4ef
parent eb13cd60ae70d0644ffb07df9a7b7b3c65a8c57e
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date: Tue, 5 Dec 2023 18:42:52 +0800
wintoast: use generated COM definitions
This commit removes the hand-written COM definitions as much
as possible.
However we are stuck with defining the _undocumented_ INotificationActivationCallback
and implementing it by hand.
The implemetnations get a cleanup here. The lifetime of our COM objects
are static, so we can remove all the locking and reference counting.
In addition, we can totally eschew Malloc/Free and just pin the objects
using [runtime.Pinner], deferring allocation to the Go runtime.
The end result is dramatically simplified.
Signed-off-by: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Diffstat:
4 files changed, 124 insertions(+), 462 deletions(-)
diff --git a/wintoast/bind_windows.go b/wintoast/bind_windows.go
@@ -13,6 +13,8 @@ import (
"unicode/utf16"
"unsafe"
+ "git.sr.ht/~jackmordaunt/go-toast/internal/winrt/data/xml/dom"
+ "git.sr.ht/~jackmordaunt/go-toast/internal/winrt/ui/notifications"
"git.sr.ht/~jackmordaunt/go-toast/tmpl"
"github.com/go-ole/go-ole"
)
@@ -61,36 +63,36 @@ func pushCOM(xml string) error {
return err
}
- // 1. allocate ClassFactory implementation.
- // 2. register ClassFactory implementation (provides our ActivationCallback to the runtime)
- // 3. load noti manager (statics impl)
- // 4. load noti factory
- // 5. create xml
- // 6. create xmlIO
- // 7. load xml
- // 8. create noti
-
- classFactory := newClassFactory()
- if classFactory == nil {
- return fmt.Errorf("could not allocate class factory")
+ if err := registerClassFactory(ClassFactory); err != nil {
+ return fmt.Errorf("registering class factory: %w", err)
}
- if err := registerClassFactory(classFactory); err != nil {
- return fmt.Errorf("registering class factory: %w", err)
+ doc, err := dom.NewXmlDocument()
+ if err != nil {
+ return fmt.Errorf("dom.NewXmlDocument(): %w", err)
+ }
+
+ if err := doc.LoadXml(xml); err != nil {
+ return fmt.Errorf("doc.LoadXml(tmpl): %w", err)
+ }
+
+ manager, err := notifications.GetDefault()
+ if err != nil {
+ return fmt.Errorf("notifications.GetDefault(): %w", err)
}
- noti, err := newNotiFromXml(xml)
+ notifier, err := manager.CreateToastNotifierWithId(appData.AppID)
if err != nil {
- return fmt.Errorf("building notification: %w", err)
+ return fmt.Errorf("manager.CreateToastNotifier(): %w", err)
}
- notifier, err := newNotifier(appData.AppID)
+ toast, err := notifications.CreateToastNotification(doc)
if err != nil {
- return fmt.Errorf("building notifier: %w", err)
+ return fmt.Errorf("notifications.CreateToastNotification(doc): %w", err)
}
- if err := notifier.Show(noti); err != nil {
- return fmt.Errorf("showing notification: %w", err)
+ if err := notifier.Show(toast); err != nil {
+ return fmt.Errorf("notifier.Show(): %w", err)
}
return nil
@@ -129,8 +131,10 @@ func setAppData(data AppData) (err error) {
return nil
}
-var initLock sync.Mutex
-var didInitialize bool
+var (
+ initLock sync.Mutex
+ didInitialize bool
+)
// initialize attempts to initialize the Windows Runtime.
// Each invocation will retry RoInitialize until a successful initialization
@@ -153,78 +157,8 @@ func initialize() (err error) {
return nil
}
-// newNotifier builds an IToastNotifier instance using the given appID.
-func newNotifier(appID string) (*IToastNotifier, error) {
- managerObject, err := ole.RoGetActivationFactory(CLSID_ToastNotificationManager, IID_ToastNotificationManager)
- if err != nil {
- return nil, fmt.Errorf("getting activation factory: %w", err)
- }
-
- // Get access to the manager vtable.
- manager := (*IToastNotificationManager)(unsafe.Pointer(managerObject))
-
- notifier, err := manager.CreateToastNotifierWithID(appID)
- if err != nil {
- return nil, fmt.Errorf("creating toast notifier: %w", err)
- }
-
- return notifier, nil
-}
-
-// newNotiFromXml builds an IToastNotification instance from the given xml content.
-func newNotiFromXml(xml string) (*IToastNotification, error) {
- factoryObject, err := ole.RoGetActivationFactory(CLSID_ToastNotification, IID_ToastNotificationFactory)
- if err != nil {
- return nil, fmt.Errorf("getting activation factory: %w", err)
- }
-
- // Get access to the factory vtable.
- factory := (*IToastNotificationFactory)(unsafe.Pointer(factoryObject))
-
- xmlDoc, err := loadXML(xml)
- if err != nil {
- return nil, fmt.Errorf("loading xml: %w", err)
- }
-
- noti, err := factory.CreateToastNotification(xmlDoc)
- if err != nil {
- return nil, fmt.Errorf("creating toast notification: %w", err)
- }
-
- return noti, nil
-}
-
-// loadXML allocates an XML document object (returned as IDispatch because we don't care
-// about representing it's vtable).
-func loadXML(xml string) (*ole.IDispatch, error) {
- xmlDocObject, err := ole.RoActivateInstance(CLSID_XMLDocument)
- if err != nil {
- return nil, fmt.Errorf("RoActivateInstance: %w", err)
- }
-
- xmlDoc, err := xmlDocObject.QueryInterface(IID_IXmlDocument)
- if err != nil {
- return nil, fmt.Errorf("querying IID_IXmlDocument: %w", err)
- }
-
- xmlDocIO, err := xmlDoc.QueryInterface(IID_IXmlDocumentIO)
- if err != nil {
- return nil, fmt.Errorf("querying interface IID_IXmlDocumentIO: %w", err)
- }
-
- // Get access to the IO vtable.
- xmlIO := (*IXMLDocumentIO)(unsafe.Pointer(xmlDocIO))
-
- if err := xmlIO.LoadXml(xml); err != nil {
- return nil, fmt.Errorf("IXmlDocumentIO.LoadXml: %w", err)
- }
-
- return xmlDoc, nil
-}
-
// sliceUserDataFromUnsafe builds a slice of UserData out of an unsafe pointer.
func sliceUserDataFromUnsafe(ptr unsafe.Pointer, count int) []UserData {
-
// Layout mirrors the memory layout of the C struct that contains this data.
// I'm not sure if there's special alignment or packing - though I don't notice
// anything in the definition to indicate as such.
diff --git a/wintoast/impl.go b/wintoast/impl.go
@@ -10,38 +10,97 @@
// are built at runtime. They are declared globally because `syscall.NewCallback` never
// releases the memory it allocates for the functions thus causing an unsolvable memory
// leak if we were to allocate these per-notification.
+//
+// The other COM interfaces we are interacting with are auto-generated from metadata.
+// However the INotificationActivationCallback is undocumented, so we have to define
+// it entirely ourselves.
+//
+// The definitions are derived from:
+// - <combase.h>
+// - <NotificationActivationCallback.h>
package wintoast
import (
- "sync"
+ "runtime"
"syscall"
"unsafe"
"github.com/go-ole/go-ole"
)
+// Interface GUIDS. These GUIDS are predefined by the Windows Runtime, identifying the various
+// interfaces we want to make use of.
+var (
+ IID_IClassFactory = ole.NewGUID("{00000001-0000-0000-C000-000000000046}")
+ IID_INotificationActivationCallback = ole.NewGUID("{53E31837-6600-4A81-9395-75CFFE746F94}")
+)
+
+// This default GUID is for our implementation.
+// This was generated and should not collide with any other GUID.
+// It's preferable for the application to override this value with its own generated GUID.
+var GUID_ImplNotificationActivationCallback = ole.NewGUID("{0F82E845-CB89-4039-BDBF-67CA33254C76}")
+
+type (
+ // IClassFactory defines the factory that builds our INotificationActivationCallback instance.
+ // Windows Runtime loves factories.
+ IClassFactory struct {
+ VTable *IClassFactoryVtbl
+ }
+
+ IClassFactoryVtbl struct {
+ ole.IUnknownVtbl
+ CreateInstance uintptr
+ LockServer uintptr
+ }
+)
+
+type (
+ // INotificationActivationCallback receives activations from toast notifications.
+ INotificationActivationCallback struct {
+ VTable *INotificationActivationCallbackVtbl
+ }
+
+ INotificationActivationCallbackVtbl struct {
+ ole.IUnknownVtbl
+ Activate uintptr
+ }
+)
+
+/*
+ Strictly speaking we shouldn't need to pin the static objects. They
+ are package-globals and wont be garabge collected. No harm in being
+ extra careful, though.
+*/
+
+var pinner runtime.Pinner
+
+func init() {
+ pinner.Pin(ClassFactory)
+ pinner.Pin(ClassFactory.VTable)
+ pinner.Pin(NotificationActivationCallback)
+ pinner.Pin(NotificationActivationCallback.VTable)
+}
+
// Static implementations for the IClassFactory.
-// syscall.NewCallback never releases its memory, so we pay that price once.
var (
- // factoryLock protects the IClassFactory reference counting.
- factoryLock sync.Mutex
+ ClassFactory = &IClassFactory{
+ VTable: &IClassFactoryVtbl{
+ IUnknownVtbl: ole.IUnknownVtbl{
+ QueryInterface: IClassFactory_QueryInterface,
+ AddRef: IClassFactory_AddRef,
+ Release: IClassFactory_Release,
+ },
+ LockServer: IClassFactory_LockServer,
+ CreateInstance: IClassFactory_CreateInstance,
+ },
+ }
IClassFactory_AddRef = syscall.NewCallback(func(this *IClassFactory) (re uintptr) {
- factoryLock.Lock()
- defer factoryLock.Unlock()
- this.RefCount += 1
- return uintptr(this.RefCount)
+ return uintptr(1)
})
IClassFactory_Release = syscall.NewCallback(func(this *IClassFactory) (re uintptr) {
- factoryLock.Lock()
- defer factoryLock.Unlock()
- this.RefCount -= 1
- if this.RefCount == 0 {
- free(unsafe.Pointer(this.lpVtbl))
- free(unsafe.Pointer(this))
- }
- return uintptr(this.RefCount)
+ return uintptr(1)
})
IClassFactory_QueryInterface = syscall.NewCallback(func(this *IClassFactory, riid *ole.GUID, out unsafe.Pointer) (re uintptr) {
@@ -50,8 +109,7 @@ var (
return ole.E_NOINTERFACE
}
*(**IClassFactory)(out) = this
- this.AddRef()
- return uintptr(ole.S_OK)
+ return ole.S_OK
})
IClassFactory_LockServer = syscall.NewCallback(func(this *IClassFactory, flock uintptr) (ret uintptr) {
@@ -63,39 +121,34 @@ var (
// Should be CLASS_E_NOAGGREGATION but ole doesn't define this.
return ole.E_NOINTERFACE
}
- object := newNotificationActivationCallback()
- if object == nil {
- return ole.E_OUTOFMEMORY
+ if !ole.IsEqualGUID(riid, IID_INotificationActivationCallback) &&
+ !ole.IsEqualGUID(riid, ole.IID_IUnknown) {
+ return ole.E_NOINTERFACE
}
- object.RefCount = 1
- hr := object.QueryInterface(riid, out)
- object.Release()
- return uintptr(hr)
+ *(**INotificationActivationCallback)(out) = NotificationActivationCallback
+ return ole.S_OK
})
)
// Static implementations for the INotificationActivationCallback.
-// syscall.NewCallback never releases its memory, so we pay that price once.
var (
- // callbackLock protects the ActivationCallback reference counting.
- callbackLock sync.Mutex
+ NotificationActivationCallback = &INotificationActivationCallback{
+ VTable: &INotificationActivationCallbackVtbl{
+ IUnknownVtbl: ole.IUnknownVtbl{
+ QueryInterface: INotificationActivationCallback_QueryInterface,
+ AddRef: INotificationActivationCallback_AddRef,
+ Release: INotificationActivationCallback_Release,
+ },
+ Activate: INotificationActivationCallback_Activate,
+ },
+ }
INotificationActivationCallback_AddRef = syscall.NewCallback(func(this *INotificationActivationCallback) (re uintptr) {
- callbackLock.Lock()
- defer callbackLock.Unlock()
- this.RefCount += 1
- return uintptr(this.RefCount)
+ return uintptr(1)
})
INotificationActivationCallback_Release = syscall.NewCallback(func(this *INotificationActivationCallback) (re uintptr) {
- callbackLock.Lock()
- defer callbackLock.Unlock()
- this.RefCount -= 1
- if this.RefCount == 0 {
- free(unsafe.Pointer(this.lpVtbl))
- free(unsafe.Pointer(this))
- }
- return uintptr(this.RefCount)
+ return uintptr(1)
})
INotificationActivationCallback_QueryInterface = syscall.NewCallback(func(this *INotificationActivationCallback, riid *ole.GUID, out unsafe.Pointer) (re uintptr) {
@@ -104,14 +157,14 @@ var (
return ole.E_NOINTERFACE
}
*(**INotificationActivationCallback)(out) = this
- this.AddRef()
- return uintptr(ole.S_OK)
+ return ole.S_OK
})
+ // Activate is our re-entrance into Go from Windows. This is the magic.
INotificationActivationCallback_Activate = syscall.NewCallback(func(
- this,
- appUserModelId,
- invokedArgs,
+ this unsafe.Pointer,
+ appUserModelId unsafe.Pointer,
+ invokedArgs unsafe.Pointer,
data unsafe.Pointer,
count uint32,
) (ret uintptr) {
@@ -123,54 +176,3 @@ var (
return
})
)
-
-// newClassFactory allocates our ClassFactory that can build our NotificationActivationCallback object.
-func newClassFactory() *IClassFactory {
-
- // Allocate the object and its vtable.
-
- v := (*IClassFactory)(malloc(unsafe.Sizeof(IClassFactory{})))
- if v == nil {
- return nil
- }
-
- v.lpVtbl = (*IClassFactoryVtbl)(malloc(unsafe.Sizeof(IClassFactoryVtbl{})))
- if v.lpVtbl == nil {
- return nil
- }
-
- // Provide function implementations in the Vtable.
-
- v.lpVtbl.AddRef = IClassFactory_AddRef
- v.lpVtbl.Release = IClassFactory_Release
- v.lpVtbl.QueryInterface = IClassFactory_QueryInterface
- v.lpVtbl.LockServer = IClassFactory_LockServer
- v.lpVtbl.CreateInstance = IClassFactory_CreateInstance
-
- return v
-}
-
-// newNotificationActivationCallback allocates our implementation of the INotificationActivationCallback.
-func newNotificationActivationCallback() *INotificationActivationCallback {
-
- // Allocate the object and its vtable.
-
- v := (*INotificationActivationCallback)(malloc(unsafe.Sizeof(INotificationActivationCallback{})))
- if v == nil {
- return nil
- }
-
- v.lpVtbl = (*INotificationActivationCallbackVtbl)(malloc(unsafe.Sizeof(INotificationActivationCallbackVtbl{})))
- if v.lpVtbl == nil {
- return nil
- }
-
- // Provide function implementations in the vtable.
-
- v.lpVtbl.AddRef = INotificationActivationCallback_AddRef
- v.lpVtbl.Release = INotificationActivationCallback_Release
- v.lpVtbl.QueryInterface = INotificationActivationCallback_QueryInterface
- v.lpVtbl.Activate = INotificationActivationCallback_Activate
-
- return v
-}
diff --git a/wintoast/interfaces.go b/wintoast/interfaces.go
@@ -1,244 +0,0 @@
-//go:build windows
-
-// This file contains the various COM interfaces we need to call.
-// Only the methods we need to call have wrappers. All methods
-// that don't have a corresponding Go wrapper method are marked
-// as such.
-//
-// The definitions are derived from:
-// - <combase.h>
-// - <windows.ui.notifications.h
-// - <NotificationActivationCallback.h>
-package wintoast
-
-import (
- "fmt"
- "syscall"
- "unsafe"
-
- "github.com/go-ole/go-ole"
-)
-
-// Runtime class names. These correspond to normal COM GUIDs, however the Windows Runtime likes
-// to use string identifiers and derives the GUIDs under the hood.
-const (
- CLSID_ToastNotificationManager = "Windows.UI.Notifications.ToastNotificationManager"
- CLSID_ToastNotification = "Windows.UI.Notifications.ToastNotification"
- CLSID_XMLDocument = "Windows.Data.Xml.Dom.XmlDocument"
-)
-
-// Interface GUIDS. These GUIDS are predefined by the Windows Runtime, identifying the various
-// interfaces we want to make use of.
-var (
- IID_IClassFactory = ole.NewGUID("{00000001-0000-0000-C000-000000000046}")
- IID_INotificationActivationCallback = ole.NewGUID("{53E31837-6600-4A81-9395-75CFFE746F94}")
- IID_ToastNotificationManager = ole.NewGUID("{50AC103F-D235-4598-BBEF-98FE4D1A3AD4}")
- IID_ToastNotificationFactory = ole.NewGUID("{04124B20-82C6-4229-B109-FD9ED4662B53}")
- IID_IXmlDocument = ole.NewGUID("{F7F3A506-1E87-42D6-BCFB-B8C809FA5494}")
- IID_IXmlDocumentIO = ole.NewGUID("{6CD0E74E-EE65-4489-9EBF-CA43E87BA637}")
-)
-
-// This default GUID is for our implementation.
-// This was generated and should not collide with any other GUID.
-var GUID_ImplNotificationActivationCallback = ole.NewGUID("{0F82E845-CB89-4039-BDBF-67CA33254C76}")
-
-// HRESULT represents a COM return value.
-type HRESULT uintptr
-
-// IToastNotification represents a single toast notification instance.
-type IToastNotification struct {
- lpVtbl *IToastNotificationVtbl
-}
-
-type IToastNotificationVtbl struct {
- ole.IInspectableVtbl
-}
-
-// IToastNotificationManager can create toast notifier objects.
-type IToastNotificationManager struct {
- lpVtbl *IToastNotificationManagerVtbl
-
- GetContent uintptr // not wrapped
- PutExpirationTime uintptr // not wrapped
- GetExpirationTime uintptr // not wrapped
- AddDismissed uintptr // not wrapped
- RemoveDismissed uintptr // not wrapped
- AddActivated uintptr // not wrapped
- RemoveActivated uintptr // not wrapped
- AddFailed uintptr // not wrapped
- RemoveFailed uintptr // not wrapped
-}
-
-type IToastNotificationManagerVtbl struct {
- ole.IInspectableVtbl
-
- CreateToastNotifier uintptr // not wrapped
- CreateToastNotifierWithID uintptr
- GetTemplateContent uintptr // not wrapped
-}
-
-func (v *IToastNotificationManager) CreateToastNotifierWithID(appID string) (ret *IToastNotifier, err error) {
- hsAppID, err := ole.NewHString(appID)
- if err != nil {
- return nil, fmt.Errorf("allocating string: %w", err)
- }
- defer ole.DeleteHString(hsAppID)
- hr, _, _ := syscall.SyscallN(
- v.lpVtbl.CreateToastNotifierWithID,
- uintptr(unsafe.Pointer(v)),
- uintptr(hsAppID),
- uintptr(unsafe.Pointer(&ret)),
- )
- if hr != ole.S_OK {
- return nil, ole.NewError(hr)
- }
- return ret, nil
-}
-
-// IToastNotifier can push notification objects to the runtime.
-type IToastNotifier struct {
- lpVtbl *IToastNotifierVtbl
-}
-
-type IToastNotifierVtbl struct {
- ole.IInspectableVtbl
-
- Show uintptr
- Hide uintptr // not wrapped
- GetSetting uintptr // not wrapped
- AddToSchedule uintptr // not wrapped
- RemoveFromSchedule uintptr // not wrapped
- GetScheduledToastNotifications uintptr // not wrapped
-}
-
-func (v *IToastNotifier) Show(noti *IToastNotification) (err error) {
- hr, _, _ := syscall.SyscallN(
- v.lpVtbl.Show,
- uintptr(unsafe.Pointer(v)),
- uintptr(unsafe.Pointer(noti)),
- )
- if hr != ole.S_OK {
- return ole.NewError(hr)
- }
- return nil
-}
-
-// IToastNotificationFactory can create toast notification objects.
-type IToastNotificationFactory struct {
- lpVtbl *IToastNotificationFactoryVtbl
-}
-
-type IToastNotificationFactoryVtbl struct {
- ole.IInspectableVtbl
-
- CreateToastNotification uintptr
-}
-
-func (v *IToastNotificationFactory) CreateToastNotification(xmlDispatch *ole.IDispatch) (ret *IToastNotification, err error) {
- hr, _, _ := syscall.SyscallN(
- v.lpVtbl.CreateToastNotification,
- uintptr(unsafe.Pointer(v)),
- uintptr(unsafe.Pointer(xmlDispatch)),
- uintptr(unsafe.Pointer(&ret)),
- )
- if hr != ole.S_OK {
- return nil, ole.NewError(hr)
- }
- return ret, nil
-}
-
-// IXMLDocumentIO implements IO for XML documents.
-type IXMLDocumentIO struct {
- lpVtbl *IXMLDocumentIOVtbl
-}
-
-type IXMLDocumentIOVtbl struct {
- ole.IInspectableVtbl
-
- LoadXml uintptr
- LoadXmlWithSettings uintptr // not wrapped
- SaveToFileAsync uintptr // not wrapped
-}
-
-func (v *IXMLDocumentIO) LoadXml(xml string) (err error) {
- hsXML, err := ole.NewHString(xml)
- if err != nil {
- return err
- }
- defer ole.DeleteHString(hsXML)
- hr, _, _ := syscall.SyscallN(
- v.lpVtbl.LoadXml,
- uintptr(unsafe.Pointer(v)),
- uintptr(hsXML),
- )
- if hr != ole.S_OK {
- return ole.NewError(hr)
- }
- return nil
-}
-
-// IClassFactory is used to build other classes. We will use this to build our implementation
-// of the INotificationActivationCallback interface.
-type IClassFactory struct {
- lpVtbl *IClassFactoryVtbl
- RefCount int64
-}
-
-type IClassFactoryVtbl struct {
- ole.IUnknownVtbl
- CreateInstance uintptr // not wrapped
- LockServer uintptr // not wrapped
-}
-
-func (v *IClassFactory) AddRef() int32 {
- count, _, _ := syscall.SyscallN(
- v.lpVtbl.AddRef,
- uintptr(unsafe.Pointer(v)),
- )
- return int32(count)
-}
-
-func (v *IClassFactory) Release() int32 {
- count, _, _ := syscall.SyscallN(
- v.lpVtbl.Release,
- uintptr(unsafe.Pointer(v)),
- )
- return int32(count)
-}
-
-// INotificationActivationCallback receives activations from toast notifications.
-type INotificationActivationCallback struct {
- lpVtbl *INotificationActivationCallbackVtbl
- RefCount int64
-}
-
-type INotificationActivationCallbackVtbl struct {
- ole.IUnknownVtbl
- Activate uintptr
-}
-
-func (v *INotificationActivationCallback) QueryInterface(riid *ole.GUID, out unsafe.Pointer) HRESULT {
- ret, _, _ := syscall.SyscallN(
- v.lpVtbl.QueryInterface,
- uintptr(unsafe.Pointer(v)),
- uintptr(unsafe.Pointer(riid)),
- uintptr(out),
- )
- return HRESULT(ret)
-}
-
-func (v *INotificationActivationCallback) AddRef() int32 {
- count, _, _ := syscall.SyscallN(
- v.lpVtbl.AddRef,
- uintptr(unsafe.Pointer(v)),
- )
- return int32(count)
-}
-
-func (v *INotificationActivationCallback) Release() int32 {
- count, _, _ := syscall.SyscallN(
- v.lpVtbl.Release,
- uintptr(unsafe.Pointer(v)),
- )
- return int32(count)
-}
diff --git a/wintoast/procs.go b/wintoast/procs.go
@@ -10,42 +10,12 @@ import (
)
var (
- // Define memory allocation procs. This is how we get a hold of unmanaged memory.
- kernel32 = windows.NewLazySystemDLL("kernel32.dll")
- procMalloc = kernel32.NewProc("GlobalAlloc")
- procFree = kernel32.NewProc("GlobalFree")
-
// Define procs that go-ole doesn't provide. This is how we register our Go-implemented
// COM objects.
modcombase = windows.NewLazySystemDLL("combase.dll")
procRegisterClassObject = modcombase.NewProc("CoRegisterClassObject")
)
-// Allocation flags we need.
-// There are more, these are just the ones we use.
-// See https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-globalalloc
-const (
- GMEM_FIXED = 0x0000
- GMEM_ZEROINIT = 0x0040
-)
-
-// malloc allocates raw memory using the Windows kernel.
-// In case of out of memory, the returned pointer will be nil.
-// The memory is zeroed out to make sure we don't get garbage that looks like
-// valid Go data types.
-func malloc(size uintptr) unsafe.Pointer {
- hr, _, _ := procMalloc.Call(uintptr(GMEM_FIXED|GMEM_ZEROINIT), uintptr(size))
- if hr == 0 {
- return nil
- }
- return unsafe.Pointer(hr)
-}
-
-// free deallocates raw memory allocated by malloc.
-func free(object unsafe.Pointer) {
- procFree.Call(uintptr(object))
-}
-
// registerClassFactory teaches the Windows Runtime about our factory that can allocate
// instances of our ActivationCallback.
func registerClassFactory(factory *IClassFactory) error {