-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathVbaObject.cls
369 lines (327 loc) · 13.8 KB
/
VbaObject.cls
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
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "VbaObject"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit
Private Type PublicObjectDescriptor
lpObjectInfo As LongPtr ' 0x00
Reserved1 As LongLong ' 0x08
lpPublicVariableIntegers As LongPtr ' 0x10
lpStaticVariableIntegers As LongPtr ' 0x18
lpDataPublicVariables As LongPtr ' 0x20
lpDataStaticVariables As LongPtr ' 0x28
lpModuleName As LongPtr ' 0x30 'Pointer to char[]
MethodCount As LongLong ' 0x38
lpMethodNamePtrArray As LongPtr ' 0x40 'Array of pointers to char[]
OffsetToStaticVariables As Long ' 0x48
Flags As Long ' 0x4C
Null1 As Long ' 0x50
End Type
Private Type ObjectInfo
ReferenceCount As Integer ' 0x00
ObjectIndex As Integer ' 0x02
DWord1 As Long ' 0x04
lpObjectTable As LongPtr ' 0x08
Ptr1 As LongPtr ' 0x10
lpObjectMethodData As LongPtr ' 0x18 'see ObjectMethodData
QWord1 As LongLong ' 0x20
QWord2 As LongLong ' 0x28
lpPublicObjectDescriptor As LongPtr ' 0x30 'Points directly to the object descriptor itself
Ptr3 As LongPtr ' 0x38 'Points to something that is generally null
MethodCount As Integer ' 0x40
MethodCount2 As Integer ' 0x42 'Count of compiled methods?
DWord2 As Long ' 0x44
lpMethodInfoPointers As LongPtr ' 0x48 'Array of pointers to MethodInfo structures
Word1 As Integer ' 0x50 'Constants in constant pool?
Word2 As Integer ' 0x52 'Constants to allocate in constant pool?
DWord3 As Long ' 0x54
Ptr4 As LongPtr ' 0x58
lpStringTable As LongPtr ' 0x60 'Array of pointers to literal strings used in module?
QWord3 As LongLong ' 0x68 'Max size of ObjectInfo = 0x70
End Type
Private Type ObjectMethodData
lpPtr1 As LongPtr
lpObjectInfo As LongPtr
Reserved1 As LongLong
Null1 As LongLong
Null2 As LongLong
lpMethodDataArray As LongPtr ' 0x28 'Array of pointers to MethodData
lpMethodData2Array As LongPtr ' 0x30 'Array of pointers to MethodData2
End Type
Private mOriginalAddress As LongPtr
Private mOriginalObjectInfoAddress As LongPtr
Private mObjectInfo As ObjectInfo
Private mPublicObjectDescriptor As PublicObjectDescriptor
Private mObjectInfoSize As Long
Private mPublicObjectDescriptorSize As Long
Private mModuleName As String
Private mMethods() As IVbaMethod
Private mWeakPointerToParent As LongPtr
Private mlpMethodDataArray As LongPtr, mlpMethodData2Array As LongPtr, mlpMethodNamePtrArray As LongPtr
Private mMethodCount As Long
Private mObjectMethodData As ObjectMethodData
Private mObjectMethodDataSize As Long
Private Const CLASS_NAME As String = "VbaObject"
Implements IVbaObject
Implements IVbaMethods
Implements IChildOfDisposable
Public Property Get PublicObjectDescriptorSize() As Long
PublicObjectDescriptorSize = mPublicObjectDescriptorSize
End Property
Private Sub IChildOfDisposable_DisposeOfParent()
Set Me.ChildOfDisposable.Parent = Nothing
End Sub
Private Property Set IChildOfDisposable_Parent(RHS As Object)
mWeakPointerToParent = ObjPtr(RHS)
End Property
Private Property Get IVbaObject_Methods() As IVbaMethods
Set IVbaObject_Methods = Me
End Property
Private Property Get IVbaMethods_Parent() As IVbaObject
Set IVbaMethods_Parent = Me
End Property
Private Sub Class_Initialize()
'Alas that we can't use a static class variable.
Const METHOD_NAME As String = CLASS_NAME & ".Class_Initialize"
On Error GoTo HandleError
mObjectInfoSize = LenB(mObjectInfo)
mPublicObjectDescriptorSize = LenB(mPublicObjectDescriptor)
mObjectMethodDataSize = LenB(mObjectMethodData)
Exit Sub
HandleError:
If Err.Source <> METHOD_NAME Then
Err.Raise Err.Number, METHOD_NAME & "." & Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
Else
Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
End If
End Sub
Public Sub Setup(lpPublicObjectDescriptor As LongPtr, Optional Parent As IVbaObjects = Nothing)
Const METHOD_NAME = CLASS_NAME & ".Setup"
On Error GoTo HandleError
Set Me.ChildOfDisposable.Parent = Parent
Call LoadPublicObjectDescriptor(lpPublicObjectDescriptor)
Exit Sub
HandleError:
If Err.Source <> METHOD_NAME Then
Err.Raise Err.Number, METHOD_NAME & "." & Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
Else
Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
End If
End Sub
Private Sub LoadPublicObjectDescriptor(lpAddress As LongPtr)
Const METHOD_NAME As String = CLASS_NAME & ".LoadPublicObjectDescriptor"
On Error GoTo HandleError
Dim n(0 To 1023) As Byte
mOriginalAddress = lpAddress
Memory.FollowPointer VarPtr(mPublicObjectDescriptor), lpAddress, mPublicObjectDescriptorSize
With mPublicObjectDescriptor
Memory.FollowPointer VarPtr(n(0)), .lpModuleName, MAX_VBA_OBJECT_NAME_LENGTH
mModuleName = RTrimNull(VBA.StrConv(n, vbUnicode))
If .Reserved1 <> -1 Then '&HFFFFFFFF in x86, &HFFFFFFFFFFFFFFFF in x64
'Invalid POD. - Always -1
GoTo InvalidObjectInfo
ElseIf .MethodCount < 0 Or .MethodCount > &H100 Then
'Invalid POD - Max methods allowed is 255 (&HFF)
GoTo InvalidObjectInfo
End If
Call LoadObjectInfo(.lpObjectInfo)
' Error handling in LoadObjectInfo ensures that this won't be loaded if it's invalid.
End With
Exit Sub
InvalidObjectInfo:
Exit Sub
HandleError:
If Err.Source <> METHOD_NAME Then
Err.Raise Err.Number, METHOD_NAME & "." & Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
Else
Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
End If
End Sub
Private Property Get IVbaObject_Name() As String
Const METHOD_NAME As String = "IVbaObject_Name[" & CLASS_NAME & "]"
On Error GoTo HandleError
IVbaObject_Name = mModuleName: Exit Property
HandleError:
If Err.Source <> METHOD_NAME Then
Err.Raise Err.Number, METHOD_NAME & "." & Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
Else
Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
End If
End Property
Private Sub LoadObjectInfo(lpAddress As LongPtr)
Const METHOD_NAME As String = CLASS_NAME & ".LoadObjectInfo"
On Error GoTo InvalidObject
Memory.FollowPointer VarPtr(mObjectInfo), lpAddress, mObjectInfoSize
'Validate the objects
With mObjectInfo
Debug.Assert .lpPublicObjectDescriptor = mOriginalAddress
If .lpPublicObjectDescriptor <> mOriginalAddress Then
GoTo InvalidObject
Exit Sub
End If
End With
mOriginalObjectInfoAddress = lpAddress
LoadObjectMethodData
Exit Sub
HandleError:
mOriginalObjectInfoAddress = 0
mPublicObjectDescriptor.lpObjectInfo = 0
If Err.Source <> METHOD_NAME Then
Err.Raise Err.Number, METHOD_NAME & "." & Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
Else
Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
End If
Exit Sub
InvalidObject:
mOriginalObjectInfoAddress = 0
mPublicObjectDescriptor.lpObjectInfo = 0
End Sub
Private Sub LoadObjectMethodData()
Memory.FollowPointer VarPtr(mObjectMethodData), mObjectInfo.lpObjectMethodData, mObjectMethodDataSize
Call SetupMethods(mObjectMethodData.lpMethodDataArray, mObjectMethodData.lpMethodData2Array, mPublicObjectDescriptor.lpMethodNamePtrArray, CLng(mObjectInfo.MethodCount))
End Sub
Public Sub SetupMethods(ByVal lpMethodDataArray As LongPtr, ByVal lpMethodData2Array As LongPtr, ByVal lpMethodNamePtrArray As LongPtr, Optional MethodCount As Long = 0)
Const METHOD_NAME = CLASS_NAME & ".SetupMethods"
On Error GoTo HandleError
mlpMethodDataArray = lpMethodDataArray
mlpMethodData2Array = lpMethodData2Array
mlpMethodNamePtrArray = lpMethodNamePtrArray
mMethodCount = MethodCount
If MethodCount > 0 Then
ReDim mMethods(0 To MethodCount - 1)
Else
Erase mMethods
End If
Exit Sub
HandleError:
If Err.Source <> METHOD_NAME Then
Err.Raise Err.Number, METHOD_NAME & "." & Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
Else
Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
End If
End Sub
Private Property Get IVbaMethods_Count() As Long
Const METHOD_NAME As String = "IVbaMethods_Count[" & CLASS_NAME & "]"
On Error GoTo HandleError
IVbaMethods_Count = mMethodCount: Exit Property
HandleError:
If Err.Source <> METHOD_NAME Then
Err.Raise Err.Number, METHOD_NAME & "." & Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
Else
Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
End If
End Property
Private Property Get IVbaMethods_Item(Index As Long) As IVbaMethod
Const METHOD_NAME As String = "IVbaMethods_Item[" & CLASS_NAME & "]"
On Error GoTo HandleError
If Index < Me.Count Then
Dim lpMyMethodData As LongPtr, lpMyMethodData2 As LongPtr, lpMyMethodName As LongPtr
Memory.FollowPointer VarPtr(lpMyMethodData), mlpMethodDataArray + (POINTER_SIZE * Index), POINTER_SIZE
Memory.FollowPointer VarPtr(lpMyMethodData2), mlpMethodData2Array + (POINTER_SIZE * Index), POINTER_SIZE
Memory.FollowPointer VarPtr(lpMyMethodName), mlpMethodNamePtrArray + (POINTER_SIZE * Index), POINTER_SIZE
Set IVbaMethods_Item = PrivateFactory.CreateVbaMethod(lpMyMethodData, lpMyMethodData2, lpMyMethodName)
'This is to maintain the ability to notify the method that it's parent has been disposed
Set mMethods(Index) = IVbaMethods_Item
End If
Exit Property
HandleError:
If Err.Source <> METHOD_NAME Then
Err.Raise Err.Number, METHOD_NAME & "." & Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
Else
Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
End If
End Property
Private Function IVbaMethods_Find(Identifier As IMethodIdentifier, Optional Criteria As MethodComparisonResult = MethodComparisonResult.Matches) As IVbaMethod
Const METHOD_NAME As String = "IVbaMethods_Find[" & CLASS_NAME & "]"
On Error GoTo HandleError
Dim i As Long
Dim theMethod As IVbaMethod
Dim compareResult As MethodComparisonResult
With Me
For i = 0 To .Count - 1
Set theMethod = .Item(i)
compareResult = theMethod.Compare(Identifier)
If (compareResult And Criteria) = Criteria Then
Set IVbaMethods_Find = theMethod
Exit Function
End If
Next
End With
Exit Function
HandleError:
If Err.Source <> METHOD_NAME Then
Err.Raise Err.Number, METHOD_NAME & "." & Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
Else
Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
End If
End Function
Public Property Get Methods() As IVbaMethods
Const METHOD_NAME As String = CLASS_NAME & ".get_Methods"
On Error GoTo HandleError
Set Methods = IVbaObject_Methods
Exit Property
HandleError:
If Err.Source <> METHOD_NAME Then
Err.Raise Err.Number, METHOD_NAME & "." & Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
Else
Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
End If
End Property
Public Property Get Name() As String
Const METHOD_NAME As String = CLASS_NAME & ".get_Name"
On Error GoTo HandleError
Name = Me.Methods.Parent.Name
Exit Property
HandleError:
If Err.Source <> METHOD_NAME Then
Err.Raise Err.Number, METHOD_NAME & "." & Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
Else
Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
End If
End Property
Public Property Get Count() As Long
Const METHOD_NAME As String = CLASS_NAME & ".get_Count"
On Error GoTo HandleError
Count = Me.Methods.Count
Exit Property
HandleError:
If Err.Source <> METHOD_NAME Then
Err.Raise Err.Number, METHOD_NAME & "." & Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
Else
Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
End If
End Property
Public Property Get Item(Index As Long) As IVbaMethod
Const METHOD_NAME As String = CLASS_NAME & ".get_Item"
On Error GoTo HandleError
Set Item = Me.Methods.Item(Index)
Exit Property
HandleError:
If Err.Source <> METHOD_NAME Then
Err.Raise Err.Number, METHOD_NAME & "." & Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
Else
Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
End If
End Property
Private Property Get IVbaObject_Parent() As IVbaObjects
If mWeakPointerToParent = 0 Then Exit Property
Set IVbaObject_Parent = PrivateFactory.GetObjectFromWeakPointer(mWeakPointerToParent)
End Property
Private Sub Class_Terminate()
Dim i As Long
Dim DC As IChildOfDisposable
For i = 1 To IVbaMethods_Count
Set DC = mMethods(i - 1)
If Not DC Is Nothing Then
DC.DisposeOfParent
End If
Next
End Sub
Public Property Get ChildOfDisposable() As IChildOfDisposable
Set ChildOfDisposable = Me
End Property