-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathtest.bas
More file actions
489 lines (363 loc) · 14.1 KB
/
test.bas
File metadata and controls
489 lines (363 loc) · 14.1 KB
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
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
' //
' // IDispatch implementation of light-weight COM object
' // By The trick 2018
' //
Option Explicit
Private Const E_INVALIDARG As Long = &H80070057
Private Const E_NOINTERFACE As Long = &H80004002
Private Const CC_STDCALL As Long = 4
Private Const VT_BYREF As Long = &H4000&
Private Const DISPATCH_PROPERTYPUT As Long = 4
Private Const DISPATCH_METHOD As Long = 1
Private Const DISPATCH_PROPERTYGET As Long = 2
Private Const LOCALE_SYSTEM_DEFAULT As Long = &H800
Private Const HEAP_ZERO_MEMORY As Long = &H8
Private Const FADF_AUTO As Long = 1
Private Type SAFEARRAYBOUND
cElements As Long
lLBound As Long
End Type
Private Type SAFEARRAY
cDims As Integer
fFeatures As Integer
cbElements As Long
cLocks As Long
pvData As Long
Bounds As SAFEARRAYBOUND
End Type
Private Type UUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(7) As Byte
End Type
Private Type PARAMDATA
pszName As String
vt As Integer
End Type
Private Type METHODDATA
pszName As String
ppdata As Long
dispid As Long
iMeth As Long
cc As Long
cArgs As Long
wFlags As Integer
vtReturn As Integer
End Type
Private Type INTERFACEDATA
pmethdata As Long
cMembers As Long
End Type
Private Enum eIUnknownMethods
METH_IUNKNOWN_QI
METH_IUNKNOWN_ADDREF
METH_IUNKNOWN_RELEASE
METH_IUNKNOWN_COUNT
End Enum
Private Enum eISumDiffMethods
METH_ISUMDIFF_SETVAL
METH_ISUMDIFF_GETVAL
METH_ISUMDIFF_SUM
METH_ISUMDIFF_DIFF
METH_ISUMDIFF_COUNT
End Enum
Private Type tIUnknownVTable
pfn(METH_IUNKNOWN_COUNT - 1) As Long
End Type
Private Type tISumDiffVtable
tUnk As tIUnknownVTable
pfn(METH_ISUMDIFF_COUNT - 1) As Long
End Type
Private Type CBaseClass
pVtbl As Long
lRefCounter As Long
pDisp As Long ' // Inner object
End Type
' // CSumDiff class
Private Type CSumDiff
CBase As CBaseClass
lVal1 As Long
lVal2 As Long
End Type
Private Const SIZEOF_CSUMDIFF As Long = &H14
Private Declare Function CreateDispTypeInfo Lib "OleAut32" ( _
ByRef pidata As INTERFACEDATA, _
ByVal lcid As Long, _
ByRef pptinfo As IUnknown) As Long
Private Declare Function CreateStdDispatch Lib "OleAut32" ( _
ByRef punkOuter As Any, _
ByRef pvThis As Any, _
ByVal ptinfo As IUnknown, _
ByRef ppunkStdDisp As Any) As Long
Private Declare Function IsEqualGUID Lib "ole32" ( _
ByRef rguid1 As UUID, _
ByRef rguid2 As UUID) As Long
Private Declare Function vbaObjSetAddref Lib "MSVBVM60.DLL" _
Alias "__vbaObjSetAddref" ( _
ByRef dstObject As Any, _
ByRef srcObjPtr As Any) As Long
Private Declare Function vbaObjSet Lib "MSVBVM60.DLL" _
Alias "__vbaObjSet" ( _
ByRef dstObject As Any, _
ByRef srcObjPtr As Any) As Long
Private Declare Function vbaCastObj Lib "MSVBVM60.DLL" _
Alias "__vbaCastObj" ( _
ByRef dstObject As Any, _
ByRef pIID As UUID) As Long
Private Declare Function HeapAlloc Lib "kernel32" ( _
ByVal hHeap As Long, _
ByVal dwFlags As Long, _
ByVal dwBytes As Long) As Long
Private Declare Function HeapFree Lib "kernel32" ( _
ByVal hHeap As Long, _
ByVal dwFlags As Long, _
ByVal lpMem As Long) As Long
Private Declare Function GetProcessHeap Lib "kernel32" () As Long
Private Declare Function GetMem4 Lib "msvbvm60" ( _
ByRef src As Any, _
ByRef dst As Any) As Long
Private Declare Sub MoveArray Lib "msvbvm60" _
Alias "__vbaAryMove" ( _
ByRef Destination() As Any, _
ByRef Source As Any)
Private Declare Sub IIDFromString Lib "ole32" ( _
ByVal lpsz As Long, _
ByRef lpiid As UUID)
Sub Main()
Dim cObj As Object
' // Restrictions:
' // You should destroy the object before ending
Set cObj = CreateSumDiffObject(10, 20)
cObj(0) = 54
cObj(1) = 20
Debug.Print cObj.Sum
Debug.Print cObj.Diff
cObj.Value(0) = 1231
cObj.Value(1) = 2000
Debug.Print cObj(0)
Debug.Print cObj.Value(1)
Set cObj = Nothing
End Sub
' // Create SumDiff object
Public Function CreateSumDiffObject( _
ByVal lVal1 As Long, _
ByVal lVal2 As Long) As IUnknown
Static tInterfaceInfo As INTERFACEDATA ' // Global data
Static cTypeInfo As IUnknown
Dim tObj() As CSumDiff
Dim pObj As Long
Dim tArrDesk As SAFEARRAY
Dim hr As Long
Dim cUnkDisp As IUnknown
' // Get the interface data and create TypeInfo
If tInterfaceInfo.pmethdata = 0 Then
tInterfaceInfo = CSumDiff_InterfaceData()
hr = CreateDispTypeInfo(tInterfaceInfo, LOCALE_SYSTEM_DEFAULT, cTypeInfo)
If hr < 0 Then Exit Function
End If
' // Alloc memory for the object and map it to the array item
pObj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, SIZEOF_CSUMDIFF)
tArrDesk.cDims = 1
tArrDesk.Bounds.cElements = 1
tArrDesk.fFeatures = FADF_AUTO ' // Don't free memory after destroying of array
tArrDesk.pvData = pObj
tArrDesk.cbElements = SIZEOF_CSUMDIFF
MoveArray tObj, VarPtr(tArrDesk)
' // Call constructor
If CSumDiff_CSumDiff(tObj(0)) = 0 Then
HeapFree GetProcessHeap(), 0, ByVal pObj
Exit Function
End If
' // Initial values
tObj(0).lVal1 = lVal1
tObj(0).lVal2 = lVal2
' // Create aggregated IDispatch
hr = CreateStdDispatch(tObj(0), tObj(0), cTypeInfo, tObj(0).CBase.pDisp)
If hr < 0 Then
HeapFree GetProcessHeap(), 0, ByVal pObj
Exit Function
End If
vbaObjSetAddref CreateSumDiffObject, ByVal pObj
Debug.Print "Object CSumDiff created at 0x" & Hex$(pObj)
End Function
Private Function FAR_PROC( _
ByVal lValue As Long) As Long
Dim bIsInIDE As Boolean
' // AddressOf statement actually returns the pointer to the small thunk which checks if code is running or not
' // When you see an object variable in the Watch window the code is stopped. We need to avoid that behavior
' // because some interfaces returns the HRESULT value and it'll cause unexpected behavior (return S_OK)
Debug.Assert MakeTrue(bIsInIDE)
If bIsInIDE Then
GetMem4 ByVal lValue + &H16, FAR_PROC ' // Skip thunk
Else
FAR_PROC = lValue
End If
End Function
Private Function MakeTrue( _
ByRef bValue As Boolean) As Boolean
MakeTrue = True
bValue = True
End Function
' // Base class constructor
Private Function CBaseClass_CBaseClass( _
ByRef tObj As CBaseClass) As Long
Static tVtable As tIUnknownVTable
tObj.lRefCounter = 0
tObj.pVtbl = VarPtr(tVtable)
If tVtable.pfn(METH_IUNKNOWN_QI) = 0 Then
tVtable.pfn(METH_IUNKNOWN_QI) = FAR_PROC(AddressOf CBaseClass_QueryInterface)
tVtable.pfn(METH_IUNKNOWN_ADDREF) = FAR_PROC(AddressOf CBaseClass_AddRef)
tVtable.pfn(METH_IUNKNOWN_RELEASE) = FAR_PROC(AddressOf CBaseClass_Release)
End If
CBaseClass_CBaseClass = 1
End Function
' // Base class methods
Private Function CBaseClass_QueryInterface( _
ByRef tObj As CBaseClass, _
ByRef tiid As UUID, _
ByRef pOut As Long) As Long
Static tIUnk As UUID, tIDisp As UUID
If tIUnk.Data1 = 0 Then
IIDFromString StrPtr("{00000000-0000-0000-C000-000000000046}"), tIUnk
IIDFromString StrPtr("{00020400-0000-0000-C000-000000000046}"), tIDisp
End If
Select Case True
Case IsEqualGUID(tiid, tIUnk)
pOut = VarPtr(tObj)
CBaseClass_AddRef tObj
Case IsEqualGUID(tiid, tIDisp)
' // Return aggregable object
pOut = vbaCastObj(ByVal tObj.pDisp, tIDisp)
Case Else
pOut = 0: CBaseClass_QueryInterface = E_NOINTERFACE
Exit Function
End Select
End Function
Private Function CBaseClass_AddRef( _
ByRef tObj As CBaseClass) As Long
tObj.lRefCounter = tObj.lRefCounter + 1
CBaseClass_AddRef = tObj.lRefCounter
End Function
Private Function CBaseClass_Release( _
ByRef tObj As CBaseClass) As Long
tObj.lRefCounter = tObj.lRefCounter - 1
CBaseClass_Release = tObj.lRefCounter
If CBaseClass_Release = 0 Then
vbaObjSet tObj.pDisp, ByVal 0&
' // Destructor
HeapFree GetProcessHeap(), 0, VarPtr(tObj)
Debug.Print "Object was destroyed at 0x" & Hex$(VarPtr(tObj))
End If
End Function
' // CSumDiff constructor
Private Function CSumDiff_CSumDiff( _
ByRef tObj As CSumDiff) As Long
Static tVtable As tISumDiffVtable
If CBaseClass_CBaseClass(tObj.CBase) = 0 Then Exit Function
tObj.CBase.pVtbl = VarPtr(tVtable)
tObj.lVal1 = 0
tObj.lVal2 = 0
If tVtable.pfn(METH_ISUMDIFF_SETVAL) = 0 Then
tVtable.tUnk.pfn(METH_IUNKNOWN_QI) = FAR_PROC(AddressOf CBaseClass_QueryInterface)
tVtable.tUnk.pfn(METH_IUNKNOWN_ADDREF) = FAR_PROC(AddressOf CBaseClass_AddRef)
tVtable.tUnk.pfn(METH_IUNKNOWN_RELEASE) = FAR_PROC(AddressOf CBaseClass_Release)
tVtable.pfn(METH_ISUMDIFF_SETVAL) = FAR_PROC(AddressOf CSumDiff_SetVal)
tVtable.pfn(METH_ISUMDIFF_GETVAL) = FAR_PROC(AddressOf CSumDiff_GetVal)
tVtable.pfn(METH_ISUMDIFF_SUM) = FAR_PROC(AddressOf CSumDiff_Sum)
tVtable.pfn(METH_ISUMDIFF_DIFF) = FAR_PROC(AddressOf CSumDiff_Diff)
End If
CSumDiff_CSumDiff = 1
End Function
' // CSumDiff methods
Private Function CSumDiff_SetVal( _
ByRef tObj As CSumDiff, _
ByVal lIndex As Long, _
ByVal lValue As Long) As Long
Select Case lIndex
Case 0: tObj.lVal1 = lValue
Case 1: tObj.lVal2 = lValue
Case Else: CSumDiff_SetVal = E_INVALIDARG
End Select
End Function
Private Function CSumDiff_GetVal( _
ByRef tObj As CSumDiff, _
ByVal lIndex As Long) As Long
Select Case lIndex
Case 0: CSumDiff_GetVal = tObj.lVal1
Case 1: CSumDiff_GetVal = tObj.lVal2
Case Else: Err.Raise 5
End Select
End Function
' // Calculate sum
Private Function CSumDiff_Sum( _
ByRef tObj As CSumDiff) As Long
CSumDiff_Sum = tObj.lVal1 + tObj.lVal2
End Function
' // Calculate difference
Private Function CSumDiff_Diff( _
ByRef tObj As CSumDiff) As Long
CSumDiff_Diff = tObj.lVal1 - tObj.lVal2
End Function
' // CSumDiff interface data
Private Function CSumDiff_InterfaceData() As INTERFACEDATA
Static tData As INTERFACEDATA
Static tMembers() As METHODDATA
Static tParams0() As PARAMDATA
Static tParams1() As PARAMDATA
Static tParams2() As PARAMDATA
If tData.pmethdata = 0 Then
ReDim tMembers(METH_ISUMDIFF_COUNT - 1)
tData.cMembers = METH_ISUMDIFF_COUNT
tData.pmethdata = VarPtr(tMembers(0))
' // [DEFAULT] HRESULT CSumDiff.Value (Byval Index As Long, Byval Value as Long)
tParams0 = SetParamData("Index", vbLong, "Value", vbLong)
tMembers(METH_ISUMDIFF_SETVAL) = SetMethodData("Value", 0, 3, vbError, DISPATCH_PROPERTYPUT, tParams0())
' // [DEFAULT] Long CSumDiff.Value (Byval Index As Long)
tParams1 = SetParamData("Index", vbLong)
tMembers(METH_ISUMDIFF_GETVAL) = SetMethodData("Value", 0, 4, vbLong, DISPATCH_PROPERTYGET Or DISPATCH_METHOD, tParams1())
' // Long CSumDiff.Sum ()
tMembers(METH_ISUMDIFF_SUM) = SetMethodData("Sum", 3, 5, vbLong, DISPATCH_METHOD, tParams2())
' // Long CSumDiff.Diff ()
tMembers(METH_ISUMDIFF_DIFF) = SetMethodData("Diff", 4, 6, vbLong, DISPATCH_METHOD, tParams2())
End If
CSumDiff_InterfaceData = tData
End Function
' // Fast creation
Private Function SetParamData( _
ParamArray vData() As Variant) As PARAMDATA()
Dim vIndex As Variant
Dim lIndex As Long
Dim tRet() As PARAMDATA
ReDim tRet(UBound(vData) \ 2)
For Each vIndex In vData
If VarType(vIndex) = vbString Then
tRet(lIndex).pszName = vIndex
Else
tRet(lIndex).vt = vIndex
lIndex = lIndex + 1
End If
Next
SetParamData = tRet
End Function
' // Fast creation
Private Function SetMethodData( _
ByRef sName As String, _
ByVal lDispID As Long, _
ByVal lMethIndex As Long, _
ByVal lRetValType As Long, _
ByVal lFlags As Long, _
ByRef tParams() As PARAMDATA) As METHODDATA
With SetMethodData
.cc = CC_STDCALL
.dispid = lDispID
.iMeth = lMethIndex
.pszName = sName
.vtReturn = lRetValType
.wFlags = lFlags
If Not Not tParams Then
.ppdata = VarPtr(tParams(0))
.cArgs = UBound(tParams) + 1
End If
End With
End Function