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
Sid PriceSoftware Systems Architect/DesignerAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Jacques Bourgeois (James Burger)PresidentCommented:
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
0
Sid PriceSoftware Systems Architect/DesignerAuthor Commented:
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.
0
Jacques Bourgeois (James Burger)PresidentCommented:
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.
0
Cloud Class® Course: C++ 11 Fundamentals

This course will introduce you to C++ 11 and teach you about syntax fundamentals.

käµfm³d 👽Commented:
A tad out of my league as well, but you may need to "pin" the memory so that it does not move.
0
Sid PriceSoftware Systems Architect/DesignerAuthor Commented:
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
0
Sid PriceSoftware Systems Architect/DesignerAuthor Commented:
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.
0
Jacques Bourgeois (James Burger)PresidentCommented:
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.
0
Sid PriceSoftware Systems Architect/DesignerAuthor Commented:
I have no idea how to use a class withing the unmanaged 'C' DLL.
Sid.
0
Jacques Bourgeois (James Burger)PresidentCommented:
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.
0
sarabandeCommented:
The pointer is a static global in the DLL

you can't save a pointer to a structure passed by managed code and expect that the pointer still is valid at next call.

you could make a copy of the data to static or global memory at first call and then use this for subsequent calls.

but this is neither good programming and makes assumptions on the "lifetime" of the dll which might be wrong. if a dll wasn't actively loaded by an executable, the OS may unload it and reload it again for next call. if that is the case (though not very likely) all static memory was lost and any code relying on it will fail. you can prevent from unloading by making an explicit call of LoadLibrary (what is not the default when using a dll from VB or C#).

the best way would be if the calls to the dll would provide all necessary data with each call. if that is not possible you should try to move the control structure with first call and subsequent calls to the dll such that the VB.NET only has one call to the dll where you provide input data and get your result data filled by the dll.

if the data to exchange is too much, you may consider to using named shared memory. this memory keeps alive as long as either dll or calling application holds a handle to it. you can map any fixed-sized structure to the memory, given that the structure only contains C type members like int, char, byte, double or fixed-sized arrays of these types. you definitively havfe to avoid pointers and class objects since those might be no longer valid for next call.

Sara
0
Sid PriceSoftware Systems Architect/DesignerAuthor Commented:
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.
0
sarabandeCommented:
The data cannot be supplied with each call because the DLL is running a number of threads
i didn't mean calls within the dll. of course each function in the dll can establish "shared" data  and start threads which can use the shared data in a thread-safe way. however, the main thread which contains the function called from managed code should not end before the threads were finished (it could wait on them by using a semaphore) and should return the results at end of function.

the pinning of the structure in the managed code is not possible.
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.

Sara
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Sid PriceSoftware Systems Architect/DesignerAuthor Commented:
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.
0
sarabandeCommented:
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
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Visual Basic.NET

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.