Sid Price
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
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
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:
The functions are imported using declarations similar to this (the init function):
Sid.
<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
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
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.
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.
A tad out of my league as well, but you may need to "pin" the memory so that it does not move.
ASKER
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
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
ASKER
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:
This is the exception:
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.
<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
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'.
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.
ASKER
I have no idea how to use a class withing the unmanaged 'C' DLL.
Sid.
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.
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
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.
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Sara,
I have read a little about this, would this be done by allocating a byte array and then using StructureToPtr?
Sid.
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.
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
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
....
}
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
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