Expert VB6 developers use the ObjPtr semi-undocumented keyword to implement a variety of advanced programming techniques. The ObjPtr keyword isn’t available in VB.NET, yet VB Migration Partner allows you to convert most of code blocks that use ObjPtr with minimal effort. The actual solution to the problem depends on the reason why ObjPtr is used in the original VB6 code.
Weak object references – 1st case
By far, the most common usage for ObjPtr is for implementing the so-called weak references under VB6. A weak reference is an object reference that doesn’t keep the referenced object alive. Implementing weak object references is often necessary under VB6 to build complex object hierarchies. For example, whenever you have a parent-child relationship and you need both the parent object and the child object have a reference to the other, you absolutely need a weak reference under VB6, because a regular (strong) reference would keep the two objects alive indefinitively.
To illustrate weak object references under VB6, let’s assume that you have defined a Person object that has a Parent property and a Child property. These two properties are used to create a hierarchy of objects. However, if a strong object reference were used for both properties then a circular reference would have been created, which in turn would cause a memory leak when the main program clears all direct references to both the parent and the child object. To avoid the circular reference problem, a weak object reference is used for one of the two property:
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(lpDest As Any, lpSource As Any, ByVal nCount As Long)
Private m_Child As Person
Private m_ParentPtr As Long
Public Property Get Child() As Person
Set Child = m_Child
End Property
Public Property Set Child(ByVal value As Person)
Set m_Child = value
End Property
Public Property Get Parent() As Person
Dim tmpObj As Person
CopyMemory tmpObj, m_ParentPtr, 4
Set Parent = tmpObj
CopyMemory tmpObj, 0&, 4
End Property
Public Property Set Parent(ByVal value As Person)
m_ParentPtr = ObjPtr(value)
End Property
The good news is that circular references aren’t a problem any longer under .NET, therefore you can safely use standard (strong) object references in VB.NET. The easiest way to convert this piece of code to VB.NET is to re-establish a strong reference before the migration. You can do this by means of pragmas, so that the original VB6 code isn’t touched:
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(lpDest As Any, lpSource As Any, ByVal nCount As Long)
Private m_Child As Person
Private m_ParentPtr As Long ' notice this
Public Property Get Child() As Person
Set Child = m_Child
End Property
Public Property Set Child(ByVal value As Person)
Set m_Child = value
End Property
Public Property Get Parent() As Person
Dim tmpObj As Person
CopyMemory tmpObj, m_ParentPtr, 4
Set Parent = tmpObj
CopyMemory tmpObj, 0&, 4
End Property
Public Property Set Parent(ByVal value As Person)
m_ParentPtr = ObjPtr(value)
End Property
The above code is correctly converted to VB.NET as follows:
Private m_Child As Person
Private m_Parent As Person
Public Property Child() As Person
Get
Return m_Child
End Get
Set(ByVal value As Person)
m_Child = value
End Set
End Property
Public Property Parent() As Person
Get
Return m_Parent
End Get
Set(ByVal value As Person)
m_Parent = value
End Set
End Property
Weak object references – 2nd case
In some advanced scenarios, a weak object reference should be preserved when converting to VB.NET. This is often the case when you keep a global collection of all the objects you have created but you don’t want this collection to prevent the individual instances to be cleared from memory when they aren’t used any longer. Here’s a VB6 example that uses weak references in this second scenario:
Private colObjects As New Collection
Sub AddObject(ByVal obj As Person)
colObjects.Add ObjPtr(obj),ObjPtr(obj)
End Sub
Sub RemoveObject(ByVal obj As Person)
colObjects.Remove ObjPtr(obj)
End Sub
Function GetObject(ByVal index As Long) As Person
Dim tmpObj As Person
CopyMemory tmpObj, CLng(colObjects(index)), 4
Set GetObject = tmpObj
CopyMemory tmpObj, 0&, 4
End Function
In this specific case, it is preferable that you convert to VB.NET using the WeakReference type, as follows:
Private colObjects As New List(Of WeakReference)
Sub AddObject(ByVal obj As Person)
Dim wr As New WeakReference(obj)
colObjects.Add(wr)
End Sub
Sub RemoveObject(ByVal obj As Person)
For index As Integer = 1 To colObjects.Count
If colObjects(index).Target is obj Then
colObjects.RemoveAt(index)
Exit For
End If
Next
End Sub
Function GetObject(ByVal index As Long) As Person
Return TryCast(colObjects(index).Target, Person)
End Function
While the above VB.NET code is the best translation of the original code, you may object that the structure of the code greatly differs from the original VB6 code. In practice the VB.NET code has been rewritten by hand rather than converted.
Starting with version 1.32, VB Migration Partner offers an alternative way to implement weak references, a way that preserves the structure of the original code, as explained in next section.
Object pointers
In many circumstances, VB6 developers need to store a 32-bit object pointer rather than an object reference because they have no choice. This is the case, for example, when they want to associate an object to a window by means of the GetProp and SetProp Windows API methods.
Private Declare Function GetProp Lib "user32" Alias "GetPropA" _
(ByVal hwnd As Long, ByVal lpString As String) As Long
Private Declare Function SetProp Lib "user32" Alias "SetPropA" _
(ByVal hwnd As Long, ByVal lpString As String, ByVal hData As Long) As Long
Const PROPNAME As String = "WindowHandler"
Sub SetWindowObject(ByVal hWnd As Long, ByVal obj As Object)
SetProp(hWnd, PROPNAME, ObjPtr(obj)
End Sub
Function GetWindowObject(ByVal hWnd As Long) As Object
Dim propValue As Long = GetProp(hWnd, PROPNAME)
Dim tmpObj As Object
CopyMemory tmpObj, propValue, 4
Set GetWindowObject = tmpObj
CopyMemory tmpObj, 0&, 4
End Function
In this case you can use neither .NET standard object references nor the WeakReference class, because you only have a 32-bit memory location to store a value that must be turned back into an object reference.
The support library that comes with VB Migration Partner 1.32 includes to new methods, ObjectPtr6 and ObjectFromPtr6, which permit to convert this sort of VB6 code while preserving its overall structure and with very few pragmas.
The ObjectPtr6 helper method works similarly to the original VB6 ObjPtr method, in that it returns a 32-bit integer that is unique for each different object. However, while ObjPtr returns the address of the memory block that contains the object’s data, the new ObjectPtr6 method returns a value that has no direct relationship with the object. (For example, you can’t use this value to inspect the object’s properties.)
The ObjectFromPtr6 helper method performs the transformation in the opposite direction: given a 32-bit value – which must be a value previously returned by a call to ObjectPtr6 – it returns the corresponding object.
Thanks to these two helper methods, you can convert the original VB6 code to VB.NET as follows:
Sub SetWindowObject(ByVal hWnd As Long, ByVal obj As Object)
SetProp(hWnd, PROPNAME, ObjectPtr6(obj)
End Sub
Function GetWindowObject(ByVal hWnd As Long) As Object
Return ObjectFromPtr6(GetProp(hWnd, PROPNAME))
End Function
Quite conveniently, ObjectPtr6 and ObjectFromPtr6 have been defined in the VBMigrationPartner_Support.bas module, therefore you can transform the original VB6 code before the migration into this code snippet and let VB Migration Partner migrate it to VB.NET without any further intervention:
Sub SetWindowObject(ByVal hWnd As Long, ByVal obj As Object)
SetProp(hWnd, PROPNAME, ObjectPtr6(obj)
End Sub
Function GetWindowObject(ByVal hWnd As Long) As Object
Dim propValue As Long = GetProp(hWnd, PROPNAME)
Set GetWindowObject = ObjectFromPtr6(propValue)
End Function
Access to an object’s internal data
Finally, in some very advanced scenarios, the ObjPtr method is used to retrieve the object’s address in memory, which is then used to read or even modify one or more object properties, for example the object’s reference counter.
This VB6 programming technique was very dangerous and absolutely not recommended. If you have adopted it in your VB6 applications, you have to redesign that portion of your code manually before or after the migration to VB.NET.
In fact, even if VB.NET had a method that returns the address of an object in memory, the layout of object data in memory differs from VB6. Many values that accompany a VB6 object – including the reference counter – are missing in VB.NET.