prototooth/winbt/event.go

83 lines
3.1 KiB
Go
Raw Permalink Normal View History

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 its 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
}