Link to home
Start Free TrialLog in
Avatar of Sid Price
Sid PriceFlag for United States of America

asked on

Using unmanaged DLL from managed application

I am calling an unmanaged ('C') DLL from my VB.NET application. The application makes an initial call that passes a reference to a data structure and the DLL gets the structure and is able to access the elements correctly. The DLL also saves a copy of the pointer at this time. When a subsequent call is made to the DLL the data elements in the structure are no longer correct.

The structure is instantiated in the calling class and from the perspective of that class the data remains the same. The pointer is a static global in the DLL so I don't understand why the data changes from the perspective of the DLL on subsequent calls.

Any pointers would be much appreciated,
Sid
Avatar of Jacques Bourgeois (James Burger)
Jacques Bourgeois (James Burger)
Flag of Canada image

This is a bit out of my league, because I have not worked in C for a good 20 years, but I suspect the following.

The "managed" in managed code means that .NET manage the pointer and you have not way to control what it does at that level. And .NET pointers are not a fixed value. The pointer to a specific object can change when the Garbage Collector needs to move because the heap has to be defragmented.

This is possibly what happens, so when you try to call the same memory address later, the data structure has moved and the pointer is no longer valid.

What you are doing could probably be done with an interop, which would act as a bridge between .NET and your C dll. I do not know if it is a good one and/or if it applies to your situation, but you might want to give a look at  C#/C interop with DllImport
Avatar of Sid Price

ASKER

Thank you for the comment, however I am using interop, plus the calling application does not update the data structure once it is passed as part of the initialization of the DLL. Here is how the structure is defined in VB.NET:

    <StructLayout(LayoutKind.Sequential)> _
    Public Structure podOptions
        <MarshalAs(UnmanagedType.U4)> Public iPodType As Integer
        <MarshalAs(UnmanagedType.U4)> Public iPodInterface As Integer
        <MarshalAs(UnmanagedType.Bool)> Public fIsISSpMode As Boolean
        <MarshalAs(UnmanagedType.U1)> Public ucClockDelay As Char
        <MarshalAs(UnmanagedType.U4)> Public iInterfaceFrequency As UInt32
        <MarshalAs(UnmanagedType.U4)> Public eEEPROMControl As eepromOptions
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=260)> Public eepromFilename As String
    End Structure

Open in new window


The functions are imported using declarations similar to this (the init function):

    <DllImport("avr_pods", SetLastError:=True, CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.Cdecl)> _
    Private Shared Function avrcmsis_init(ByRef podOptions As podOptions) As eErrors
    End Function

Open in new window


Sid.
Give a look at Walkthrough: Calling Windows APIs (Visual Basic), specifically at the section labelled API Calls Using DllImport : Unlike Declare statements, DllImport calls cannot use the MarshalAs attribute.

And although all of this is out of my actual experience, so I speak only through intuition and a look at the documentation here, I wonder why you marshal both Integer and UInt32 to U4. Integer is signed and my first reflex would be to marshal Integer values to I4, not U4. If you happen to have a negative value in iPodType or iPodInterface, you will probably end up with the wrong value in your dll.
Avatar of kaufmed
A tad out of my league as well, but you may need to "pin" the memory so that it does not move.
Jacques, thank you again for attempting to help however I do not have a problem passing the structure to the DLL, the problem is persistence of the structure across multiple calls to the DLL when only the first function passes it.

I believe the problem is related to the GC moving the managed structure and I will investigate that suggestion and technique in the link.
Sid
The next issue I have is that pinning structures appears to not work, I get an exception that the object is not blitable. This point is made in the linked tutorial. I tried converting my structure declaration to LayoutKind.Explicit but that causes an exception when the structure is instantiated. Ignoring the signed/unsigned issue for now this is what the explicit structure looks like:

    <StructLayout(LayoutKind.Explicit, Size:=281)> _
    Public Structure podOptions
        <FieldOffset(0)> Public iPodType As Integer
        <FieldOffset(4)> Public iPodInterface As Integer
        <FieldOffset(8)> Public fIsISSpMode As Boolean
        <FieldOffset(12)> Public ucClockDelay As Char
        <FieldOffset(13)> Public iInterfaceFrequency As UInt32
        <FieldOffset(17)> Public eEEPROMControl As eepromOptions
        <FieldOffset(21)> Public eepromFilename As String

Open in new window


This is the exception:

An unhandled exception of type 'System.Windows.Markup.XamlParseException' occurred in PresentationFramework.dll

Additional information: 'The invocation of the constructor on type 'iFlash.MainWindow' that matches the specified binding constraints threw an exception.' Line number '4' and line position '2'.

Open in new window


I updated my DLL to make its own copy of the passed structure and that works. However, in the future I would like to know how to make this work in case I don't have the DLL source available.
Sid.
Have you tried making your Structure a Class. I do not know how C would react to a class, but classes are not handled the same way as structures, so that might solve you problem.
I have no idea how to use a class withing the unmanaged 'C' DLL.
Sid.
Basically, for the user, the only difference between a class and a structure is that the class needs to be instantiated, while a structure does not. There are other differences, but many programmers live for years without even being aware of them.

Giving you that information however automatically invalidates my last post. C does not know how to instantiate an object, so the idea of using a class with C is a stupidity.

You might want to give a look at the Guide to using Pinning Pointers. It goes further than the previous reference about the subject and might help you. Other than that, on my side, no more idea.

Good luck.
SOLUTION
Avatar of sarabande
sarabande
Flag of Luxembourg image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Sara, thank you for your detailed reply.

The data cannot be supplied with each call because the DLL is running a number of threads that need the structure for their correct operation and they run without any calls from the managed code.

I find it hard to believe that the pinning of the structure in the managed code is not possible. I have read a couple of articles but the technique does not work, at least my implementation of it. See my earlier post on this subject.

For now the ONLY choice I appear to have is copying the structure on the first call to the DLL, which is what I have done. Not best practice but I don't see any other choice.
Sid.
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Sara,

it is possible but you have provide the memory for the structure as a byte array in managed code and can't use gc for to manage the data.

I have read a little about this, would this be done by allocating a byte array and then using StructureToPtr?
Sid.
yes, as far as i know (i am not a c# programmer). i think the pointer can be stored in a variable of type IntPtr.

at the c++ side you would declare the argument as 'BYTE*' or equivalent as 'unsigned char*'. this pointer can be "casted" to pointer of structure.

struct MyInputStruct
{ 
      int i1;
      char s1[256];
      ...
};

struct MyOutputStruct
{
     int n[100];
     char s[200];
     double d;
     ...
};

int FunctionCalledFromManagedCode(BYTE * input, int sizInput, BYTE * output, int sizOutput)
{
       MyInputStruct * pInput = NULL;
       MyOutputStruct * pInput = NULL;
       ....
       if (sizeof(MyInputStruct ) != sizInput || sizeof(MyOutputStruct ) != sizOutput)
       {
           // error: wrong sizes
           return false;
       }       
       pInput = (MyInputStruct *) input; // cast pointer to structure 
       pOutput = (MyOutputStruct *) output; // cast pointer to structure 
       // from here you have a valid pointer both to input and output
       ....
}

Open in new window


at the c# or vb.net side you would need to provide the input arguments respectively to evaluate the output structure by so-called marshaling functions. which allow to convert the c# types to c types and vice versa.

there are a lot of sample code available for all of these.

Sara