I've converted a project from VB6 to vb.Net, with visual studio 2008 and I'm experiencing difficulties with the addressOf operator.

I have an application which uses an external dll to communicate with a payment terminal, through callback functions. I understand that in vb.Net, you have to use delegates. Before I continue, here's a part of the VB6 code.

Type PRN_FUNC_TYPE
    Status As Long
    init As Long
    Run As Long
    Exit As Long
End Type

Public Function GetProcAddress(ByVal lngAddressOf As Long) As Long
  GetProcAddress = lngAddressOf
End Function

Sub signOffPrinter(ByVal hwnd As Long)
    Dim PrnFunc As PRN_FUNC_TYPE

    PrnFunc.init = GetProcAddress(AddressOf printSignOffInit)
    PrnFunc.Run = GetProcAddress(AddressOf printSignOffRun)
    PrnFunc.Exit = GetProcAddress(AddressOf printSignOffExit)
    PrnFunc.Status = GetProcAddress(AddressOf printerStatus)

    changePrinter hwnd, PrnFunc
End Sub

Function printerStatus(ByVal lpVoidPrn As Long) As Long
    printerStatus = 1
End Function

Here's the vb.net code (created by converting the vb6 code):

Structure PRN_FUNC_TYPE
    Dim Status As Integer
    Dim init As Integer
    Dim Run As Integer
    'UPGRADE_NOTE: Exit was upgraded to Exit_Renamed. Click for more: 'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="A9E4979A-37FA-4718-9994-97DD76ED70A7"'
    Dim Exit_Renamed As Integer
End Structure

Public Function GetProcAddress(ByVal lngAddressOf As Integer) As Integer
    GetProcAddress = lngAddressOf
End Function

Sub signOffPrinter(ByVal hwnd As Integer)
    Dim PrnFunc As PRN_FUNC_TYPE
    Dim dlgPrinterStatus = New DelegatePrinterStatus(AddressOf printerStatus)

    'UPGRADE_WARNING: Add a delegate for AddressOf printSignOffInit Click for more: 'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="E9E157F7-EF0C-4016-87B7-7D7FBBC6EE08"'
    PrnFunc.init = GetProcAddress(AddressOf printSignOffInit)
    'UPGRADE_WARNING: Add a delegate for AddressOf printSignOffRun Click for more: 'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="E9E157F7-EF0C-4016-87B7-7D7FBBC6EE08"'
    PrnFunc.Run = GetProcAddress(AddressOf printSignOffRun)
    'UPGRADE_WARNING: Add a delegate for AddressOf printSignOffExit Click for more: 'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="E9E157F7-EF0C-4016-87B7-7D7FBBC6EE08"'
    PrnFunc.Exit_Renamed = GetProcAddress(AddressOf printSignOffExit)
    'UPGRADE_WARNING: Add a delegate for AddressOf printerStatus Click for more: 'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="E9E157F7-EF0C-4016-87B7-7D7FBBC6EE08"'
    PrnFunc.Status = GetProcAddress(AddressOf printerStatus)

    changePrinter(hwnd, PrnFunc)
End Sub

Delegate Function DelegatePrinterStatus(ByVal lpVoidPrn As Integer) As Integer

Function printerStatus(ByVal lpVoidPrn As Integer) As Integer
    printerStatus = 1
End Function

The red text is added by me. What I want to do is change PrnFunc.Status = GetProcAddress(AddressOf printerStatus) so that it uses the delegate. I've searched the net and all the info I found said that I have to use the Invoke method. The problem is that by calling the Invoke method on the delegate, I have to pass an integer value lpVoidPrn to the Invoke method, which I don't have.

I don't know much about delegates, and the code is from an external supplier, so I'm not sure if adjusting the function signature printerStatus(ByVal lpVoidPrn As Integer) to printerStatus() would solve my problem. My guess is that the code will not work, because the matching method (plus signature) does not exist in the external dll.

I hope someone can shine a light on this issue, because I'm out of ideas. Thanks in advance.

Jochem

I haven't worked much with old COM components with VB.NET.

Have you tried to add a references from the VB.NET project directly to the COM component? VB.NET creates a "COM wrapper" for the component and you should see it in the Object Browser similar to:

add reference to Windows Media Player COM -component, and you'll get

Assembly AxInterop.WMPLib
D:\VBNet\BlogTest\BlogTest\BlogTest\obj\Debug\AxInterop.WMPLib.dll
Namespace AxWMPLib
Member of: AxInterop.WMPLib

This uses the name space System.Runtime.InteropServices. I'm not sure if interop is applicable in your case, but it's worth to have a look at it.

It's not possible to add a reference. The dll is written by a 3rd party in C++ and it does not allow to be referenced.

After some research I've figured out how to call the function without getting compiler errors, but when I run the project, it throws an error when executing

PP.PrinterFunctions.Status = AddressOf Printer.printerStatus

stating that "{"Method may only be called on a Type for which Type.IsGenericParameter is true."}"
I've got the following code (including the C++ defenition).

'    typedef struct PRN_FUNC_TYPE {
    '        int   (__stdcall *Status)  (void);
    '        void* (__stdcall *Init  )  (void);
    '        int   (__stdcall *Run   )  (void* lpV,int TicketType , int NrOfTickets, unsigned char *szTicket);
    '        int   (__stdcall *Exit  )  (void* lpV);
    '    }PRN_FUNC;

    <StructLayout(LayoutKind.Sequential)> _
    Structure PRN_FUNC_TYPE
        Dim Status As Printer.dlgPrinterStatus
        Dim Init As Printer.dlgPrinterStatus
        Dim Run As Printer.dlgPrintGeneralRun
        'UPGRADE_NOTE: Exit was upgraded to Exit_Renamed. Click for more: 'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="A9E4979A-37FA-4718-9994-97DD76ED70A7"'
        Dim Exit_Renamed As Printer.dlgPrintGeneralExit
    End Structure

'   typedef struct POS_PARAM_TYPE {
    '        HWND        hWndParent;
    '        char        szCommPort[8];
    '        int         Language;

    '        int         x;
    '        int         y;
    '        int         Width;
    '        PRN_FUNC    PrinterFunctions;
    '        void (__stdcall *fCmdsAllowed) (int Cmds);
    '    } POS_PARAM;

    <StructLayout(LayoutKind.Sequential)> _
    Structure POS_PARAM_TYPE
        Dim hWndParent As IntPtr
        '<VBFixedArray(8)> Dim szCommPort() As Byte
        <MarshalAs(UnmanagedType.ByValArray, SizeConst:=8)> Dim szCommPort() As Byte
        Dim Language As Int32
        Dim x As Int32
        Dim y As Int32
        Dim Width As Int32
        <MarshalAs(UnmanagedType.Struct)> Dim PrinterFunctions As PRN_FUNC_TYPE
        Dim fCmdsAllowed As IntPtr '        void (__stdcall *fCmdsAllowed) (int Cmds);
        'UPGRADE_TODO: "Initialize" must be called to initialize instances of this structure. Click for more: 'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="B4BFF9E0-8631-45CF-910E-62AB3970F27B"'
        Public Sub Initialize()
            ReDim szCommPort(8)
        End Sub
    End Structure
...
PP.PrinterFunctions.Status = AddressOf Printer.printerStatus
PP.PrinterFunctions.Init = AddressOf Printer.printGeneralInit
...

In Printer module:

Delegate Function dlgPrinterStatus() As Integer

Delegate Function dlgPrintGeneralRun(ByVal lpVoidPrn As Integer, ByVal TicketType As Short, ByVal NrOfTickets As Short, ByVal Ticket As Integer) As Short

Function printerStatus() As Integer
        printerStatus = 1
    End Function
	'///////////////////////////////////////
	'// General Print funkties
	Function printGeneralInit() As Integer
		printGeneralInit = 1 
	End Function
	
    Function printGeneralRun(ByVal lpVoidPrn As Integer, ByVal TicketType As Short, ByVal NrOfTickets As Short, ByVal Ticket As Integer) As Short

        'TODO: change copymemory to marshal alternative
        'CopyMemory(szTicket.ToString(), Ticket, 4)
        ECRWin.Ticket.Text = convert_msg(Len(szTicket.ToString()), szTicket.ToString())

        printGeneralRun = 1 
    End Function
	
	
	Function printGeneralExit(ByVal lpVoidPrn As Integer) As Integer
		printGeneralExit = 1 
	End Function

...

I'm struggeling with this issue for allmost a week now, and I'm getting pretty desparate. So if someone can help in any way, it's much appreciated

I have imported dll functions by means of marshal:

<DllImport("user32", SetLastError:=True)> _
    Public Function GetWindowLong(ByVal hWnd As Integer, ByVal nIndex As Integer) As Integer
    End Function

    <DllImport("user32", SetLastError:=True)> _
    Public Function DestroyWindow(ByVal hWnd As Integer) As Integer
    End Function

    <DllImport("user32", SetLastError:=True)> _
    Public Function UnregisterClass(ByVal lpClassName As String, ByVal hInstance As Integer) As Integer
    End Function

'ATOM TCALL VICRegisterClass(HINSTANCE hInstance);
    <DllImport("Czam.dll", EntryPoint:="VICRegisterClass", SetLastError:=True)> _
    Public Function VICRegisterClass(ByVal hInstance As Integer) As Short
    End Function

    'HWND TCALL CreateVICWindow(POS_PARAM *lpPosParam);
    'UPGRADE_WARNING: Structure POS_PARAM_TYPE may require marshalling attributes to be passed as an argument in this Declare statement. Click for more: 'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="C429C3A5-5D47-4CD9-8F51-74A1616405DC"'
    <DllImport("Czam.dll", EntryPoint:="CreateVICWindow", SetLastError:=True)> _
    Public Function CreateVICWindow(ByRef lpPosParam As POS_PARAM_TYPE) As Short
    End Function

When I call the method, as mentioned below, everything goes well and I get a valid handle/pointer to the method.

Dim i As Integer
Dim b() As Byte
	
i = GetWindowLong(Me.Handle.ToInt32, GWL_HINSTANCE)
i = VICRegisterClass(i)
If (i = 0) Then
      DestroyWindow(Me.Handle.ToInt32)
End If

i has a valid value. So far nothing wrong with calling a function from an external dll. The problem lies in the calling of the AddressOf Printer.printerStatus function.

As you can see in the code previously posted, this function is defined in the Printer module (which is included in my project. But something goes wrong when I try to obtain the address of this function.

What I don't understand is why VS 2008 throws an error when executing the following code

PP.PrinterFunctions.Status = AddressOf Printer.printerStatus

stating that {"Method may only be called on a Type for which Type.IsGenericParameter is true."}
What does this mean, and how can I solve this.

Actually that I was thinking. I mean using DLLImport with PP.dll.

The definition
int (__stdcall *Status) (void);
states that printerStatus function has no arguments and returns (C++) integer. Since Init, Run and Exit methods are (quite) similar, do they work i.e. AddressOf does work with them? If they work, I would recheck the definition for the Status method. Anyway, I don't see any point of getting that "Type.IsGenericParameter" error. Really strange :-O

This article has been dead for over six months. Start a new discussion instead.