Thursday, March 31, 2011

callback function from unmanaged dll in VB .NET

I'm trying to use an unmanaged dll in VB.NET. The example source code provided with the dll is in VB6 and below is my attempt to convert it to .NET. When the dll tries to do a callback I get a "Attempted to read or write protected memory" exception. I really don't care about the callback function getting actually called. My code:

<DllImport("AlertMan.dll")> _
Public Shared Function AlertManC( _
    ByVal CallbackAddr As AlertManCallbackDel) As Long
End Function

Public Delegate Sub AlertManCallbackDel(ByVal data As Long)

Public Sub AlertManCallback(ByVal data As Long)       
End Sub

Public mydel As New AlertManCallbackDel(AddressOf AlertManCallback)
'protected memeory exception here
Dim IStat as Long = AlertManC(mydel)

Original VB6 example code:

Declare Function AlertManC _
    Lib "AlertMan.dll" _
    Alias "AlertManC" (ByVal CallbackAddr As Long) As Long

Private Sub AlertManCallback(ByVal data As Long)
End Sub

' calling code
Dim IStat As Long
IStat = AlertManC(AddressOf AlertManCallBack)

Original dll header

typedef void TACBFUNC(char *);
int AlertManC(TACBFUNC *WriteCaller cHANDLEPARM);
From stackoverflow
  • Can you post the original native definiton for AlertManC?

    My guess though is that the data parameter of the callback function is actually an Integer vs. a Long. In VB6 I believe Long's were actually only 32 bits vs. VB.Net where they are 64 bit. Try this

       <DllImport("AlertMan.dll")> _
       Public Shared Function AlertManC(ByVal CallbackAddr As AlertManCallbackDel) As Long
       End Function
    
       Public Delegate Sub AlertManCallbackDel(ByVal data As IntPtr)
    
    
       Public Sub AlertManCallback(ByVal data As IntPtr)       
       End Sub
    

    Edit

    I updated the code based on the native signature you posted. Can you try this out?

    Michael : Thanks, you rock! Changing long to integer was the issue! Your orginal solution would have worked as well. there are actually 7 other parameters on the method that I removed for brevity (shame on me). Switching them to Integers was the trick.
  • Your callback should look like this:

    Public Delegate Sub AlertManCallbackDel(ByRef data As Byte)
    

    The reason for this being that you are passing a single-byte value by reference.

    As for the declaration of the unmanaged function, it should look like this:

    <DllImport("AlertMan.dll")> _
    Public Shared Function AlertManC( _
        ByVal CallbackAddr As AlertManCallbackDel) As Integer
    End Function
    

    Note that the return type is an Integer, which in VB.NET is a 32-bit value. In VB6, a Long was a 32-bit value, hence the need for a change in VB.NET.

    The callback definition is important to get right as well, btw.

    JaredPar : Adding a ByRef byte is potentially dangerous. Imagine what happens if the native code passes NULL. I think the CLR will generate an exception in this case because by default a ByRef is marshalled both ways.
    casperOne : @Jared: I agree, it all depends on the documentation and what the guarantee is of the code that is implemented. However, for most cases, this will suffice.
  • If the callback's calling convention is cdecl, you cant do that directly in C# or VB.NET.

    You will have to modify the IL of the delegate to behave correctly.

    You can search on CodeProject for an in-depth article.

    Update:

    I guess not the correct answer :) But will leave my response.

0 comments:

Post a Comment