返回 DeepSeek-Reasonix
bridge.go
1 //go:build windows
2
3 package combridge
4
5 import (
6 "fmt"
7 "runtime"
8 "sync"
9 "sync/atomic"
10 )
11
12 var (
13 comIfcePointersL sync.RWMutex
14 comIfcePointers = map[uintptr]*comObject{} // Map from ComInterfacePointer to the Go ComObject
15 )
16
17 // Resolve the GoInterface of the specified ComInterfacePointer
18 func Resolve[T IUnknown](ifceP uintptr) T {
19 comIfcePointersL.RLock()
20 comObj := comIfcePointers[ifceP]
21 comIfcePointersL.RUnlock()
22
23 var n T
24 if comObj != nil {
25 t := comObj.resolve(ifceP)
26 if t != nil {
27 n = t.(T)
28 }
29 }
30
31 return n
32 }
33
34 // New returns a new ComObject which implements the specified Com Interface, com calls will be redirected
35 // to the specified go interface.
36 func New[T IUnknown](obj T) *ComObject[T] {
37 cObj := new(
38 ifceDef[T]{obj},
39 )
40 return newComObject[T](cObj)
41 }
42
43 // New2 returns a new ComObject which implements the two specified Com Interfaces, com calls will be redirected
44 // to those interfaces accordingly.
45 // This is needed if a ComObject should implement two interfaces that are not descendants of each other,
46 // then you get multiple inheritance.
47 func New2[T IUnknown, T2 IUnknown](obj T, obj2 T2) *ComObject[T] {
48 cObj := new(
49 ifceDef[T]{obj},
50 ifceDef[T2]{obj2},
51 )
52 return newComObject[T](cObj)
53 }
54
55 // new returns a new ComObject which implements multiple specified Com Interfaces, com calls will be redirected
56 // to the specified go interfaces accordingly.
57 // This is needed if a ComObject should implement multiple interfaces that are not descendants of each other,
58 // then you get multiple inheritance.
59 func new(impls ...ifceImpl) *comObject {
60 impls = append([]ifceImpl{ifceDef[IUnknown]{}}, impls...)
61
62 cObj := &comObject{
63 refCount: 1,
64 ifces: map[string]int{},
65 ifcesImpl: make([]comInterfaceDesc, len(impls)),
66 }
67
68 for i, ifceDef := range impls {
69 vtable, err := ifceDef.ifce()
70 if err != nil {
71 panic(err)
72 }
73
74 needsImplement := false
75 for table := vtable; table != nil; table = table.Parent {
76 guid := table.ComGUID
77 if i, found := cObj.ifces[guid]; found {
78 // This Interface is already implemented
79 if guid == iUnknownGUID {
80 // IUnknown is a special interface and never has an user specific implementation
81 } else if cObj.ifcesImpl[i].impl != ifceDef.impl() {
82 panic(fmt.Sprintf("Interface '%s' is already implemented by another object", table.Name))
83 }
84
85 break
86 }
87
88 needsImplement = true
89 cObj.ifces[guid] = i
90 }
91
92 if !needsImplement {
93 continue
94 }
95
96 ifceP, ifcePSlice := allocUintptrObject(1)
97 ifcePSlice[0] = vtable.ComVTable
98 cObj.ifcesImpl[i] = comInterfaceDesc{ifceP, ifceDef.impl()}
99 }
100
101 comIfcePointersL.Lock()
102 for _, ifceImpl := range cObj.ifcesImpl {
103 comIfcePointers[ifceImpl.ref] = cObj
104 }
105 comIfcePointersL.Unlock()
106
107 return cObj
108 }
109
110 func newComObject[T IUnknown](comObj *comObject) *ComObject[T] {
111 c := &ComObject[T]{obj: comObj}
112 // Make sure to async release since release needs locks and might block the finalizer goroutine for a longer period
113 runtime.SetFinalizer(c, func(obj *ComObject[T]) { obj.close(true) })
114 return c
115 }
116
117 // ComObject describes an exported go instance to be used as a ComObject which implements
118 // the specified Interface.
119 type ComObject[T IUnknown] struct {
120 obj *comObject
121 closed int32
122 }
123
124 // Ref returns the native uintptr that points to the ComObject that is an interface pointer to T.
125 // This can be used in native calls. If the object has been closed this function will panic.
126 func (o *ComObject[T]) Ref() uintptr {
127 if atomic.LoadInt32(&o.closed) != 0 {
128 panic("ComObject has been released")
129 }
130 return o.obj.queryInterface(guidOf[T](), false)
131 }
132
133 // Close releases the native com object from the go side. It will only be destroyed if the ref counter
134 // reaches zero.
135 // After closing `Ref()` will panic.
136 func (o *ComObject[T]) Close() error {
137 o.close(false)
138 return nil
139 }
140
141 // close releases the native com object from the go side. It will only be destroyed if the ref counter
142 // reaches zero.
143 // After closing `Ref()` will panic.
144 func (o *ComObject[T]) close(asyncRelease bool) {
145 if atomic.CompareAndSwapInt32(&o.closed, 0, 1) {
146 runtime.SetFinalizer(o, nil)
147 if asyncRelease {
148 go o.obj.release()
149 } else {
150 o.obj.release()
151 }
152 }
153 }
154
155 type comInterfaceDesc struct {
156 ref uintptr // The native Com InterfacePointer
157 impl any // The golang target object
158 }
159
160 type comObject struct {
161 l sync.Mutex
162
163 refCount int32
164 ifces map[string]int // Map of ComInterfaceGUID to Interface Slots
165 ifcesImpl []comInterfaceDesc // Slots with InterfaceDescriptors
166 }
167
168 func (c *comObject) queryInterface(ifceGUID string, withAddRef bool) uintptr {
169 c.l.Lock()
170 defer c.l.Unlock()
171 if c.refCount <= 0 {
172 panic("call on released com object")
173 }
174
175 i, found := c.ifces[ifceGUID]
176 if !found {
177 return 0
178 }
179
180 if withAddRef {
181 c.refCount++
182 }
183 return c.ifcesImpl[i].ref
184 }
185
186 func (c *comObject) resolve(ifceP uintptr) any {
187 c.l.Lock()
188 defer c.l.Unlock()
189 if c.refCount <= 0 {
190 panic("call on destroyed com object")
191 }
192
193 for _, ifce := range c.ifcesImpl {
194 if ifce.ref != ifceP {
195 continue
196 }
197
198 return ifce.impl
199 }
200 return nil
201 }
202
203 func (c *comObject) addRef() int32 {
204 c.l.Lock()
205 defer c.l.Unlock()
206 if c.refCount <= 0 {
207 panic("call on destroyed com object")
208 }
209
210 c.refCount++
211 return c.refCount
212 }
213
214 func (c *comObject) release() int32 {
215 c.l.Lock()
216 defer c.l.Unlock()
217 if c.refCount <= 0 {
218 panic("call on destroyed com object")
219 }
220
221 if c.refCount--; c.refCount == 0 {
222 comIfcePointersL.Lock()
223 for _, ref := range c.ifcesImpl {
224 delete(comIfcePointers, ref.ref)
225 }
226 comIfcePointersL.Unlock()
227
228 for _, impl := range c.ifcesImpl {
229 ref := impl.ref
230 if ref == 0 {
231 continue
232 }
233
234 globalFree(ref)
235 }
236 }
237
238 return c.refCount
239 }
240
240 lines GO