返回 DeepSeek-Reasonix
vtables.go
1 //go:build windows
2
3 package combridge
4
5 import (
6 "fmt"
7 "reflect"
8 "sync"
9
10 "golang.org/x/sys/windows"
11 )
12
13 var (
14 vTablesL sync.Mutex
15 vTables = make(map[string]*vTable)
16 )
17
18 // RegisterVTable registers the vtable trampoline methods for the specified ComInterface
19 // TBase is the base interface of T, and must be another ComInterface which roots in IUnknown or IUnknown itself.
20 // The first paramter of the fn is always the uintptr of the ComObject and the GoObject can be resolved with Resolve().
21 // After having resolved the GoObject the call must be redirected to the GoObject.
22 // Typically a trampoline FN looks like this.
23 //
24 // func _ICoreWebView2NavigationCompletedEventHandlerInvoke(this uintptr, sender *ICoreWebView2, args *ICoreWebView2NavigationCompletedEventArgs) uintptr {
25 // return combridge.Resolve[_ICoreWebView2NavigationCompletedEventHandler](this).NavigationCompleted(sender, args)
26 // }
27 //
28 // The order of registration must be in the correct order as specified in the IDL of the interface.
29 func RegisterVTable[TParent, T IUnknown](guid string, fns ...interface{}) {
30 registerVTableInternal[TParent, T](guid, false, fns...)
31 }
32
33 type vTable struct {
34 Parent *vTable
35
36 Name string
37 ComGUID string
38 ComVTable uintptr
39 ComProcs []uintptr
40 }
41
42 func registerVTableInternal[TParent, T IUnknown](guid string, isInternal bool, fns ...interface{}) {
43 vTablesL.Lock()
44 defer vTablesL.Unlock()
45
46 t, tName := typeInterfaceToString[T]()
47 tParent, tParentName := typeInterfaceToString[TParent]()
48 if !t.Implements(tParent) {
49 panic(fmt.Errorf("RegisterVTable '%s': '%s' must implement '%s'", tName, tName, tParentName))
50 }
51
52 if !isInternal {
53 if t == reflect.TypeOf((*IUnknown)(nil)).Elem() {
54 panic(fmt.Errorf("RegisterVTable '%s' IUnknown can't be registered", tName))
55 }
56
57 if t == tParent {
58 panic(fmt.Errorf("RegisterVTable '%s': T and TParent can't be the same type", tName))
59 }
60 }
61
62 var parent *vTable
63 var parentProcs []uintptr
64 var parentProcsCount int
65 if t != tParent {
66 parent = vTables[tParentName]
67 if parent == nil {
68 panic(fmt.Errorf("RegisterVTable '%s': Parent VTable '%s' not registered", tName, tParentName))
69 }
70
71 parentProcs = parent.ComProcs
72 parentProcsCount = len(parentProcs)
73 }
74
75 comGuid, err := windows.GUIDFromString(guid)
76 if err != nil {
77 panic(fmt.Errorf("RegisterVTable '%s': invalid guid: %s", tName, err))
78 }
79
80 vTable := &vTable{
81 Parent: parent,
82 Name: tName,
83 ComGUID: comGuid.String(),
84 }
85 vTable.ComVTable, vTable.ComProcs = allocUintptrObject(parentProcsCount + len(fns))
86
87 for i, proc := range parentProcs {
88 vTable.ComProcs[i] = proc
89 }
90
91 for i, fn := range fns {
92 vTable.ComProcs[parentProcsCount+i] = windows.NewCallback(fn)
93 }
94
95 vTables[tName] = vTable
96 }
97
98 func typeInterfaceToString[T any]() (reflect.Type, string) {
99 t := reflect.TypeOf((*T)(nil))
100 if t.Kind() != reflect.Pointer {
101 panic("must be a (*yourInterfaceType)(nil)")
102 }
103 t = t.Elem()
104 return t, t.PkgPath() + "/" + t.Name()
105 }
106
107 func typeInterfaceToStringOnly[T any]() string {
108 _, nane := typeInterfaceToString[T]()
109 return nane
110 }
111
112 func guidOf[T any]() string {
113 vtable := vTableOf[T]()
114 if vtable == nil {
115 return ""
116 }
117 return vtable.ComGUID
118 }
119
120 func vTableOf[T any]() *vTable {
121 name := typeInterfaceToStringOnly[T]()
122 vTablesL.Lock()
123 defer vTablesL.Unlock()
124
125 return vTables[name]
126 }
127
128 type ifceImpl interface {
129 impl() any
130 ifce() (*vTable, error)
131 }
132
133 type ifceDef[T any] struct {
134 objImpl any
135 }
136
137 func (i ifceDef[T]) impl() any {
138 return i.objImpl
139 }
140
141 func (i ifceDef[T]) ifce() (*vTable, error) {
142 vtable := vTableOf[T]()
143 if vtable == nil {
144 return nil, fmt.Errorf("Unable to find vTable for %s", typeInterfaceToStringOnly[T]())
145 }
146 return vtable, nil
147 }
148
148 lines GO