VB Migration Partner correctly migrates AddressOf operators that appear inside calls to external methods (i.e. methods defined by means of Declare statements). When this operators appears in calls to standard methods, however, no automatic translation is possible and the resulting VB.NET application has the following compilation errors:
'AddressOf' expression cannot be converted to 'Integer' because _
'Integer' is not a delegate type.
VB6 developers typically use the AddressOf operator in calls to standard methods in two cases. First, when the VB6 code is implementing window subclassing; second, when the VB6 code is invoking a Windows API method that takes a user-defined type (i.e. a Type…End Type structure) as an argument, and that user-defined type has one or more members that are pointers to callback methods. This section deals with the latter case. Consider the following VB6 code, which invokes the SHBrowseForFolder API function:
Private Declare Function SHBrowseForFolder Lib "shell32.dll" Alias "SHBrowseForFolderA" _
(lpbi As BrowseInfo) As Long
Private Declare Function SHGetPathFromIDList Lib "shell32.dll" Alias _
"SHGetPathFromIDListA" (ByVal pidl As Long, ByVal pszPath As String) As Long
Private Declare Function SendMessage Lib "USER32" Alias "SendMessageA" (ByVal hwnd As _
Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Declare Sub CoTaskMemFree Lib "ole32.dll" (ByVal hMem As Long)
Private Const MAX_PATH = 260
Private Const BIF_RETURNONLYFSDIRS = 1
Private Const BIF_STATUSTEXT = 4
Private Const BFFM_INITIALIZED = 1
Private Const WM_USER = &H400
Private Const BFFM_SETSELECTIONA = (WM_USER + 102)
Private Type BrowseInfo
hwndOwner As Long
pIDLRoot As Long
pszDisplayName As String
lpszTitle As String
ulFlags As Long
lpfn As Long
lParam As Long
iImage As Long
End Type
Private m_sDefaultFolder As String
Public Function BrowseForFolder(DefaultFolder As String, Optional Parent As Long = 0, _
Optional Caption As String = "") As String
Dim bi As BrowseInfo
Dim sResult As String, nResult As Long
bi.hwndOwner = Parent
bi.pIDLRoot = 0
bi.pszDisplayName = String$(MAX_PATH, Chr$(0))
If Len(Caption) > 0 Then bi.lpszTitle = Caption
bi.ulFlags = BIF_RETURNONLYFSDIRS
bi.lpfn = GetAddress(AddressOf BrowseCallbackProc)
bi.lParam = 0
bi.iImage = 0
m_sDefaultFolder = DefaultFolder
nResult = SHBrowseForFolder(bi)
If nResult <> 0 Then
sResult = String(MAX_PATH, 0)
If SHGetPathFromIDList(nResult, sResult) Then
BrowseForFolder = Left$(sResult, InStr(sResult, Chr$(0)) - 1)
End If
CoTaskMemFree nResult
End If
End Function
Private Function BrowseCallbackProc(ByVal hwnd As Long, ByVal uMsg As Long, _
ByVal lParam As Long, ByVal lpData As Long) As Long
If uMsg = BFFM_INITIALIZED And Len(m_sDefaultFolder) > 0 Then
SendMessage hwnd, BFFM_SETSELECTIONA, True, ByVal m_sDefaultFolder
End If
End Function
Private Function GetAddress(ByVal value As Long) As Long
GetAddress = value
End Function
As you see, the GetAddress helper method is used only to assign the address of the callback method – the BrowseCallbackProc function – to the lpfn member of the BrowseInfo user-defined type. The statement that invokes the GetAddress method is flagged as a compilation error after the migration and is also preceded by an upgrade issue generated by VB Migration Partner:
bi.lpfn = GetAddress(AddressOf BrowseCallbackProc)
You need to take the following steps to work around this issue.
- use an InsertStatement pragma to declare a delegate with the same signature as the callback method:
- use a ReplaceStatement pragma to so that the lpfn member is typed after the new delegate in the migrated VB.NET code:
- use a ReplaceStatement pragma to drop the call to GetAddress method:
- use an OutputMode pragma to remove the GetAddress method from the converted VB.NET application, because this method is now useless (this step is optional):
Private Function GetAddress(ByVal value As Long) As Long
GetAddress = value
End Function
The resulting VB6 code therefore becomes:
Private Declare Function SHBrowseForFolder Lib "shell32.dll" Alias "SHBrowseForFolderA" _
(lpbi As BrowseInfo) As Long
Private Declare Function SHGetPathFromIDList Lib "shell32.dll" Alias _
"SHGetPathFromIDListA" (ByVal pidl As Long, ByVal pszPath As String) As Long
Private Declare Function SendMessage Lib "USER32" Alias "SendMessageA" (ByVal hwnd As _
Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Declare Sub CoTaskMemFree Lib "ole32.dll" (ByVal hMem As Long)
Private Const MAX_PATH = 260
Private Const BIF_RETURNONLYFSDIRS = 1
Private Const BIF_STATUSTEXT = 4
Private Const BFFM_INITIALIZED = 1
Private Const WM_USER = &H400
Private Const BFFM_SETSELECTIONA = (WM_USER + 102)
Private Type BrowseInfo
hwndOwner As Long
pIDLRoot As Long
pszDisplayName As String
lpszTitle As String
ulFlags As Long
lpfn As Long
lParam As Long
iImage As Long7
End Type
Private m_sDefaultFolder As String
Public Function BrowseForFolder(DefaultFolder As String, Optional Parent As Long = 0, _
Optional Caption As String = "") As String
Dim bi As BrowseInfo
Dim sResult As String, nResult As Long
bi.hwndOwner = Parent
bi.pIDLRoot = 0
bi.pszDisplayName = String$(MAX_PATH, Chr$(0))
If Len(Caption) > 0 Then bi.lpszTitle = Caption
bi.ulFlags = BIF_RETURNONLYFSDIRS
bi.lpfn = GetAddress(AddressOf BrowseCallbackProc)
bi.lParam = 0
bi.iImage = 0
m_sDefaultFolder = DefaultFolder
nResult = SHBrowseForFolder(bi)
If nResult <> 0 Then
sResult = String(MAX_PATH, 0)
If SHGetPathFromIDList(nResult, sResult) Then
BrowseForFolder = Left$(sResult, InStr(sResult, Chr$(0)) - 1)
End If
CoTaskMemFree nResult
End If
End Function
Private Function BrowseCallbackProc(ByVal hwnd As Long, ByVal uMsg As Long, _
ByVal lParam As Long, ByVal lpData As Long) As Long
If uMsg = BFFM_INITIALIZED And Len(m_sDefaultFolder) > 0 Then
SendMessage hwnd, BFFM_SETSELECTIONA, True, ByVal m_sDefaultFolder
End If
End Function
Private Function GetAddress(ByVal value As Long) As Long
GetAddress = value
End Function