83 lines
3.1 KiB
Go
83 lines
3.1 KiB
Go
|
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
|
|||
|
}
|