5

I have a .DLL file in C. The primary structure required by all functions in that DLL is of the following form.

typedef struct
{
    char *snsAccessID;      
    char *snsSecretKey;     
    char *snsPath;          
    char *snsTopicName;     
    char *snsTopicAmazonResourceName; 
    char *snsDisplayName;   
    char *snsOwnerId;       
} snsTopic, *HSNS;

One of the functions just for example is as follows:

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

All char pointers above are Input parameters.

I am using C# with .NET CF 3.5 on a WinCE6/7 device.

I have tried using a class and then passing the pointer to the structure required by the C function as follows:

public class HSNS
{
    public string snsAccessID;      
    public string snsSecretKey;     
    public string snsPath;          
    public string snsTopicName;     
    public string snsTopicAmazonResourceName; 
    public string snsDisplayName;   
    public string snsOwnerId;       
}

[DllImport("Cloud.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Boolean SnsOpenTopic(string accessID, string secretKey, string ownerId, string path, string topicName, string displayName, ref HSNS snsTopicHandle);

Using the C# snippet above results in NotSupportedException being thrown. I can't figure out what's wrong with the above C# code?

Another thing i tried is using unmanaged code with C#.

unsafe public 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", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Boolean SnsOpenTopic(string accessID, string secretKey, string ownerId, string path, string topicName, string displayName, HSNS *snsTopicHandle);

fixed (HSNS *snsAcsTopicHandle = &snsAcsTopic)
{
    if (SnsOpenTopic(AWS_ACCOUNT_ACCESS_ID, AWS_ACCOUNT_SECRET_KEY, AWS_ACCOUNT_OWNER_ID, AWS_SNS_SINGAPORE_REGION, topicName, displayName, snsAcsTopicHandle))
    {
    }
}

In the above case, in debug i can check the pointers inside the structure are not being populated and in the debug view i can see Invalid Reference. Cannot dereference pointer message. The rest of the functions fail because of this.

What is the correct way to use Platform Invoke and Marshalling for the above scenario. I have tried searching on Google and Stack overflow. Didn't find a use case similar to mine.

Sanchayan Maity
  • 637
  • 6
  • 19

2 Answers2

0

I believe you use IntPtr for char* and "ref" for your structure

[DllImport("Cloud.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Boolean SnsOpenTopic(string accessID, string secretKey, string ownerId, string path, string topicName, string displayName, ref HSNS snsTopicHandle);

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

Then when you want to access your structures results, you need to marshal the IntPtr to a string.

http://msdn.microsoft.com/en-us/library/7b620dhe.aspx

System.Runtime.InteropServices.Marshal.PtrToStringAnsi(snsTopicHandler.snsPath);

Or http://msdn.microsoft.com/en-us/library/ewyktcaa.aspx

System.Runtime.InteropServices.Marshal.PtrToStringAuto(snsTopicHandler.snsPath);
Louis Ricci
  • 20,804
  • 5
  • 48
  • 62
  • I tried what you suggested. Doesn't work. The pointers in the structure are still not populated. This is clear in Debug and also while using Marshal.PtrToStringBSTR(snsAcsTopic.snsDisplayName), a NullException is thrown. – Sanchayan Maity Jul 30 '13 at 05:28
  • @Sanchayan - without knowing your C code it's difficult to help. Are the char arrays in C being globally allocated? – Louis Ricci Jul 30 '13 at 11:39
0

The problem was with encoding. .NET CF 3.5 uses Unicode encoding throughout. The functions in DLL which I had, expected character pointer to strings in ASCII and not Unicode.

I did it like this.

unsafe public 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);

// Sample of encoding conversion
public Byte[] topicName = Encoding.ASCII.GetBytes("CSharpACSAlert\0");

And then using below to get the pointer and fix it

fixed (Byte* ptrTopicName = &topicName[0])

I still have problems with making a few other functions work but the two primary functions have started working.

The below two posts helped a lot.

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

BenMorel
  • 34,448
  • 50
  • 182
  • 322
Sanchayan Maity
  • 637
  • 6
  • 19