Click here to Skip to main content
6,629,885 members and growing! (22,145 online)
Email Password   helpLost your password?
Platforms, Frameworks & Libraries » Mobile Development » Compact Framework     Beginner License: The Code Project Open License (CPOL)

Marshaling data in the Compact Framework

By Eugene Kordin, Apriorit Inc

In this article, we will review some aspects of using data types and ways of using them during marshaling data. We’ll concentrate on marshaling to and from unmanaged code.
C++, C#, Windows, .NET CF, Visual Studio, WCF, Dev
Version:3 (See All)
Posted:26 Jun 2009
Views:2,700
Bookmarked:6 times
Announcements
Loading...
 
Search    
Advanced Search
Add to IE Search
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
6 votes for this article.
Popularity: 3.78 Rating: 4.86 out of 5

1

2

3
1 vote, 16.7%
4
5 votes, 83.3%
5

Introduction

In many situations, when we create applications for different embedded systems or mobile platforms, we can't develop all parts of the product using managed code only.

For example, we need several modules written in native language which perform some low level operations, or we already have these libraries written in C++. So we have to use more than one programming language in our product and also use data marshaling in it.

In this articlen we will review some aspects of using data types and ways of using them during marshalling data to and from unmanaged code.

Making your interop calls more efficient

Marshaling is the act of taking data from one environment to another. In the context of .NET, marshaling refers to transferring data from the app-domain you are in to somewhere else, outside.

You should remember that such Platform Invoke calls are slower than direct native calls and than regular managed calls. The speed depends on the types marshaled between managed and native code, but nevertheless, you should avoid using Platform Invoke calls if you have a chance to do this. Also, it is recommended to use calls with some amount of transferred data than several "small" Platform Invoke calls.

Blittable types

It is recommended to use simple data types (int, byte, boolean, characters, and strings). It makes the call more efficient and helps to avoid any conversions and copying. These blittable types have identical representation in both managed and unmanaged memory. But, you should remember that in Compact Framework, during marshaling, the boolean type is represented as a 1-byte integer value (instead of a 4-byte integer value in the full .NET Framework), character type (char) is always represented as a 2-bytes Unicode character, and the String type is always treated as a Unicode array (in the full .NET Framework, it may be treated as a Unicode or ANSI array, or a BSTR).

Method inlining

The JIT compiler can inline some methods in order to make the calls more efficient. You can not force a method to be inlined by the compiler, but you can make it not to be inlined. In order to avoid inlining, you can:

  • make the method virtual;
  • add branching to the method's body;
  • define local variables in the method;
  • use 2-bit floating point arguments (or return value).

Disabling method inlining can help to detect a problem during Platform Invoke calls.

Sequential layout

In the Compact Framework, all structures and classes always have sequential layout (the managed value type has the same memory layout as the unmanaged structure). This behavior can be specified by setting the LayoutKind.Sequential attribute. You don't need to specify this attribute in the Compact Framework, but if you use these pieces of code in both the full .NET Framework and the Compact Framework, you have to set it to avoid different behaviors on two platforms.

The following sample shows how to send some pointers from C# code for storing them in the native module.

C#:

[StructLayout(LayoutKind.Sequential)]
public class BasePointers // you can use the struct too
{
    public IntPtr pointer1;
    public IntPtr pointer2;
}

[DllImport("NativeDLL.dll", CallingConvention = CallingConvention.Winapi)] // Cdecl
public static extern int TransferStruct(BasePointers pointers);

C++:

struct BasePointers
{
   unsigned int pointer1;
   unsigned int pointer2;
}

extern "C" __declspec(dllexport) int CDECL 
           TransferArray(BasePointers* pointers);

One calling convention

The calling convention determines the order in which parameters are passed to the function and who is responsible for the stack cleaning. The .NET Compact Framework supports only the WinAPI value (CDECL on this platform) of calling convention. It defines the calling convention for C and C++ (the full .NET Framework supports three different calling conventions). To avoid crashes of your application, you should make sure that your calling conventions in both the managed and native declarations are the same.

If you specify the attribute to preserve the signature of functions ([PreserveSig]), then the returned value will contain a 32-bit HRESULT that will give you more data to analyze errors during the native function execution. The calling convention can be specified by adding the attribute CallingConvention to the declaration of your function. As it was mentioned, the .NET Compact Framework supports only the "WinAPI" calling convention that corresponds to CDECL:

C#:

[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int ProgressEventHandler(int progressValue);

C++:

typedef void (CDECL *ProgressEventHandler)(int progressValue);

Data alignment

In some situations, we need to transfer data between the managed and unmanaged code in the structures. As it's written above, all structures have sequential layout in the Compact Framework, but you should remember about the representation of structs in managed and unmanaged code. The way of packing structures depends on a platform and on how the members of the structures are aligned. On an ARM platform, this value for alignment is four (all values in structures are aligned to 4 bytes).

typedef struct OurStruct
{
  unsigned char valueChar;
  usingned int valueInt;
} ourStruct_;

This structure could be perfectly acceptable in desktop code, but if you use such a on the Windows Mobile platform, then you might receive a valueInt at the offset 4. If you use such structures in both the desktop and the device's code, you have to use them carefully during marshaling.

During marshaling data, you might receive such errors as "Datatype misalignment" (0x80000002) or "Access violation" (0x80000005). It indicates that you are using wrong pointers or are trying to access the wrong offset of data. For example, you transfer an array of bytes from C# code to the native module and define your function as:

C#:

[DllImport("NativeDLL.dll", CallingConvention = CallingConvention.Winapi)] // Cdecl
public static extern int TransferArray(IntPtr src, int srcSize);

C++ Native Module code:

extern "C" __declspec(dllexport) int CDECL TransferArray(byte* srcArr, int srcSize);

If you try to use the pointer "srcArr" as a pointer to an integer (int*) and then try to use the corresponding value, you will receive an error:

int value = *(int*)srcArr;  // Datatype misalignment

The simple way to avoid this problem is to change the declaration of the C++ function and change the pointer to the array of bytes to the pointer to the array of integers and use it without any problems:

extern "C" __declspec(dllexport) int CDECL TransferArray(int* srcArr, int srcSize);

Marshal class

You can use methods in the class Marshal to manually convert managed objects and perform conversions between IntPtrs. These methods are PtrToStructure, GetComInterfaceForObject, PtrToStringBSTR, GetFunctionPointerForDelegate and others. It allows you to control marshaling. These methods are also useful for debugging issues with marshaling parameters where the runtime is not able to convert a particular argument.

You cannot pass the delegate directly to the native module as the parameter of your function because the .NET Compact Framework does not support marshaling of delegates. Instead, you should use the method Marshal.GetFunctionPointerForDelegate for getting the function pointer which you can pass to the native code and call it.

Code:

class MainClass
{
   [UnmanagedFunctionPointer(CallingConvention.Winapi)]
   public delegate int ProgressEventHandler(int progressValue);

   ...
    void OnProgressChanged(int progressValue)
    {
        ...
    }
   ...
 
   ... 

   [DllImport("NativeDLL.dll", 
      CallingConvention = CallingConvention.Winapi)] // Cdecl
   public static extern int SetCallbackFunction(IntPtr functionPointer);
}


// Passing  function pointer

Delegate d = new  ProgressEventHandler(OnProgressChanged);

IntPtr progressChanged =  Marshal.GetFunctionPointerForDelegate(d);

int result = SetCallbackFunction(progressChanged);

But, you should be aware of the Garbage Collector (GC) in such a situation. The GC might collect your delegates, and your function pointers will become invalid. It may happen when you pass the function pointer to the native code as a callback method in order to call it later - the GC might think that there are no references to it in the managed code. To avoid this situation, you should keep a reference to this delegate. For example, you can store it in the classes variable, or create some delegates pool, in which you can keep the references to the several delegates.

GCHandle

Since we're passing a pointer to some data, we need to allocate memory for that data and make sure that the GC will not remove that memory. One of the possible ways to manage this situation is to use a GCHandle.

If you want to pass some class (or array of bytes) to unmanaged code and you need to pin the memory for properly working with it in unmanaged code, you can write:

class SampleClass
{
 ...
}

SampleClass classSample = new SampleClass();

GCHandle classHandle = GCHandle.Alloc(classSample, GCHandleType.Pinned);
IntPtr ptrToClass = classHandle.AddrOfPinnedObject(); 

int result = PassPtrToUnmanagedCode(ptrToClass); // our function

You can also make an instance of GCHandle as a member of the class to avoid deleting them by the GC. Also, you should remember that the structure is a value-type. And, pinning it to the memory will cause a problem, because the structure will be copied and the GCHandle will handle a reference to the created "boxed" copy of the object. It will be hard to track such problems in the future.

Conclusion

During marshaling data, you may face the problems described above. Very often, you may get "NotSupportedException" and other exceptions. To track problem, you can enable the logging of setting the Registry keys. One of the logging components is "Interop". The log provides information about Platform Invoke calls and marshaling. You can read the MSDN for more information about creating log files.

With the .NET Compact Framework 2.0, you can use Platform Invoke calls in managed applications, even though there are a few limitations. You should remember all the differences and the limitations between the full .NET Framework and the Compact Framework to avoid problems in your applications.

Resources

Creating Log Files

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Authors

Eugene Kordin


Member

Occupation: Software Developer
Company: ApriorIT
Location: Ukraine Ukraine

Apriorit Inc


Member
ApriorIT is a Software Research and Development company that works in advanced knowledge-intensive scopes.

Company offers integrated research&development services for the software projects in such directions as Corporate Security, Remote Control, Mobile Development, Embedded Systems, Virtualization, Drivers and others.

Official site http://www.apriorit.com
Company: Apriorit Inc.
Location: Ukraine Ukraine

Other popular Mobile Development articles:

  • Writing Your Own GPS Applications: Part 2
    In part two of the series, the author of "GPS.NET" teaches developers how to write GPS applications suitable for the real world by mastering GPS precision concepts. Source code includes a working NMEA interpreter and sample high-precision application in C# and VB.NET.
  • Writing Your Own GPS Applications: Part I
    What is it that GPS applications need to be good enough to use for in-car navigation? Also, how does the process of interpreting GPS data actually work? In this three-part series, I will cover both topics and give you the skills you need to write a commercial-grade GPS application.
  • Learn How to Find GPS Location on Any SmartPhone, and Then Make it Relevant
    A step by step tutorial for getting GPS from any SmartPhone, even without GPS built in, and then making location useful.
  • iPhone UI in Windows Mobile
    It's an interface that works with transparency effects. As a sample I used an interface just like the iPhone one. In this tutorial I am explaining how simple is working with transparency on Windows Mobile.
  • Pocket 1945 - A C# .NET CF Shooter
    An article on Pocket PC game development
Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
  (Refresh) 
-- There are no messages in this forum --

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 26 Jun 2009
Editor: Smitha Vijayan
Copyright 2009 by Eugene Kordin, Apriorit Inc
Everything else Copyright © CodeProject, 1999-2009
Web17 | Advertise on the Code Project