first commit

This commit is contained in:
2025-08-22 17:42:23 -04:00
commit a6c09a5890
120 changed files with 11443 additions and 0 deletions

View File

@ -0,0 +1,139 @@
//go:build windows
package delegate
import (
"sync"
"syscall"
"unsafe"
"github.com/go-ole/go-ole"
)
// Only a limited number of callbacks may be created in a single Go process,
// and any memory allocated for these callbacks is never released.
// Between NewCallback and NewCallbackCDecl, at least 1024 callbacks can always be created.
var (
queryInterfaceCallback = syscall.NewCallback(queryInterface)
addRefCallback = syscall.NewCallback(addRef)
releaseCallback = syscall.NewCallback(release)
invokeCallback = syscall.NewCallback(invoke)
)
// Delegate represents a WinRT delegate class.
type Delegate interface {
GetIID() *ole.GUID
Invoke(instancePtr, rawArgs0, rawArgs1, rawArgs2, rawArgs3, rawArgs4, rawArgs5, rawArgs6, rawArgs7, rawArgs8 unsafe.Pointer) uintptr
AddRef() uintptr
Release() uintptr
}
// Callbacks contains the syscalls registered on Windows.
type Callbacks struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
Invoke uintptr
}
var mutex = sync.RWMutex{}
var instances = make(map[uintptr]Delegate)
// RegisterCallbacks adds the given pointer and the Delegate it points to to our instances.
// This is required to redirect received callbacks to the correct object instance.
// The function returns the callbacks to use when creating a new delegate instance.
func RegisterCallbacks(ptr unsafe.Pointer, inst Delegate) *Callbacks {
mutex.Lock()
defer mutex.Unlock()
instances[uintptr(ptr)] = inst
return &Callbacks{
QueryInterface: queryInterfaceCallback,
AddRef: addRefCallback,
Release: releaseCallback,
Invoke: invokeCallback,
}
}
func getInstance(ptr unsafe.Pointer) (Delegate, bool) {
mutex.RLock() // locks writing, allows concurrent read
defer mutex.RUnlock()
i, ok := instances[uintptr(ptr)]
return i, ok
}
func removeInstance(ptr unsafe.Pointer) {
mutex.Lock()
defer mutex.Unlock()
delete(instances, uintptr(ptr))
}
func queryInterface(instancePtr unsafe.Pointer, iidPtr unsafe.Pointer, ppvObject *unsafe.Pointer) uintptr {
instance, ok := getInstance(instancePtr)
if !ok {
// instance not found
return ole.E_POINTER
}
// Checkout these sources for more information about the QueryInterface method.
// - https://docs.microsoft.com/en-us/cpp/atl/queryinterface
// - https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-queryinterface(refiid_void)
if ppvObject == nil {
// If ppvObject (the address) is nullptr, then this method returns E_POINTER.
return ole.E_POINTER
}
// This function must adhere to the QueryInterface defined here:
// https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nn-unknwn-iunknown
if iid := (*ole.GUID)(iidPtr); ole.IsEqualGUID(iid, instance.GetIID()) || ole.IsEqualGUID(iid, ole.IID_IUnknown) || ole.IsEqualGUID(iid, ole.IID_IInspectable) {
*ppvObject = instancePtr
} else {
*ppvObject = nil
// Return E_NOINTERFACE if the interface is not supported
return ole.E_NOINTERFACE
}
// If the COM object implements the interface, then it returns
// a pointer to that interface after calling IUnknown::AddRef on it.
(*ole.IUnknown)(*ppvObject).AddRef()
// Return S_OK if the interface is supported
return ole.S_OK
}
func invoke(instancePtr, rawArgs0, rawArgs1, rawArgs2, rawArgs3, rawArgs4, rawArgs5, rawArgs6, rawArgs7, rawArgs8 unsafe.Pointer) uintptr {
instance, ok := getInstance(instancePtr)
if !ok {
// instance not found
return ole.E_FAIL
}
return instance.Invoke(instancePtr, rawArgs0, rawArgs1, rawArgs2, rawArgs3, rawArgs4, rawArgs5, rawArgs6, rawArgs7, rawArgs8)
}
func addRef(instancePtr unsafe.Pointer) uintptr {
instance, ok := getInstance(instancePtr)
if !ok {
// instance not found
return ole.E_FAIL
}
return instance.AddRef()
}
func release(instancePtr unsafe.Pointer) uintptr {
instance, ok := getInstance(instancePtr)
if !ok {
// instance not found
return ole.E_FAIL
}
rem := instance.Release()
if rem == 0 {
// remove this delegate
removeInstance(instancePtr)
}
return rem
}