views:

2039

answers:

3

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);
+1  A: 

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?

JaredPar
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.
Michael
A: 

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.

casperOne
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.
JaredPar
@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.
casperOne
A: 

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.

leppie