/
idispatch.go
326 lines (290 loc) · 11 KB
/
idispatch.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
package w32
import (
"fmt"
"math/big"
"syscall"
"time"
"unsafe"
)
var preferLCID LCID = LOCALE_USER_DEFAULT
func SetPreferLCID(id LCID) {
preferLCID = id
}
// IDispatch
// Methods: https://learn.microsoft.com/en-us/windows/win32/api/oaidl/nn-oaidl-idispatch#methods
type IDispatch struct {
IUnknown
}
type IDispatchVTable struct { // 🧙 注意! 順序不可以亂放,否則disp.VTable().xxx取到的函數會是錯的
IUnknownVTable // 所有的其他類型都一定會含有IUnknown所提供的方法,記得要補上
GetTypeInfoCount uintptr
GetTypeInfo uintptr
GetIDsOfNames uintptr
Invoke uintptr
}
func (d *IDispatch) VTable() *IDispatchVTable {
if d == nil {
panic("IDispatch is nil")
}
return (*IDispatchVTable)(unsafe.Pointer(d.RawVTable))
}
// GetIDsOfNames https://learn.microsoft.com/en-us/windows/win32/api/oaidl/nf-oaidl-idispatch-getidsofnames
// LCID kernel32dll.GetUserDefaultLCID()
func (d *IDispatch) GetIDsOfNames(
iid *GUID, // Reserved for future use. Must be IID_NULL.
names []string,
nNames uint32,
lcID LCID,
) ([]DISPID, syscall.Errno) {
if iid == nil {
iid = IID_NULL
}
if nNames == 0 {
nNames = uint32(len(names))
}
dispIDs := make([]DISPID, len(names))
wNames := make([]*uint16, len(names)) // winapi用的是utf16,go字串是utf8,要轉換
for i := 0; i < len(names); i++ {
pWStr, err := syscall.UTF16PtrFromString(names[i])
if err != nil {
panic(err)
}
wNames[i] = pWStr
}
// log.Println(d.VTable().GetIDsOfNames) // 如果您知道位址可以直接寫入也是一樣,例如: uintptr(140715441321184), 如果函數調用失敗不妨看看成功的例子他的數值是多少,來判斷是否函數的順序寫錯 // 這個是固定的數值,表示某一函數,如果d.VTable()也就是IDispatchVTable,依照該函數順序去取得相關函數
hr, _, _ := syscall.SyscallN(d.VTable().GetIDsOfNames, uintptr(unsafe.Pointer(d)),
uintptr(unsafe.Pointer(iid)), // Reserved for future use. Must be IID_NULL.
uintptr(unsafe.Pointer(&wNames[0])),
uintptr(nNames),
uintptr(lcID),
uintptr(unsafe.Pointer(&dispIDs[0])),
)
return dispIDs, syscall.Errno(hr)
}
// GetTypeInfo https://learn.microsoft.com/en-us/windows/win32/api/oaidl/nf-oaidl-idispatch-gettypeinfo
func (d *IDispatch) GetTypeInfo(lcid LCID) (*ITypeInfo, syscall.Errno) {
var tinfo *ITypeInfo
hr, _, _ := syscall.SyscallN(d.VTable().GetTypeInfo,
uintptr(unsafe.Pointer(d)),
uintptr(lcid),
uintptr(unsafe.Pointer(&tinfo)),
)
return tinfo, syscall.Errno(hr)
}
// GetTypeInfoCount https://learn.microsoft.com/en-us/windows/win32/api/oaidl/nf-oaidl-idispatch-gettypeinfocount
// Retrieves the number of type information interfaces that an object provides (either 0 or 1).
func (d *IDispatch) GetTypeInfoCount() (c uint32, errno syscall.Errno) {
hr, _, _ := syscall.SyscallN(d.VTable().GetTypeInfoCount, uintptr(unsafe.Pointer(d)),
uintptr(unsafe.Pointer(&c)),
)
errno = syscall.Errno(hr)
return
}
// Invoke https://learn.microsoft.com/en-us/windows/win32/api/oaidl/nf-oaidl-idispatch-invoke
func (d *IDispatch) Invoke(
disID DISPID, // Use GetIDsOfNames or the object's documentation to obtain the dispatch identifier.
rIID *GUID, // Reserved for future use. Must be IID_NULL.
lcID LCID,
dispatchFlag DispatchFlag,
dispParams *DispParams, // [in, out]
// result *VARIANT, // 🧙 不能放裡面
exceptInfo *EXCEPINFO, // [out]
puArgErr *uint32, // [out]
) (result *VARIANT, errno syscall.Errno) {
if rIID == nil {
rIID = IID_NULL
}
var argErr uintptr
if puArgErr != nil {
argErr = uintptr(unsafe.Pointer(&puArgErr))
}
result = new(VARIANT)
OleAutDll.VariantInit(result)
hr, _, _ := syscall.SyscallN(d.VTable().Invoke, uintptr(unsafe.Pointer(d)),
uintptr(disID),
uintptr(unsafe.Pointer(rIID)), // Reserved for future use. Must be IID_NULL.
uintptr(lcID),
uintptr(dispatchFlag),
uintptr(unsafe.Pointer(dispParams)), // 不能給0
uintptr(unsafe.Pointer(result)),
uintptr(unsafe.Pointer(exceptInfo)),
argErr,
)
return result, syscall.Errno(hr)
}
func (d *IDispatch) PropertyPut(name string, params ...any) (*VARIANT, syscall.Errno) {
return invoke(d, name, DISPATCH_PROPERTYPUT, params...)
}
func (d *IDispatch) MustPropertyPut(name string, params ...any) *VARIANT {
result, errno := invoke(d, name, DISPATCH_PROPERTYPUT, params...)
if errno != 0 {
panic(fmt.Sprintf("%s", errno))
}
return result
}
func (d *IDispatch) PropertyPutRef(name string, params ...any) (*VARIANT, syscall.Errno) {
return invoke(d, name, DISPATCH_PROPERTYPUTREF, params...)
}
func (d *IDispatch) MustPropertyPutRef(name string, params ...any) *VARIANT {
result, errno := invoke(d, name, DISPATCH_PROPERTYPUTREF, params...)
if errno != 0 {
panic(fmt.Sprintf("%s", errno))
}
return result
}
func (d *IDispatch) PropertyGet(name string, params ...any) (*VARIANT, syscall.Errno) {
return invoke(d, name, DISPATCH_PROPERTYGET, params...)
}
func (d *IDispatch) MustPropertyGet(name string, params ...any) *VARIANT {
result, errno := invoke(d, name, DISPATCH_PROPERTYGET, params...)
if errno != 0 {
panic(fmt.Sprintf("%s", errno))
}
return result
}
func (d *IDispatch) Method(name string, params ...any) (*VARIANT, syscall.Errno) {
return invoke(d, name, DISPATCH_METHOD, params...)
}
func (d *IDispatch) MustMethod(name string, params ...any) (result *VARIANT) {
var errno syscall.Errno
result, errno = invoke(d, name, DISPATCH_METHOD, params...)
if errno != 0 {
panic(fmt.Sprintf("%s", errno))
}
return result
}
func invoke(d *IDispatch, name string, dispatchFlag DispatchFlag, params ...any) (outVariant *VARIANT, errno syscall.Errno) {
var (
propertyNameID DISPID
names []DISPID
)
names, errno = d.GetIDsOfNames(IID_NULL, []string{name}, 1, preferLCID)
if errno != 0 {
return nil, errno
}
propertyNameID = names[0]
dispParams := new(DispParams) // 都沒有數值需要填還是需要指向一個空內容,不能是nil,否則會錯
// Set NameArgs if and only if the property is the PUT.
if dispatchFlag&DISPATCH_PROPERTYPUT != 0 {
nameArgs := [1]DISPID{DISPID_PROPERTYPUT}
dispParams.NamedArgs = uintptr(unsafe.Pointer(&nameArgs[0]))
dispParams.CNamedArgs = 1
} else if dispatchFlag&DISPATCH_PROPERTYPUTREF != 0 {
nameArgs := [1]DISPID{DISPID_PROPERTYPUT}
dispParams.NamedArgs = uintptr(unsafe.Pointer(&nameArgs[0]))
dispParams.CNamedArgs = 1
}
// Set Params
var vargs []VARIANT // 寫在外面是應付當為VT_BSTR或VT_BYREF,最後需要再釋放
if len(params) > 0 {
vargs = make([]VARIANT, len(params))
for i, v := range params {
n := len(params) - i - 1 // https://go.dev/play/p/cbE_62Q1-96 因為defer有後進先出(LIFO)的特性,所以排序也要在顛倒,才會是原本的順序
OleAutDll.VariantInit(&vargs[n])
switch vv := v.(type) {
case bool:
if vv {
vargs[n] = NewVariant(VT_BOOL, 0xffff)
} else {
vargs[n] = NewVariant(VT_BOOL, 0)
}
case *bool:
vargs[n] = NewVariant(VT_BOOL|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*bool)))))
case uint8:
vargs[n] = NewVariant(VT_I1, int64(v.(uint8)))
case *uint8:
vargs[n] = NewVariant(VT_I1|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint8)))))
case int8:
vargs[n] = NewVariant(VT_I1, int64(v.(int8)))
case *int8:
vargs[n] = NewVariant(VT_I1|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int8)))))
case int16:
vargs[n] = NewVariant(VT_I2, int64(v.(int16)))
case *int16:
vargs[n] = NewVariant(VT_I2|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int16)))))
case uint16:
vargs[n] = NewVariant(VT_UI2, int64(v.(uint16)))
case *uint16:
vargs[n] = NewVariant(VT_UI2|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint16)))))
case int32:
vargs[n] = NewVariant(VT_I4, int64(v.(int32)))
case *int32:
vargs[n] = NewVariant(VT_I4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int32)))))
case uint32:
vargs[n] = NewVariant(VT_UI4, int64(v.(uint32)))
case *uint32:
vargs[n] = NewVariant(VT_UI4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint32)))))
case int64:
vargs[n] = NewVariant(VT_I8, int64(v.(int64)))
case *int64:
vargs[n] = NewVariant(VT_I8|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int64)))))
case uint64:
vargs[n] = NewVariant(VT_UI8, int64(uintptr(v.(uint64))))
case *uint64:
vargs[n] = NewVariant(VT_UI8|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint64)))))
case int:
vargs[n] = NewVariant(VT_I4, int64(v.(int)))
case *int:
vargs[n] = NewVariant(VT_I4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int)))))
case uint:
vargs[n] = NewVariant(VT_UI4, int64(v.(uint)))
case *uint:
vargs[n] = NewVariant(VT_UI4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint)))))
case float32:
vargs[n] = NewVariant(VT_R4, *(*int64)(unsafe.Pointer(&vv)))
case *float32:
vargs[n] = NewVariant(VT_R4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*float32)))))
case float64:
vargs[n] = NewVariant(VT_R8, *(*int64)(unsafe.Pointer(&vv)))
case *float64:
vargs[n] = NewVariant(VT_R8|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*float64)))))
case *big.Int:
vargs[n] = NewVariant(VT_DECIMAL, v.(*big.Int).Int64())
case string:
vargs[n] = NewVariant(VT_BSTR, int64(uintptr(unsafe.Pointer(OleAutDll.SysAllocStringLen(v.(string))))))
case *string:
vargs[n] = NewVariant(VT_BSTR|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*string)))))
case time.Time:
s := vv.Format("2006-01-02 15:04:05")
vargs[n] = NewVariant(VT_BSTR, int64(uintptr(unsafe.Pointer(OleAutDll.SysAllocStringLen(s)))))
case *time.Time:
s := vv.Format("2006-01-02 15:04:05")
vargs[n] = NewVariant(VT_BSTR|VT_BYREF, int64(uintptr(unsafe.Pointer(&s))))
case *IDispatch:
vargs[n] = NewVariant(VT_DISPATCH, int64(uintptr(unsafe.Pointer(v.(*IDispatch)))))
case **IDispatch:
vargs[n] = NewVariant(VT_DISPATCH|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(**IDispatch)))))
case nil:
vargs[n] = NewVariant(VT_NULL, 0)
case *VARIANT:
vargs[n] = NewVariant(VT_VARIANT|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*VARIANT)))))
case []byte:
safeByteArray := safeArrayFromByteSlice(v.([]byte))
vargs[n] = NewVariant(VT_ARRAY|VT_UI1, int64(uintptr(unsafe.Pointer(safeByteArray))))
defer OleAutDll.VariantClear(&vargs[n])
case []string:
safeByteArray := safeArrayFromStringSlice(v.([]string))
vargs[n] = NewVariant(VT_ARRAY|VT_BSTR, int64(uintptr(unsafe.Pointer(safeByteArray))))
defer OleAutDll.VariantClear(&vargs[n])
default:
panic("unknown type")
}
}
dispParams.VArgs = uintptr(unsafe.Pointer(&vargs[0]))
dispParams.CArgs = uint32(len(params))
}
var exceptInfo EXCEPINFO
outVariant, errno = d.Invoke(propertyNameID, IID_NULL, preferLCID, dispatchFlag, dispParams, &exceptInfo, nil)
if vargs != nil {
for i, varg := range vargs {
n := len(params) - i - 1
if varg.VT == VT_BSTR && varg.Val != 0 {
OleAutDll.SysFreeString((*uint16)(unsafe.Pointer(uintptr(varg.Val))))
}
if varg.VT == (VT_BSTR|VT_BYREF) && varg.Val != 0 {
*(params[n].(*string)) = LpOleStrToString(*(**uint16)(unsafe.Pointer(uintptr(varg.Val))))
}
}
}
return outVariant, errno
}