package winbt // This file implements a COM object that can be used for various event // callbacks. In C++, it would use an ITypedEventHandler instantiation. import ( "unsafe" "github.com/go-ole/go-ole" ) // const void * winbt_getEventVtbl(void); import "C" // Event implements event handler interfaces (ITypedEventHandler). This is // therefore a valid COM object, derived from IUnknown. type Event struct { ole.IUnknown IID *ole.GUID Callback func(*Event, *ole.IInspectable) token uintptr } // NewEvent creates a new COM object for event callbacks. The IID must be the // IID for a particular event, which is unfortunately not included in the *.idl // files but has to be found in the relevant C++/WinRT header files. func NewEvent(iid *ole.GUID, callback func(*Event, *ole.IInspectable)) *Event { // Another way to find the IID is to print the requested IID in the // QueryInterface method below. The first queried IID is likely the one // that is the event interface IID. // The event must be allocated on the C heap, because it is not allowed to // retain a pointer on the Go heap after a non-Go call returns (e.g. // syscall.Syscall). event := (*Event)(C.malloc(C.size_t(unsafe.Sizeof(Event{})))) event.RawVTable = (*interface{})(C.winbt_getEventVtbl()) event.IID = iid event.Callback = callback return event } // The two functions below implement IUnknown and an interface I couldn't // really get the definition of (possibly ITypedEventHandler, although // ITypedEventHandler is really a C++ template). The closest thing I could find // to documentation are the C++/WinRT headers and this mention by Kenny Kerr // (the original developer of C++/WinRT): // https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/august/windows-with-c-the-windows-runtime-application-model // > [...] Fortunately, all of these interfaces are generated by the MIDL // > compiler, which takes care to specialize each one, and it’s on these // > specializations that it attaches the GUID representing the interface // > identifier. As complicated as the previous typedef might appear, it // > defines a COM interface that derives directly from IUnknown and provides // > a single method called Invoke. //export winbt_Event_QueryInterface func winbt_Event_QueryInterface(eventPtr, iidPtr unsafe.Pointer, ppvObject *unsafe.Pointer) uintptr { // This function must adhere to the QueryInterface defined here: // https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nn-unknwn-iunknown iid := (*ole.GUID)(iidPtr) event := (*Event)(eventPtr) if ole.IsEqualGUID(iid, event.IID) { // This is us. *ppvObject = eventPtr return ole.S_OK } if ole.IsEqualGUID(iid, ole.IID_IUnknown) { // This is our parent. There are some limitations as to what we can // return here, but returning the *Event pointer is fine. *ppvObject = eventPtr return ole.S_OK } return 0x80004002 // E_NOTINTERFACE } //export winbt_Event_Invoke func winbt_Event_Invoke(eventPtr, senderPtr, argsPtr unsafe.Pointer) uintptr { // See the quote above. event := (*Event)(eventPtr) argsInspectable := (*ole.IInspectable)(argsPtr) event.Callback(event, argsInspectable) return ole.S_OK }