P/Invoke and Marshaling on the Microsoft .NET Compact Framework

Even though a lot of functionality is provided while working at higher levels for application development, situations arise where you have to use a third party library in your code or may be a library which you yourself developed. Often this library might be in some other language and you will only have access to the Dynamic Link Library or Static Library.

I faced such a situation recently where i had a library which was written in C and the DLL was available for it which i had to use in my C# application. Now had i been working on the desktop it would have been much easier since the full .NET Compact Framework provides much more features than the .NET Compact Framework 3.5 which i had to use, since i was working on Windows CE.

The Platform Invoke is a feature of C# which allows you to call functions which are only available through a native DLL. You can get an overview of this feature at the below link. This is a general explanation of Platform Invoke feature.

http://msdn.microsoft.com/en-us/magazine/cc164123.aspx

What helped me were the below two links which discuss Platform Invoke with respect to the .NET Compact Framework.

http://www.codeproject.com/Articles/5888/An-Introduction-to-P-Invoke-and-Marshaling-on-the

http://www.codeproject.com/Articles/5890/Advanced-P-Invoke-on-the-Microsoft-NET-Compact-Fra

I will not give a detail explanation of my use case, since that should be clear after one has read the above two articles.

C :


typedef struct

{
 char *snsAccessID; ///<Pointer to hold address of AWS Account AccessId string
 char *snsSecretKey; ///<Pointer to hold address of AWS Account SecretKey string
 char *snsPath; ///<Pointer to hold address of AWS Account Sns region Path string
 char *snsTopicName; ///<Pointer to hold address of AWS Account AccessID string
 char *snsTopicAmazonResourceName; ///<Pointer to hold address of AWS Account AccessID string
 char *snsDisplayName; ///<Pointer to hold address of Topic Display Name
 char *snsOwnerId; ///<Pointer to hold address of AWS Account OwnerId string
} snsTopic, *HSNS;

BOOL SnsOpenTopic(char *accessID, char *secretKey, char *ownerId, char *path, char *topicName, char *displayName, HSNS *snsTopicHandle);

C# :


[StructLayout(LayoutKind.Sequential)]
 public unsafe struct HSNS
 {
 public char *snsAccessID;
 public char *snsSecretKey;
 public char *snsPath;
 public char *snsTopicName;
 public char *snsTopicAmazonResourceName;
 public char *snsDisplayName;
 public char *snsOwnerId;
 }

 [DllImport("Cloud.dll", SetLastError = true)]
 public unsafe static extern Boolean SnsOpenTopic(Byte* accessID, Byte* secretKey, Byte* ownerId, Byte* path, Byte* topicName, Byte* displayName, ref HSNS snsAcsTopic);

public Byte[] topicName = Encoding.ASCII.GetBytes("CSharpACSAlert\0");

HSNS snsAcsTopic = new HSNS();
 fixed (Byte* accessID = &AWS_ACCOUNT_ACCESS_ID[0])
 {
 fixed (Byte* secretKey = &AWS_ACCOUNT_SECRET_KEY[0])
 {
 fixed (Byte* ownerId = &AWS_ACCOUNT_OWNER_ID[0])
 {
 fixed (Byte* path = &AWS_SNS_SINGAPORE_REGION[0])
 {
 fixed (Byte* ptrTopicName = &topicName[0])
 {
 fixed (Byte* ptrDisplayName = &displayName[0])
 {
 fixed (Byte* ptrEmailId = &emailId[0])
 {
 fixed (Byte* ptrSubject = &subject[0])
 {
 fixed (Byte* ptrMessage = &message[0])
 {
 if (SnsOpenTopic(accessID, secretKey, ownerId, path, ptrTopicName, ptrDisplayName, ref snsAcsTopic))
 {
 if (SnsCreateTopic(snsAcsTopic))
 {
 MessageBox.Show("Done");
 }
 else
 {
 MessageBox.Show("Could not create topic");
 MessageBox.Show(Convert.ToString(Marshal.GetLastWin32Error()));
 }
 }
 else
 {
 MessageBox.Show("Could not open topic");
 MessageBox.Show(Convert.ToString(Marshal.GetLastWin32Error()));
 }
 }
 }
 }
 }
 }
 }
 }
 }
 }

N.B. Above is only a snippet of my code and not the complete source. But, this is complete enough to demonstrate how to use Platform Invoke for such a case
First i have given the template which is being used in C and then i have posted the C# equivalent to be used with Platform Invoke.

One of the important points to note here is that if your function expects a pointer to an ANSI string you will have to convert to ASCII encoding by using “Encoding.ASCII.GetBytes”. Then you need to get the pointer to it which has to be passed to the argument expecting an ANSI string. This has to be done since everything in .NET CF3.5 is handled in Unicode. I had a hunch about this being the reason of why my code was not working but i was not sure till i came across those two articles. If your character pointers are “Wide” or Unicode style viz. WChar_t* to be more specific you can skip the encoding conversion.