0

I don`t understand this code below, that it is working with my c++ but it just does not want to work with c#. Can you please help me to understand what is wrong here and i think i have to say that i am absolutely new to C#.

    My_Lib.h 
    extern "C"  __declspec(dllexport) void __cdecl get(char** buffer);

    My_lib.c 
    void get(char** buffer)
    {
    *buffer = (char*)calloc(6, sizeof(char));
    assert(*buffer);

    buffer[5] = '\0';

    *buffer = "Hello";
    }

in my C#----->

public static class NativeMethods
    {
        [DllImport("My_C_Lib.dll", CallingConvention = CallingConvention.Cdecl)]

        unsafe public static extern void get(char** buffer);
    }
 //////////////////// Main()///////
        unsafe
            {
                char* my_buf;

                NativeMethods.get(&my_buf);

                string s = new string(my_buf);

                Console.WriteLine(s);
            }

NOTE: Actually my DLL does Work when i call this c function from c++ but as i said above NOT in C#, there is no Errors but string s variable in C# prints some undefined sibols, but DLL "works" thanks in advance!!!

Abdurasul1996
  • 51
  • 1
  • 8
  • `char` in C# is not the same thing as `char` in C. Don't use raw pointers in C#, use [PInvoke](https://learn.microsoft.com/en-us/cpp/dotnet/how-to-call-native-dlls-from-managed-code-using-pinvoke). – Mark Benningfield Jan 05 '18 at 19:06
  • thank you, can you please show me how to use PInvoke in this case – Abdurasul1996 Jan 05 '18 at 19:11
  • Also note that you have a memory leak here. – Ben Voigt Jan 05 '18 at 19:13
  • 4
    Marshaling calls to unmanaged code that uses raw pointers and allocators, and uses string encodings that are not natively supported in C#, is **really hard to get right**. It is not a task for beginners. Hire an expert, or start with an easier problem. This is not a site for tutoring people on complex subjects; do some reading up on interop and marshaling. – Eric Lippert Jan 05 '18 at 19:13
  • the bible on pinvoke is adam nathans book https://www.amazon.com/NET-COM-Complete-Interoperability-Guide/dp/067232170X – pm100 Jan 05 '18 at 19:16

2 Answers2

1

The code is nearly correct, but...

C DLL does NOT Work in C# but Does work in C++

In C and C++ char is a 8-bit data type. In C# char is a 16-bit data type.

This means that C# expects that the pointer returned by the get() function is expected to be a "wide string", while in C++ expects an "ANSI string".

I simply changed one single line in your program:

*buffer = "H\0e\0l\0l\0o\0\0\0";

... and it works!

You may of course also use the "wide string" functions of the modern C compilers:

void get(wchar_t** buffer)
{
    *buffer = L"Hello";
}

By the way

There is another error in your program:

*buffer = (char*)calloc(6, sizeof(char));
...
*buffer = "Hello";

This makes no sense:

The second line (*buffer = "Hello";) will not copy the string to the memory allocated by calloc, but it will write the address of the string "Hello" to the variable buffer and the value (address) returned by calloc is overwritten (and lost).

Martin Rosenau
  • 17,897
  • 3
  • 19
  • 38
0

Your best bet is to change the PInvoke signature of your C function to taking a ref IntPtr:

[DllImport("My_C_Lib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void get(ref IntPtr buffer);

To call this, you'll need to instantiate an IntPtr and pass it by reference to the C function (this is analagous to declaring a char * in C and passing it by address):

IntPtr ptr;
get(ref ptr);
// ptr is now an unmanaged pointer to the allocated string

Now, you need to convert the pointed-to string to a managed string. To do this you must make a copy of the string.

string str = Marshal.PtrToStringAnsi(ptr);
  • Be sure you understand the distinctions between ANSI and Unicode strings and make sure you call the appropriate PtrToString...() variant.
  • Note that the .NET runtime will not manage the allocated ptr for you since it was allocated by unmanaged code. You must free it yourself using whatever the appropriate mechanism is for your DLL (ideally, the DLL should provide a corresponding free function since it is the one who allocated the memory in the first place).
  • For robustness, add appropriate error / null pointer checking. Make sure get() succeeded and that ptr is not IntPtr.Zero.
  • To avoid leaks, if there is any possibility of any code throwing an exception between the time get() returns and the time you free the pointer, the try/finally construct is your friend.

(Aside: Note that get is a Contextual Keyword in C# and thus while you can use it as an identifier, you may prefer not to in order to avoid confusion.)

TypeIA
  • 16,916
  • 1
  • 38
  • 52