-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathManagedCharSafeArray.cls
334 lines (313 loc) · 15.1 KB
/
ManagedCharSafeArray.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
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "ManagedCharSafeArray"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Attribute VB_Description = "Creates a SafeArray descriptor to be associated with a managed array of characters represented by an Integer array of Unicode values. \r\n\r\nVBA-IDictionary v2.2 (February 05, 2023)\r\n(c) Mark Johnstone - https://github.com/MarkJohnstoneGitHub/VBA-IDictionary\r\nAuthor: markjohnstone@hotmail.com\r\n"
''
'Rubberduck annotations
'@ModuleDescription "Creates a SafeArray descriptor to be associated with a managed array of characters represented by an Integer array of Unicode values. \r\n\r\nVBA-IDictionary v2.2 (February 05, 2023)\r\n(c) Mark Johnstone - https://github.com/MarkJohnstoneGitHub/VBA-IDictionary\r\nAuthor: markjohnstone@hotmail.com\r\n"
'@Folder("VBA-IScriptingDictionary.Data Types.SafeArray")
'@PredeclaredId
''
''
'@Version VBA-IScriptingDictionary v2.2 (February 05, 2023)
'(c) Mark Johnstone - https://github.com/MarkJohnstoneGitHub/VBA-IDictionary
'@Description Creates a managed Char Array where a specified string is overlayed into the SafeArray.pvData
'@Author Mark Johnstone markjohnstone@hotmail.com
'@LastModified February 05, 2023
'@Dependencies
' TypeSafeArray.bas
'
'@Usage eg Display the Unicode characters of a string
' Dim managedChars() As Integer
' Dim managedCharsDescriptor As ManagedCharSafeArray
' Set managedCharsDescriptor = ManagedCharSafeArray.Create(managedChars)
' Dim text As String
' text = "ABCDabcd"
' managedCharsDescriptor.AllocateCharData text
' Dim index As Long
' For index = LBound(managedChars) To UBound(managedChars)
' Debug.Print managedChars(index)
' Next
' managedCharsDescriptor.Dispose
'
'Errors Raised:
'@Error 5 Invalid procedure call or argument
' Raised when the SafeArray descriptor has already been allocated to a managed Char array.
' Raised when the managed Char array specified isn't an unitialised Integer Array.
'@Error 10 This array is fixed or temporary locked.
' Raised when the managedChars array is attempted to resized as it is locked.
'
'@Remarks
' The ManagedCharSafeArray manipulates a SafeArray descriptor without the use
' of the SafeArray API.
' Each integer element in the managed Char Array represents a Unicode character
' for the specified string using the method AllocateCharData.
' The Integer/managed Char array specified must be uninitialize,
' to allow a SafeArray descriptor to be allocated.
' The calling code cannot resize the integer array returned as it is locked,
' however the array contents may be altered.
' Attempting to resize the managed Chars array will result in the
' Runtime Error 10 This array is fixed or temporary locked.
' The SafeArray descriptor should be freed from the array by calling the
' Dispose method, this is to ensure that it is freed before
' the managed chars array goes out of scope.
'
' For futher reading regarding the SafeArray descriptor see:
' https://doxygen.reactos.org/db/d60/dll_2win32_2oleaut32_2safearray_8c_source.html
' https://stackoverflow.com/questions/18784470/where-is-safearray-var-type-stored
'------------------------------------------------------------'
Option Explicit
'============================================='
'Constants
'============================================='
#If VBA7 Then
Private Const NULL_PTR As LongPtr = 0
#Else
Private Const NULL_PTR As Long = 0
#End If
Private Const SIZEOF_VBA_INTEGER As Long = 2
'============================================='
'API Declarations
'============================================='
''
'@API_Declaration
'API's declarations for copy memory by pointer for Windows and Mac, with VBA6 and VBA7 compatibility.
'See https://stackoverflow.com/questions/45756170/how-to-read-write-memory-on-mac-os-x-with-vba
'------------------------------------------------------------'
#If Mac Then
#If Win64 Then
Private Declare PtrSafe Function CopyMemoryByPtr Lib "libc.dylib" Alias "memmove" _
(ByVal destination As LongPtr, _
ByVal source As LongPtr, _
ByVal size As Long) _
As LongPtr
#Else
Private Declare Function CopyMemoryByPtr Lib "libc.dylib" Alias "memmove" _
(ByVal destination As Long, _
ByVal src As Long, _
ByVal size As Long) _
As Long
#End If
#ElseIf VBA7 Then
#If Win64 Then
Private Declare PtrSafe Sub CopyMemoryByPtr Lib "kernel32" Alias "RtlMoveMemory" _
(ByVal destination As LongPtr, _
ByVal source As LongPtr, _
ByVal size As LongLong)
#Else
Private Declare PtrSafe Sub CopyMemoryByPtr Lib "kernel32" Alias "RtlMoveMemory" _
(ByVal destination As LongPtr, _
ByVal source As LongPtr, _
ByVal size As Long)
#End If
#Else
Private Declare Sub CopyMemoryByPtr Lib "kernel32" Alias "RtlMoveMemory" _
(ByVal destination As Long, _
ByVal src As Long, _
ByVal size As Long)
#End If
''
'@API_Declaration
'API's declarations for CopyAnyToMemory for Windows and Mac, with VBA6 and VBA7 compatibility.
'------------------------------------------------------------'
#If Mac Then
#If Win64 Then
Private Declare PtrSafe Function CopyAnyToMemory Lib "libc.dylib" Alias "memmove" _
(ByVal destination As LongPtr, _
ByRef source As Any, _
ByVal size As Long) _
As LongPtr
#Else
Private Declare Function CopyAnyToMemory Lib "libc.dylib" Alias "memmove" _
(ByVal destination As Long, _
ByVal source As Any, _
ByVal size As Long) _
As Long
#End If
#ElseIf VBA7 Then
#If Win64 Then
Private Declare PtrSafe Sub CopyAnyToMemory Lib "kernel32.dll" Alias "RtlMoveMemory" _
(ByVal destination As LongPtr, _
ByRef source As Any, _
ByVal size As LongLong)
#Else
Private Declare PtrSafe Sub CopyAnyToMemory Lib "kernel32.dll" Alias "RtlMoveMemory" _
(ByVal destination As LongPtr, _
ByRef source As Any, _
ByVal size As Long)
#End If
#Else
Private Declare Sub CopyAnyToMemory Lib "kernel32.dll" Alias "RtlMoveMemory" _
(ByVal destination As Long, _
ByRef source As Any, _
ByVal size As Long)
#End If
''
'@API_Declaration for VarPtrArray
'------------------------------------------------------------'
#If VBA7 Then
Private Declare PtrSafe Function VarPtrArray Lib "VBE7" Alias _
"VarPtr" (ByRef var() As Any) As LongPtr
#Else
Private Declare PtrSafe Function VarPtrArray Lib "VBE6" Alias _
"VarPtr" (ByRef var() As Any) As Long
#End If
'============================================='
'Types
'============================================='
Private Type TSafeArrayDescriptor
vt(15) As Byte 'Array variable type of 16 bytes which precedes the SafeArray pointer.
charSafeArray As SafeArray1D 'SafeArray Structure for one dimensional array.
End Type
#If VBA7 Then
Private Type TManagedCharArray
saDescriptor As TSafeArrayDescriptor 'SafeArray descriptor structure for a one dimensional Integer array.
pManagedChars As LongPtr 'Pointer to managed Char array associated with the SafeArray descriptor.
End Type
#Else
Private Type TManagedCharArray
saDescriptor As TSafeArrayDescriptor 'SafeArray descriptor structure for a one dimensional Integer array.
pManagedChars As Long 'Pointer to managed Char array associated with the SafeArray descriptor.
End Type
#End If
'============================================='
'Private Variables
'============================================='
Private this As TManagedCharArray
'============================================='
'Constructors and destructors
'============================================='
Private Sub Class_Initialize()
CreateSafeArrayDescriptor
End Sub
Private Sub Class_Terminate()
Dispose
End Sub
'============================================='
'Public Methods
'============================================='
''
'@Description("Creates a new instance of a ManagedCharSafeArray for the managed Char array specified.")
'@param outManagedChars() The uninitialised managed Char array to be allocated a SafeArray descriptor.
'@Remarks
' The Integer array specified must be uninitialize to allow a SafeArray descriptor to be allocated.
'------------------------------------------------------------'
Public Function Create(ByRef outManagedChars() As Integer) As ManagedCharSafeArray
Attribute Create.VB_Description = "Creates a new instance of a ManagedCharSafeArray for the managed Char array specified."
Dim newManagedCharSafeArray As ManagedCharSafeArray
Set newManagedCharSafeArray = New ManagedCharSafeArray
newManagedCharSafeArray.AllocateSafeArrayToCharArray outManagedChars
Set Create = newManagedCharSafeArray
End Function
''
'@Description("Allocatates the specified string data to the managed Char array.")
'@param inText The specified string to be allocated to the managed Char array SafeArray.pvData
'@Remarks
' For the specified String updates the SafeArray.pvData and the SafeArray.cElements
' according to its character length.
'------------------------------------------------------------'
Public Sub AllocateCharData(ByRef inText As String)
Attribute AllocateCharData.VB_Description = "Allocatate the specified string data to the SafeArray.pvData"
this.saDescriptor.charSafeArray.pvData = StrPtr(inText)
this.saDescriptor.charSafeArray.cElements = Len(inText)
End Sub
''
'@Description("Allocates the SafeArray descriptor to the specified managed Char array.")
'@param outManagedChars The uninitialised managed Char array to be allocated a SafeArray descriptor.
'@Remarks
' The Integer array specified must be uninitialize to allow a SafeArray descriptor to be allocated.
' The calling code cann't resize the integer array returned as it is locked,
' however the array contents may be altered.
' The SafeArray descriptor should be freed from the manged array before it goes out of scope,
' by calling the Dispose method.
'@Error 5 Invalid procedure call or argument
' Raised when the SafeArray descriptor has already been allocated to a managed Char array.
' Raised when the managed Char array specified isn't an unitialised Integer Array.
'------------------------------------------------------------'
Public Sub AllocateSafeArrayToCharArray(ByRef outManagedChars() As Integer)
Attribute AllocateSafeArrayToCharArray.VB_Description = "Allocates the SafeArray descriptor to the specified managed Char array."
#If VBA7 Then
Dim pOutManagedChars As LongPtr
'@Ignore VariableNotAssigned
Dim pSafeArray As LongPtr
#Else
Dim pOutManagedChars As Long
Dim pSafeArray As Long
#End If
'Obtain the array pointer of outManagedChars
pOutManagedChars = VarPtrArray(outManagedChars())
'Obtain the SafeArray pointer of outManagedChars
CopyMemoryByPtr VarPtr(pSafeArray), pOutManagedChars, LenB(pSafeArray)
'Allocate the SafeArray descriptor to the specified unitialize integer array
If pSafeArray = NULL_PTR Then
'The SafeArray descriptor can only be allocted to one managed Char array at a time
If this.pManagedChars = NULL_PTR Then
#If VBA7 Then
Dim pNewSafeArray As LongPtr
#Else
Dim pNewSafeArray As Long
#End If
pNewSafeArray = VarPtr(this.saDescriptor.charSafeArray)
'Copy the new SafeArray pointer to the managed chars array
CopyMemoryByPtr pOutManagedChars, VarPtr(pNewSafeArray), LenB(this.pManagedChars)
'set the managed Char array pointer that is associated with the SafeArray descriptor
this.pManagedChars = pOutManagedChars
Else
VBA.Err.Raise 5, "ManagedCharSafeArray.AllocateSafeArrayToCharArray", "The SafeArray descriptor has already been allocated to a managed Char array."
End If
Else
VBA.Err.Raise 5, "ManagedCharSafeArray.AllocateSafeArrayToCharArray", "Cannot assign an initialise Integer array to the managed Char array."
End If
End Sub
''
'@Description("Disposes of the SafeArray descriptor from the managed Char array.")
'------------------------------------------------------------'
Public Sub Dispose()
Attribute Dispose.VB_Description = "Disposes of the SafeArray descriptor from the managed Char array."
DisposeData
DisposeManagedCharArray
End Sub
'============================================='
'Private Methods
'============================================='
''
'@Description("Creates initialized and empty SafeArray descriptor for a one dimensional Integer array, which is locked.")
'@Remarks
' Creates the managed Chars SafeArray descriptor for a read-only, one dimensional Integer array, which
' is only resized and char data added calling the AllocateCharData method.
' The four byte DWord preceding the SafeArray pointer containts the Variable Type
' of an Array when fFeatures = FADF_HAVEVARTYPE is set.
'------------------------------------------------------------'
Private Sub CreateSafeArrayDescriptor()
Attribute CreateSafeArrayDescriptor.VB_Description = "Creates initialized and empty SafeArray descriptor for a one dimensional Integer array, which is locked."
'The last four byte DWord contains the array variable type for fFeatures = FADF_HAVEVARTYPE
this.saDescriptor.vt(12) = CByte(SAFEARRAY_VT.VT_INTEGER)
this.saDescriptor.charSafeArray.cbElements = SIZEOF_VBA_INTEGER
this.saDescriptor.charSafeArray.cDims = 1 'One dimensional
this.saDescriptor.charSafeArray.fFeatures = SafeArrayFeatures.FADF_HAVEVARTYPE
this.saDescriptor.charSafeArray.cLocks = 1 'lock the array from being resized
End Sub
''
'@Description("Disposes of the SafeArray.pvData by setting a null pointer and resets the number of elements to zero")
'------------------------------------------------------------'
Private Sub DisposeData()
Attribute DisposeData.VB_Description = "Disposes of the SafeArray.pvData by setting a null pointer and resets the number of elements to zero"
this.saDescriptor.charSafeArray.pvData = NULL_PTR
this.saDescriptor.charSafeArray.cElements = 0
End Sub
''
'@Description("Disposes of the SafeArray descriptor from its associated managed Char array.")
'------------------------------------------------------------'
Private Sub DisposeManagedCharArray()
Attribute DisposeManagedCharArray.VB_Description = "Disposes of the SafeArray descriptor from its associated managed Char array."
If Not this.pManagedChars = NULL_PTR Then
CopyAnyToMemory this.pManagedChars, NULL_PTR, LenB(this.pManagedChars)
this.pManagedChars = NULL_PTR
End If
End Sub