Export (0) Print
Expand All
9 out of 26 rated this helpful - Rate this topic

Marshalling Types During Platform Invoke (P/Invoke) on the Microsoft .NET Compact Framework

.NET Compact Framework 1.0
 

Content Master Ltd

April 2003

Applies to:
    Microsoft® .NET Compact Framework 1.0
    Microsoft Visual Studio® .NET 2003

Summary: Learn how to marshal data between managed and unmanaged code using the .NET Compact Framework. (6 printed pages)

Contents

Introduction
Marshalling Value and Reference Types
Value Types
Reference Types
Marshalling Arrays
Marshalling Structures and Classes
Marshalling as Other Types
More Information

Introduction

Data type representations in the Microsoft® .NET Compact Framework differ from those in unmanaged code. Converting between the managed and unmanaged representations is called marshaling and is automatic for most simple data types.

This article will look at how value types and reference types are marshaled across the interoperability boundary, as well as how to pass more complex data types, such as structures, classes and arrays.

This article assumes some familiarity with C/C++ and C# to be able to understand to the code samples used to illustrate marshaling.

Marshalling Value and Reference Types

The .NET Compact Framework can handle value types with relative ease. This is because value types are simple types that can be held in just a few bytes of memory. Reference types, such as strings, arrays and structures, are more complex to marshal because they consist of a reference to a memory area on the .NET Compact Framework garbage-collected heap.

Value types are marshaled to unmanaged code on the stack. Reference types are passed by address. This means a pointer is passed on the stack, and the pointer contains the address of the marshaled data on the heap.

A type is considered blittable if it has a common representation in managed and unmanaged code memory. Non-blittable types require custom marshaling to convert between the unmanaged and managed representations.

Value Types

The data types outlined in the table below are automatically marshaled by value using P/Invoke. The table also shows the unmanaged equivalents in C/C++.

Table 1. Common Value Types that are Automatically Marshaled

C# Visual Basic .NET Native C/C++ Size (bits)
int Integer int 32
short Short short 16
bool Boolean BYTE 8
char Char WCHAR 16

Take the following C/C++ function as an example. The function accepts three integer parameters and calculates their arithmetic mean:

extern "C" _declspec(dllexport) int mean(int x, int y, int z)
{
    return (x + y + z) / 3;
}

This method can be declared and called in a Smart Device application. The following code calculates the mean of 1, 3 and 5 (that is, 3) and displays it in a Message Box:

using System.Runtime.InteropServices;
.
.
.
[DllImport("MarshalByValueDemo.dll")]
extern static int mean(int x, int y, int z);
.
.
.
int avg = mean(1, 3, 5);
MessageBox.Show(String.Format("The mean is {0}", avg));

It is worth noting; only value types of 32 bits or less can be marshaled automatically. Long types (64-bit integer) and floating-point types (float and double) cannot be marshaled by value into unmanaged code. You should pass these values by reference.

Reference Types

In the .NET Compact Framework, reference types are, by default, passed by reference. The two most important reference types are the String and StringBuilder. When parameters are passed by reference, a pointer to the parameters on the managed heap is passed to the unmanaged code. Since the unmanaged code receives a pointer, it is possible for the method to modify the data held on the managed heap. The .NET Compact Framework also supports passing value types by reference, using the ref keyword in C# and ByRef in Visual Basic .NET. The following example takes three double parameters, passed by reference, and returns the arithmetic mean through a fourth parameter, also passed by reference.

extern "C" _declspec(dllexport) void mean(double* x, double* y, double* z, 
  double* mean)
{
    *mean = (*x + *y + *z) / 3.0;
}

To call this from managed code:

[DllImport("MarshalByRefDemo.dll")]
extern static void mean(ref double x, ref double y, ref double z, ref 
  double mean);
.
.
.
double x = 1.0;
double y = 3.0;
double z = 5.0;
double avg = 0.0;
mean(ref x, ref y, ref z, ref avg);
MessageBox.Show(String.Format("The mean is {0}", avg));

Marshalling Arrays

Array parameters have different representations in managed and unmanaged environments. In C/C++, arrays are representations as pointers to a contiguous region of memory with the array elements addressed as offsets from this pointer, starting with zero. The marshaler in the .NET Compact Framework Common Language Runtime (CLR) ensures that managed arrays adhere to this format when passed to unmanaged code. In this C/C++ example, an array is passed into a function and is searched to locate the smallest value:

extern "C" _declspec(dllexport) int MinArray(int* pData, int length)
{
    // Initialise minData to the first element of the pData Array
    int minData = pData[0];
    int pos;

    // Loop through the array
    for(pos = 1; pos < length; pos++)
    {
        // If the current element is less than minData, 
        // set minData to the value of current element
        if(pData[pos] < minData)
            minData = pData[pos];
    }

    return minData;
}

The following code declares the above method and calls it from C#:

[DllImport("MarshalArray.dll")]
extern static int MinArray(int[] pData, int length);
.
.
.
int[] sampleData = int[] {5, 1, 3 };
int result = MinArray(sampleData, sampleData.Length);
MessageBox.Show(String.Format("Smallest integer is {0}, result));

Marshalling Structures and Classes

The .NET Compact Framework supports automatic marshaling of structures and classes that contain simple types. All fields are laid out sequentially in memory in the same order as they appear in the structure or class definition. Both classes and structures appear in native code as pointers to C/C++ structs.

Objects in the managed heap can be moved around in memory at any time by the garbage collector, so their physical addresses may change without notice. P/Invoke automatically pins down managed objects passed by reference for the duration of each method call. This means pointers passed to unmanaged code will be valid for that one call. Bear in mind that there is no guarantee that the object will not be moved to a different memory address on subsequent calls.

There is no marshaling of a class’s method entry points, so you cannot call a managed method in a class passed to unmanaged code as a pointer.

Below is an example of a C/C++ structure that defines a Contact entity in terms of first name, last name and email address.

struct Contact
{
    WCHAR* FirstName;
    WCHAR* LastName;
    WCHAR* EmailAddress;
}

This unmanaged struct cannot be automatically marshaled by the CLR. You need to manually marshal managed strings to C/C++ WCHAR pointers, which is out of the scope of this article.

Marshalling as Other Types

If you are familiar with the full .NET Framework, you might be aware of the MarshalAs attribute, which allows a type to marshal as if it were a different type. This is not available in the .NET Compact Framework. If you require a type conversion before or after a call, it must be performed explicitly in the managed code.

More Information

For further information, please see the following resources:

Did you find this helpful?
(1500 characters remaining)
Thank you for your feedback
Show:
© 2014 Microsoft. All rights reserved.