Converting C++ to VB2008: Using DLL and union structure

vbnoob2503

New member
Joined
Feb 10, 2009
Messages
3
Hi folks,

I try to use a DLL file (ReadIMX.DLL) generated with C++ in my VB application. I have the DLL sourcecode and I guess I know how to call the DLL from VB. Still I have some trouble with generating structures like in the C++ code I need calling the DLL.
The DLL basically has the decryption routine to read special image files (*.IMG, *.IMX, ...) associated with a laboratory camera. Looking at the C++ code the DLL is called as follows:
Code:
extern "C" int EXPORT ReadIMX ( const char* theFileName, BufferType* myBuffer, AttributeList** myList )
Giving the filename to the DLL it decrypts the image file and stores data into myBuffer. The myList list includes image attributes. Setting myList = NULL, the DLL does not read those attributes. As I am not interested in the attributes I will use the NULL argument here. The DLL returns an integer representing an error code. The BufferType structure is defined like this:
Code:
typedef struct
{
	float	factor;
	float offset;
	char	description[16];
	char	unit[16];
} BufferScaleType;

typedef struct
{
   int         isFloat;
   int         nx,ny,nz,nf;
   int         totalLines;
	int			vectorGrid;			// 0 for images
	int			image_sub_type;	// BufferFormat_t
   union 
	{
      float*   floatArray;
      Word*    wordArray;
   };
	BufferScaleType	scaleX;		// x-scale
	BufferScaleType	scaleY;		// y-scale
	BufferScaleType	scaleI;		// intensity scale
} BufferType;
I tried to define the structures in my VB script as follows:
Code:
Imports System.Runtime.InteropServices

<StructLayout(LayoutKind.Sequential)> _
    Public Structure BufferScaleType
        Public factor As Single
        Public offset As Single
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=16)> _
        Public description As String
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=16)> _
        Public unit As String
    End Structure

    <StructLayout(LayoutKind.Explicit)> _
    Public Structure Union
        <FieldOffset(0)> _
        Public float As Single()
        <FieldOffset(0)> _
        Public Word As String()
    End Structure

    <StructLayout(LayoutKind.Sequential)> _
    Public Structure BufferType
        Public isFloat As Integer
        Public nx, ny, nz, nf As Integer
        Public totalLines As Integer
        Public vectorGrid As Integer
        Public image_sub_type As Integer
        Public Unionx As Union
        Public scalex As BufferScaleType
        Public scaley As BufferScaleType
        Public scalei As BufferScaleType
    End Structure
Calling the DLL as follows I get an error message pasted below:
Code:
Public Declare Function ReadIMX Lib "ReadIMX.dll" (ByVal filename As String, ByRef btype As BufferType, ByRef attributelist As Object) As Integer

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim filename As String = "D:\Daten\Messungen\Test\429mbarAr_7_5kHz_740Vpp_696_64nm\B0001.IMG"
        Dim buftype As New BufferType
        Dim A As Integer = ReadIMX(filename, buftype, System.DBNull.Value)
    End Sub
The error message says (in my VB version the message is German, so please excuse if my translation is not conform with VB error expressions):
InvalidVariant was detected.
Converting a none managed VARIANT to a managed object an invalid INVARIANT was found. If an invalid INVARIANT is passed to the CLR, unexpected exceptions, data harm or loss can occur.
Do you have an idea where the problem is hiding? I attached the DLL and a sample IMG-file to this post.
 

Attachments

Is the error due to converting a managed object to a COM variant, or vice-versa?

DBNull.Value is not a null value, it is an object that represents a null database value. Visual Basic represents null with the keyword "Nothing", so try this:
Code:
Public Declare Function ReadIMX Lib "ReadIMX.dll" (ByVal filename As String, ByRef btype As BufferType, ByRef attributelist As Object) As Integer

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim filename As String = "D:\Daten\Messungen\Test\429mbarAr_7_5kHz_740Vpp_696_64nm\B0001.IMG"
        Dim buftype As New BufferType
        Dim A As Integer = ReadIMX(filename, buftype, [COLOR="Blue"]Nothing[/COLOR])
    End Sub

That might help. Its hard to say. Things get tricky with P/Invoke when you throw in pointers and pointers to pointers and unions.
 
Hi marble_eater,

thank you for your help. Using the "Nothing" argument did not solve the problem. The error occurs converting an unmanaged variant to a managed object. I guess you are right and the conversion of the C++ structures to VB structures is the main error source.
I will try to find out more on basis of the DLL source code. Changing the arguments in the ReadIMX routine I could check if the communication between the DLL and my VB script works without errors. Then the structure definitions will most probably be the error source. Please excuse if I use unusual terms to describe routines etc. as I am not too experienced with VB and unfamiliar with C++ and C#.
 
Looking over your structure definition again, I see two problems. First of all, you are replacing a pointer to a Word (Word is a numeric type) with a string, and you are replacing a pointer to a float with a float (Single). Secondly, seeing as the C++ union actually contains two pointers of different types, I dont think you actually need a union. VB only supports one kind of pointer, the IntPtr. I dont know whether you need to access the data that this pointer points to.

Again, I dont know if it will help, but you can try modifying your code as follows:
Code:
Imports System.Runtime.InteropServices

<StructLayout(LayoutKind.Sequential)> _
    Public Structure BufferScaleType
        Public factor As Single
        Public offset As Single
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=16)> _
        Public description As String
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=16)> _
        Public unit As String
    End Structure

    <StructLayout(LayoutKind.Sequential)> _
    Public Structure BufferType
        Public isFloat As Integer
        Public nx, ny, nz, nf As Integer
        Public totalLines As Integer
        Public vectorGrid As Integer
        Public image_sub_type As Integer
        [B][I][COLOR="Blue"]Public floatOrShortPointer As IntPtr[/COLOR][/I][/B] [COLOR="SeaGreen"]// This might point to an array[/COLOR]
        Public scalex As BufferScaleType
        Public scaley As BufferScaleType
        Public scalei As BufferScaleType
    End Structure
 
if the above dont work you might want to check out
http://www.vbthunder.com/articles/readcpp.php

also, the error is an mda error. You can turn off the error by Debug->Exceptions->Managed Debugging Assistants->Invalid Variant

Last I read something about structures being something totally different in c++ and vb.net, although Im not that good with vb.net and hardly use structures in VB.NET I cant provide you with more information about that. Also I cant find that article right now :(

Hope this points you in the right direction

Good luck
~ DP
 
Ok, marble_eaters last suggestion solved the problem. I can now access the DLL without error messages and get return values that make sense. Replacing the "union" with a single pointer is a brilliant idea. Thank you both for your help. Now I have to dig myself through the DLL to read the images, which should be ok, now as I can access the DLL properly. Thank you once more.
 
Back
Top