.NET Programming Hints - Data Types and Marshalling

<< Click to Display Table of Contents >>

Navigation:  Programming with Common Vision Blox > Compiler specific hints > .NET languages and CVB Programming Hints > Further .NET Programming Hints >

.NET Programming Hints - Data Types and Marshalling

 

Data Types and Marshalling

In most cases the simple scheme of importing functions as described can be used.

But there are also cases where the default marshalling doesn't succeed and the compiler has to be informed how to transform managed .NET code to unmanaged and vice versa.

The way to do is the same as before: Using an attribute:

 [MarshalAs(<Unmanaged Type>)]

for parameters or

 [return:MarshalAs(<Unmanaged Type>)]

for return values. The <Unmanaged Type> is an enumeration (UnmanagedType) which defines how to handle the data type following the attribute.

Now a few points are introduced where problems result.

The correct marshalling of structs which contain nonprimitive data types goes beyond the scope of this introduction.

Please refer to the .Net Framework help.

 

Arrays

C-Style arrays are defined as pointers of a certain type.

Thus the compiler has to be told that this pointer should be handled as an array.

This is done by marshalling an array as UnmanagedType.LPArray.

Example:

As an example the ImageHistogram function is used. The function is defined as follows:

 

 IMPORT(bool) ImageHistogram (IMG I, long Index, long Density, TArea Area, THistogram& HGram);

 

We assume that the IMG definition was already made. The IMG type is a struct which contains the handle to an image.

On a 32 bit system long is 32 bit value and thus an System.Int32 (or int for short).

TArea also is assumed to be defined earlier and contains all coordinates of an area.

In .NET we don't need the THistogram type-definition; it is a simple int-array with 256 entries (8 bit images).

 

The simple conversion described above would be:

 [DllImport("cvcimg", CharSet=System.Runtime.InteropServices.CharSet.Ansi)]

 public static extern bool ImageHistogram(IMG img, int index, int density, TArea area, int[] histogram);

 

Two MarshalAs attributes are needed for this function: First we need the marshalling for the array and second the marshalling for the return type (see bool):

 [DllImport("cvcimg", CharSet=System.Runtime.InteropServices.CharSet.Ansi)]

 [return:MarshalAs(UnmanagedType.U1)]

 public static extern bool ImageHistogram(IMG img, int index, int density, TArea area, [MarshalAs(UnmanagedType.LPArray)] int[] histogram);

 

bool

If a bool, not a BOOL, data type is used (C/C++ data types), it can happen that the value is always true.

The reason is that the default marshalling expects a BOOL value which is stored in a 32 bit variable.

bool on the other hand is stored in a 8 bit variable.

Thus, especially when a bool is returned, random numbers can be found in the preceding 24 bits.

That very often leads to a number unequal zero and therefore results in a true-value even when the last 8 bits only contain zeros.

The solution of this problem is to tell the compiler that it should marshal this value not as a 32 bit variable but as an 8 bit variable: This is done by marshalling the value as UnmanagedType.U1.

 

Example:

As an example the IsImage function is used. The header file of the CVimg.dll contains this entry:

 IMPORT(bool) IsImage (OBJ P);

We assume that the OBJ definition was already made. The OBJ type is defined as a struct which manages the handle to the object to be checked. The simple conversion would lead to:

 [DllImport("cvcimg", CharSet=System.Runtime.InteropServices.CharSet.Ansi)]

 public static extern bool IsImage(OBJ obj);

 

But exactly this implementation would lead to the problem described above. Thus the MarshalAs-attribut for the return value is added:

[DllImport("cvcimg", CharSet=System.Runtime.InteropServices.CharSet.Ansi)]

[return:MarshalAs(UnmanagedType.U1)]

public static extern bool IsImage(OBJ hnd);

 

string as output parameter

In most cases the default marshalling for strings will work fine because of the CharSet definition in the DllImport attribute.

When a string is an expected output parameter the importing is more complicated: A string can not be marshalled as every unmanaged string type and no maximum size can be set.

To bypass this problem the StringBuilder is used instead and marshalled accordingly (e.g. as UnmanagedType.LPStr).

 

Example:

In this example the GetCVBDirectory function will be imported. The definition in the header file is:

 IMPORT(BOOL) GetCVBDirectory (LPSTR Directory, long lMaxLen);

 

An LPSTR is a char-array (thus a pointer to a char) and a long a 32 bit value (on a 32 bit system).

The char array can be marshalled to a managed string and the long can be interpreted as a System.Int32 (or int for short).

The simple conversion would lead to the following:

 [DllImport("cvcutilities", CharSet=System.Runtime.InteropServices.CharSet.Ansi)]

 public static extern bool GetCVBDirectory(out string directory, int maxLen);

 

But this won't wor even when the string is marshalled as UnmanagedType.LPStr, because the string can not be marshalled as this type. Instead we use the StringBuilder found in System.Text:

 [DllImport("cvcutilities", CharSet=System.Runtime.InteropServices.CharSet.Ansi)]

 public static extern bool GetCVBDirectory([MarshalAs(UnmanagedType.LPStr)] System.Text.StringBuilder directory, int maxLen);

 

The StringBuilder given as the parameter has to have a length of at least maxLen. Otherwise the .NET framework will throw an exception.

For easier handling and for ensuring the minimum length of the StringBuilder a wrapper method that returns strings can be used:

 public static bool GetCVBDirectory(out string directory, int maxLen)

 {

  System.Text.StringBuilder buffer = new System.Text.StringBuilder(maxLen);

  bool result = GetCVBDirectory(buffer, maxLen);

  directory = buffer.ToString();

  return result;

 }

This wrapper matches the method definition of the first try to import the dll function. When this wrapper is used the method to import the function should be private.