148 lines
3.6 KiB
Go
148 lines
3.6 KiB
Go
|
//go:build windows
|
||
|
|
||
|
package combridge
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"reflect"
|
||
|
"sync"
|
||
|
|
||
|
"golang.org/x/sys/windows"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
vTablesL sync.Mutex
|
||
|
vTables = make(map[string]*vTable)
|
||
|
)
|
||
|
|
||
|
// RegisterVTable registers the vtable trampoline methods for the specified ComInterface
|
||
|
// TBase is the base interface of T, and must be another ComInterface which roots in IUnknown or IUnknown itself.
|
||
|
// The first paramter of the fn is always the uintptr of the ComObject and the GoObject can be resolved with Resolve().
|
||
|
// After having resolved the GoObject the call must be redirected to the GoObject.
|
||
|
// Typically a trampoline FN looks like this.
|
||
|
//
|
||
|
// func _ICoreWebView2NavigationCompletedEventHandlerInvoke(this uintptr, sender *ICoreWebView2, args *ICoreWebView2NavigationCompletedEventArgs) uintptr {
|
||
|
// return combridge.Resolve[_ICoreWebView2NavigationCompletedEventHandler](this).NavigationCompleted(sender, args)
|
||
|
// }
|
||
|
//
|
||
|
// The order of registration must be in the correct order as specified in the IDL of the interface.
|
||
|
func RegisterVTable[TParent, T IUnknown](guid string, fns ...interface{}) {
|
||
|
registerVTableInternal[TParent, T](guid, false, fns...)
|
||
|
}
|
||
|
|
||
|
type vTable struct {
|
||
|
Parent *vTable
|
||
|
|
||
|
Name string
|
||
|
ComGUID string
|
||
|
ComVTable uintptr
|
||
|
ComProcs []uintptr
|
||
|
}
|
||
|
|
||
|
func registerVTableInternal[TParent, T IUnknown](guid string, isInternal bool, fns ...interface{}) {
|
||
|
vTablesL.Lock()
|
||
|
defer vTablesL.Unlock()
|
||
|
|
||
|
t, tName := typeInterfaceToString[T]()
|
||
|
tParent, tParentName := typeInterfaceToString[TParent]()
|
||
|
if !t.Implements(tParent) {
|
||
|
panic(fmt.Errorf("RegisterVTable '%s': '%s' must implement '%s'", tName, tName, tParentName))
|
||
|
}
|
||
|
|
||
|
if !isInternal {
|
||
|
if t == reflect.TypeOf((*IUnknown)(nil)).Elem() {
|
||
|
panic(fmt.Errorf("RegisterVTable '%s' IUnknown can't be registered", tName))
|
||
|
}
|
||
|
|
||
|
if t == tParent {
|
||
|
panic(fmt.Errorf("RegisterVTable '%s': T and TParent can't be the same type", tName))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var parent *vTable
|
||
|
var parentProcs []uintptr
|
||
|
var parentProcsCount int
|
||
|
if t != tParent {
|
||
|
parent = vTables[tParentName]
|
||
|
if parent == nil {
|
||
|
panic(fmt.Errorf("RegisterVTable '%s': Parent VTable '%s' not registered", tName, tParentName))
|
||
|
}
|
||
|
|
||
|
parentProcs = parent.ComProcs
|
||
|
parentProcsCount = len(parentProcs)
|
||
|
}
|
||
|
|
||
|
comGuid, err := windows.GUIDFromString(guid)
|
||
|
if err != nil {
|
||
|
panic(fmt.Errorf("RegisterVTable '%s': invalid guid: %s", tName, err))
|
||
|
}
|
||
|
|
||
|
vTable := &vTable{
|
||
|
Parent: parent,
|
||
|
Name: tName,
|
||
|
ComGUID: comGuid.String(),
|
||
|
}
|
||
|
vTable.ComVTable, vTable.ComProcs = allocUintptrObject(parentProcsCount + len(fns))
|
||
|
|
||
|
for i, proc := range parentProcs {
|
||
|
vTable.ComProcs[i] = proc
|
||
|
}
|
||
|
|
||
|
for i, fn := range fns {
|
||
|
vTable.ComProcs[parentProcsCount+i] = windows.NewCallback(fn)
|
||
|
}
|
||
|
|
||
|
vTables[tName] = vTable
|
||
|
}
|
||
|
|
||
|
func typeInterfaceToString[T any]() (reflect.Type, string) {
|
||
|
t := reflect.TypeOf((*T)(nil))
|
||
|
if t.Kind() != reflect.Pointer {
|
||
|
panic("must be a (*yourInterfaceType)(nil)")
|
||
|
}
|
||
|
t = t.Elem()
|
||
|
return t, t.PkgPath() + "/" + t.Name()
|
||
|
}
|
||
|
|
||
|
func typeInterfaceToStringOnly[T any]() string {
|
||
|
_, nane := typeInterfaceToString[T]()
|
||
|
return nane
|
||
|
}
|
||
|
|
||
|
func guidOf[T any]() string {
|
||
|
vtable := vTableOf[T]()
|
||
|
if vtable == nil {
|
||
|
return ""
|
||
|
}
|
||
|
return vtable.ComGUID
|
||
|
}
|
||
|
|
||
|
func vTableOf[T any]() *vTable {
|
||
|
name := typeInterfaceToStringOnly[T]()
|
||
|
vTablesL.Lock()
|
||
|
defer vTablesL.Unlock()
|
||
|
|
||
|
return vTables[name]
|
||
|
}
|
||
|
|
||
|
type ifceImpl interface {
|
||
|
impl() any
|
||
|
ifce() (*vTable, error)
|
||
|
}
|
||
|
|
||
|
type ifceDef[T any] struct {
|
||
|
objImpl any
|
||
|
}
|
||
|
|
||
|
func (i ifceDef[T]) impl() any {
|
||
|
return i.objImpl
|
||
|
}
|
||
|
|
||
|
func (i ifceDef[T]) ifce() (*vTable, error) {
|
||
|
vtable := vTableOf[T]()
|
||
|
if vtable == nil {
|
||
|
return nil, fmt.Errorf("Unable to find vTable for %s", typeInterfaceToStringOnly[T]())
|
||
|
}
|
||
|
return vtable, nil
|
||
|
}
|