C DLL不能在C#中工作,但在C ++中工作

时间:2018-01-05 19:00:36

标签: c# c dll unsafe

我不理解下面的代码,它正在使用我的c ++,但它只是不想使用c#。 你能帮我理解这里有什么问题,我想我必须说我对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";
    }

在我的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);
            }

注意:实际上当我从c ++调用这个c函数时我的DLL工作正常但正如我上面所说的那样,不是在C#中,没有错误,但C#中的字符串变量打印出一些未定义的sibols,但是DLL“工作” 提前谢谢!!!

2 个答案:

答案 0 :(得分:1)

代码几乎是正确的,但是......

  

C DLL在C#中不起作用,但在C ++中起作用

在C和C ++中char是一种8位数据类型。在C#char中是一个16位数据类型。

这意味着C#期望get()函数返回的指针应该是“宽字符串”,而在C ++中则需要“ANSI字符串”。

我只是改变了程序中的一行:

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

......它有效!

您当然也可以使用现代C编译器的“宽字符串”功能:

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

顺便说一下

您的计划中还有其他错误:

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

这没有任何意义:

第二行(*buffer = "Hello";)将将字符串复制到calloc分配的内存中,但它会写入字符串"Hello"的地址变量buffercalloc返回的值(地址)被覆盖(并丢失)。

答案 1 :(得分:0)

最好的办法是将C函数的PInvoke签名更改为ref IntPtr

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

要调用它,您需要实例化IntPtr并通过引用C函数传递它(这与在C中声明char *并通过地址传递它类似):

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

现在,您需要convert the pointed-to string to a managed string。为此,您必须复制该字符串。

string str = Marshal.PtrToStringAnsi(ptr);
  • 请务必了解ANSI和Unicode字符串之间的区别,并确保调用相应的PtrToString...()变体。
  • 请注意, .NET运行时将不会为您分配ptr ,因为它是由非托管代码分配的。您必须使用适用于您的DLL的适当机制自由释放它(理想情况下,DLL应该提供相应的自由函数,因为它是首先分配内存的那个)。
  • 为了增强健壮性,请添加适当的错误/空指针检查。确保get()成功,ptr不是IntPtr.Zero
  • 为了避免泄漏,如果任何代码在get()返回的时间与释放指针的时间之间发生异常,则try/finally construct是您的朋友。

(旁白:请注意get在C#中是Contextual Keyword,因此虽然您可以将其用作标识符,但您可能不愿意为了避免混淆。)