Previous | Index | Next 

[PRB] The VB.NET application can’t compile because of an ”'AddressOf' expression cannot be converted to 'Integer' because 'Integer' is not a delegate type” error

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   ' <<< this item must be assigned the callback method’s address
    lParam As Long
    iImage As Long
End Type

' this variable will be set in callback method
Private m_sDefaultFolder As String

' Displays the BrowseForFolder dialog box
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
        ' Free memory
        CoTaskMemFree nResult
    End If
End Function

' the callback 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
        ' Set default folder when dialog has initialized
        SendMessage hwnd, BFFM_SETSELECTIONA, True, ByVal m_sDefaultFolder
    End If
End Function

' helper method that returns the address passed as an argument
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:

' UPGRADE_ISSUE (#0168): AddressOf keyword can't be used in calls to standard 
(non-Declare) methods. (Notice that can subclass a window by overriding its
WndProc protected method.)
bi.lpfn = GetAddress(AddressOf BrowseCallbackProc)

You need to take the following steps to work around this issue.

  1. use an InsertStatement pragma to declare a delegate with the same signature as the callback method:
    '## InsertStatement Delegate Function BrowseCallback(ByVal hwnd As Integer,
    ByVal uMsg As Integer, ByVal lParam As Integer, ByVal lpData As Integer) As Integer
  2. use a ReplaceStatement pragma to so that the lpfn member is typed after the new delegate in the migrated VB.NET code:
    '## ReplaceStatement Public lpfn As BrowseCallback
    lpfn As Long
  3. use a ReplaceStatement pragma to drop the call to GetAddress method:
    '## ReplaceStatement bi.lpfn = AddressOf BrowseCallbackProc
        bi.lpfn = GetAddress(AddressOf BrowseCallbackProc)
  4. 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):
    ##OutputMode Off, 4 
    ' helper method that returns the address passed as an argument
    Private Function GetAddress(ByVal value As Long) As Long 
       GetAddress = value 
    End Function

The resulting VB6 code therefore becomes:

'## REM next pragma goes on a single line
'## InsertStatement Delegate Function BrowseCallback(ByVal hwnd As Integer,
      ByVal uMsg As Integer, ByVal lParam As Integer, ByVal lpData As Integer) As Integer
  
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
    '## ReplaceStatement Public lpfn As BrowseCallback
    lpfn As Long
    lParam As Long
    iImage As Long7
End Type
  
' this variable will be set in callback method
Private m_sDefaultFolder As String
  
' Displays the BrowseForFolder dialog box
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   
    '## ReplaceStatement bi.lpfn = AddressOf BrowseCallbackProc
    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
        ' Free memory
        CoTaskMemFree nResult
    End If
End Function
  
' the callback 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
        ' Set default folder when dialog has initialized
        SendMessage hwnd, BFFM_SETSELECTIONA, True, ByVal m_sDefaultFolder
    End If
End Function
  
'##OutputMode Off, 4
' helper method that returns the address passed as an argument
Private Function GetAddress(ByVal value As Long) As Long
    GetAddress = value
End Function

 

Previous | Index | Next