locked
Linking C# with C - dynamic and static

    Question

  • I've been trying to find information about how I can get C# to call C routines including how to pass strings, so that C# needs access to the C address space. There are a couple of places on the web that say how to do this, but none work as described. I get the error:

    PInvokeStackImbalance was detected Message: A call to PInvoke function
    'testming!call_dll::func_dll' has unbalanced the stack. This is likely
    because the managed PInvoke signature does not match the unmanaged
    target signature. Check that the calling convention and parameters of
    the PInvoke signature match the target unmanaged signature.

    I made sure that the prototype of the routine as invoked in C# and as declared in C was correct but it didn't help. I found I could make a C routine successfully return a value to C#, but anytime I added in a simple argument to the C routine, I'd get that error. I even typed in an example directly in a tutorial web page and got the same error. I'm thinking that MS did something in Visual Studio C# 2010 Express that made it incompatible with C code compiled with gcc. I could post the code, but I figured I'd see if anybody has any ideas that I could try different from what I've already done first. Or maybe a pointer to someplace that's done it before.

    So, my question is how can I compile C code in a way that is compatible with C# code compiled in 2010 Express? I need to use 2010 Express because it has WPF built in.

    I can do a static link in my application, but it'd be nice to see how to make dynamic linkage work too. And a side question, all the MS examples use the  extern "C" with __declspec(dllexport) and __declspec(dllimport) constructs, but some examples I see out there don't use these at all. Are they necessary?

    Monday, February 21, 2011 12:52 AM

All replies

  • This is most likely due to having the calling convention specified incorrectly.  This is why you shoudl always use extern "C" _declspec(dllexport).

     

    The extern "C" portion prevents C++ name mangling.  Without this, a routine named "Foo" will not be exported as "Foo", but rather a name mangled version.  The _declspec(dllexport) portion is usually used because it does two things - it forces this to be exported, and it also sets it to stdcall calling convention.  Without this, it will be exported using cdecl.

     

    The stack mismatch is probably because of one of the above.  Make sure the signature matches, then try adding:

    [DllImport("textming.dll",CallingConvention=CallingConvention.Cdecl)]
    // Your method sig. here...
    
    

    If you use the extern "C" + declspec above, you can leave the calling convention off, since StdCall is the default.

     


    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".
    • Proposed as answer by Reed Copsey, JrMVP Monday, February 21, 2011 6:44 PM
    • Marked as answer by Cookie Luo Tuesday, March 08, 2011 5:58 AM
    • Unmarked as answer by Cookie Luo Tuesday, March 08, 2011 6:42 AM
    Monday, February 21, 2011 6:06 AM
  • [I thought I posted a reply but it's not here so I'll repost]

    That works, thanks! However even if I use the extern C and declspec, I still needed the calling convention thing, maybe the default got changed in the Visual Studio? Got a couple of follow-on questions...

    Is there any issue with passing string args and a string return value? In other words, the C routine would need access to the C# address space and visa versa for string pointers to work. Are both program images linked into in the same address space?

    Is there a way that I can get a printf in the C routine to print to the console or any other way I can print to the console other than having a routine call and passing a string?

    Would statically linking C to the C# program help with printing?

    In fact, I may want to statically link them anyway, how do I tell Visual Studio C# 2010 Express to to link to a C-compiled object file at build time?

    TIA!

    Monday, February 21, 2011 5:32 PM
  • Is there any issue with passing string args and a string return value? In other words, the C routine would need access to the C# address space and visa versa for string pointers to work. Are both program images linked into in the same address space?

     

    You have to be careful with marshaling string data.  Typically, you'll want to make your C API look something like:

     

    void WriteToString(char* buffer, int maxLength);

     

    Then, in C#, you'd marshal this as a StringBuilder (preallocated to a size) and pass the allocated length to the second argument.  The C++ code could then just strncpy into the buffer as needed.

     

    Is there a way that I can get a printf in the C routine to print to the console or any other way I can print to the console other than having a routine call and passing a string?

     

    Printf, by default, writes to the standard stream.  This will just work with console applications.  The cout stream should work, as well, if you're using C++.  

    That being said, passing a string back and forth has some advantages.  It allows you to make your library more general purpose - for example, if you later decide to wrap this in a Windows Forms or WPF application, you may want to display the message in a Window instead of the Console.  By passing a string, you can change this later without changing your C++ code.

     

    Would statically linking C to the C# program help with printing?

     

    You can't.  There is no way to "statically link" C code into a C# program.  You'll need to make a DLL, and P/Invoke into it.

     

    The other alternative here, btw, is to use C++/CLI.  This allows you to directly make .NET types that use your native code, but are consumable by any .NET language (including C#).

     

     


    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".
    Monday, February 21, 2011 6:48 PM
  • Sigh, whenever I think I've got all the info I need, I find I'm missing something...

    Strings in C# are 16-bits, but in C they're 8-bits.

    And in the Visual C# 2010 'John Sharp' book, he doesn't mention 'byte' as a data type at all. Looks like the Byte object is in the System namespace, but there's no 'byte' primitive?

    How does the square peg plug into the round hole, both for the argument, as well as if I return a string as the C-routine value, C# has to get the 8-bit characters from C and turn them into 16-bit characters to make a C# string somehow?

    In an example I found on the web, the C# DLLImport line has this for the string arg declaration:

       [MarshalAs(UnmanagedType.LPArray)] byte[] string_filled_in_dll,
    Is that how you do it? And is there some similar MarshalAs declaration I
    can do for the return value of the C routine so I can return a string?

    Where does the 'byte[]' type get defined in the example I found?

    TIA!
    Tuesday, February 22, 2011 12:08 AM
  • For the most part, you'll want to use StringBuilder or string to marshal to an 8-bit string (char*) in C.  You will likely want to use [MarshalAs(UnmanagedType.LPStr)]

     

    For example, if you want to have C "fill in" a string, you could do:

     

     

    [DllImport("YourDLL")]
    static extern void YourMethod([MarshalAs(UnmanagedType.LPTStr)]
      StringBuilder stringToFillIn, uint bufferSize);
    

    This causes the compiler to automatically handle the conversion from C# strings (16-bit) to "C" strings (8-bit char*).


    If you're writing to the string from C, use StringBuilder, and pre-allocate the stringbuidler's capacity.  if you're just sending data TO the C API, you can use string.

     

     

     


    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".
    Tuesday, February 22, 2011 6:35 AM
  • Wow, I've got it stumbling to its feet! However, I did have some trouble with StringBuilder, so because it seems mostly for performance, I dropped back to just strings. For my application, the strings are small and performance is not an issue.

    However, the printf in the C code does not seem to be printing to anywhere when I run it under Visual Studio C# 2010 Express but I do get the strings written with Console.Write.Line(). Is there something else I need to do to get C printf's to work?

    Also, I notice in some examples they have "In" and "Out" associated with the MarshalAs like:

     [In, MarshalAs(UnmanagedType.LPStr)]

    Does that mean that without In or Out that the MarshalAs does the right thing bidirectionally (for instance when passing a pointer, strings can be written by C or C#) and the In/Out restricts the arg to be used in only one direction? So if I use "In", C# would write the string to be read by C?

     

    Tuesday, February 22, 2011 11:46 PM
  • The In/Out really makes it so you can tell the marshaller that it doesn't have to worry about bringing data back.  For the most part, if you're going to write to a string from C, you should always use StringBuilder.  If you're writing from C#, and passing to C, you can use string.  It'll tend to handle it properly in that case.

     

    As for the printf - normally, standard output is redirected properly, and it's fine.  If you're having problems, you might want to force a redirection of stnadard output FILE* streams to the standard output handle.  Here's a post demonstrating how to do this (in C++): http://www.google.com/search?sourceid=chrome&ie=UTF-8&q=printf+not+writing+to+console

     

    Once you redirect, printf should write properly to the console.


    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".
    Tuesday, February 22, 2011 11:59 PM
  • Sorry, I looked at that link you posted, and saw a number of items, and found one close, so I tried for printing:

      fprintf(GetStdHandle(STD_OUTPUT_HANDLE), "Test output to stdout\n");

    wouldn't compile, and

      fprintf(stdout, "Test output to stdout\n");

    doesn't print anything. Any more concrete pointers would be appreciated.

    Also, could I expect the DLL interface technique to be similar if I port it to the Windows Phone environment? Of course I'd have to cross-compile it to ARM, but anything besides that? And any old gcc would do, or does MS have a compiler for that?

    Wednesday, February 23, 2011 5:36 AM