Monday, August 3, 2009

C# calling C DLL routines (YET ANOTHER ONE)

I am totally baffled on the examples given by sites on how to call C routines in DLL via C#.
Not only are many of them repeating one same example after another, you soon realize they are not very helpful when you want to do something that is common like passing a structure with all kinds of types inside.

So here is one example that serve as an answer to MANY of the pinvoke/calling C apis problem :- ( i wanted to say 90% but i can't justify or prove it ;-))

I got this example structure that has :

struct ToughCallDef
{
char * ptrName; // this will contain some return string values
int ptrNameLen; // normal integer
MEMBLOCK somestruct; // a structure
ANOTERBLOCK* structpointer; // structure pointer
int* ptrtoMyInt; // integer pointer
char * ptrID // this must be supplied
};

Just translate it this way :-

[StructLayout(LayoutKind.Sequential)]
public class ToughCallDef
{
IntPtr ptrName;
MEMBLOCK somestruct; // you need to define MEMBLOCK also
IntPtr structPointer;
IntPtr ptrtoMyInt;
IntPtr ptrID;
}

And if the call requires a reference to this "ToughCall" :

[DllImport("anotherc.dll")]
public static extern int Call_C_API(ref ptrToughCall);

define it this way instead :

[DllImport("anotherc.dll")]
public static extern int Call_C_API(IntPtr ptrToughCall);


Basically anything to do with Pointers, regardless of whether its integer *, char *, structure reference etc, just use IntPtr.


1 . If the member is a "char *" that you expect return values you point the IntPtr this way :

szReason = new StringBuilder(100);
zReason.Append(' ', 100); // empty string with spaces
ToughCall.PtrName = Marshal.StringToHGlobalAnsi(m_szReason.ToString());


2. If the member is a "char *" that you need to assign a value before passing in :

ToughCall.ptrID = Marshal.StringToHGlobalAnsi(StringVar);


3. If the member is a structure pointer, you assign it this way :

ANOTHERBLOCK o = new ANOTHERBLOCK();
ToughCall.structPointer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ANOTHERBLOCK)));
Marshal.StructureToPtr(o, ToughCall.structPointer, true);



4. After calling the C routine you will need to do the following to get the values :-
  1. If its a string, use :
    string s = Marshal.PtrToStringAnsi(ToughCall.PtrName)
  2. If its a structure use :
    Marshal.PtrToStructure(ToughCall.structPointer, o);
  3. Free any allocated strings/struct pointers via :
    Marshal.FreeHGlobal

That's it.

5 comments:

  1. Wow. You're right. That's it. Thanks for the very clear examples!

    ReplyDelete
  2. Thanks for the tip.

    ... but how to define ptrToughCall ?

    ReplyDelete
  3. Very, very, very, very helpful, thank you :)

    ReplyDelete
  4. Thanks for the helpful article. I am, however, not able to get my call to a c dll.

    This is the signature from the dll header:
    extern int sdms_getlocks(int, char *, FILE_LIST *);

    This is how I defined the function:
    const string _dllLocation = "C:\\Program Files (x86)\\Synergex\\SynergyDE\\dbl\\bin\\sdms.dll";
    [DllImport(_dllLocation, EntryPoint = "sdms_getlocks")]
    public static extern Int32 sdms_getlocks(
    int flags,
    IntPtr hostname,
    IntPtr file_head
    );

    And this is how I call the function:
    UnsafeNativeMethods.FILE_LIST file_head = new UnsafeNativeMethods.FILE_LIST();

    Int32 flags = 0;
    IntPtr host = Marshal.StringToHGlobalAnsi("steveh");

    IntPtr x = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(UnsafeNativeMethods.FILE_LIST)));
    Marshal.StructureToPtr(file_head, x, true);

    Int32 stat = UnsafeNativeMethods.sdms_getlocks(flags, host, x);

    Marshal.PtrToStructure(x, file_head);

    Marshal.FreeHGlobal(x);

    When I try to step over the call I get the error "PInvokeStackImbalance was detected...A call to PInvoke function...sdms_getlocks has unbalanced the stack.

    Please, what am I doing wrong?

    ReplyDelete
  5. Hi there, I enjoy reading through your post. I like to write a little comment to support you.


    Also visit my website; golf galaxy gift card value

    ReplyDelete