从dll调用方法时C#app阻止

时间:2012-07-08 19:30:18

标签: c# c++

我目前正在探索DLL导出函数和C#中的P / invoke。 我创建了非常简单的.dll:

Test.h

#ifndef TEST_DLL_H
#define TEST_DLL_H
extern "C" __declspec(dllexport) const char * __cdecl hello ();
extern "C" __declspec(dllexport) const char * __cdecl test ();    
#endif  // TEST_DLL_H

Test.cpp的

#include <stdlib.h>
#include "test.h"
#include <string.h>

const char* hello()
{
    char *novi = (char *)malloc(51);
    strcpy(novi, "Test.");

    return novi;
}

const char * test()
{
    return "Test.";
}

我编译了它并在C#项目中使用如下:

    [DllImport("test.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr hello();

    [DllImport("test.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern string test();

    private void button1_Click(object sender, EventArgs e)
    {
        MessageBox.Show(test());
        IntPtr a =  hello();
        MessageBox.Show(Marshal.PtrToStringAnsi(a));
    }

但它不起作用。成功调用test()并返回正确的字符串。但是hello()只是暂停程序。如果我从hello()定义中删除malloc行并返回常量,那么一切正常,所以我想我现在已经知道了malloc的问题。

另外,在某些地方我看到当返回类型为char *时不应该使用该字符串。如果这是真的,我们为什么要使用IntPtr?

1 个答案:

答案 0 :(得分:3)

跨越DLL边界返回字符串的函数很难从C或C ++中可靠地调用,当你从C#中执行它时,它没有任何好转。问题是调用者将如何释放字符串缓冲区。这需要为hello()而不是test()完成。一些很难猜到的东西。 hello()函数需要使用free()函数,使用用于调用malloc()的 exact 相同的分配器。这只能在DLL和调用者共享相同的CRT实现时才能工作。可能性很小。

pinvoke marshaller也会释放字符串缓冲区。并且只有合理的选择,CoTaskMemFree()。它使用COM使用的默认分配器。这没有达到目的,你的C代码没有使用CoTaskMemAlloc()。这可能的结果取决于操作系统。在Vista及以上版本中,您的程序将因AccessViolation而死亡,这些Windows版本使用严格的堆分配器来解决行为不端的程序。在XP上,你会在内存泄漏和堆损坏之间得到一些东西,听起来就像你碰到了第二个选项。

将返回值声明为IntPtr将会很好地结束。好吧,你的程序不会崩溃,你仍然有一个无法插入的内存泄漏。没有办法可靠地调用free()。或者在C代码中使用CoTaskMemAlloc(),以便pinvoke marshaller的释放调用可以正常工作。

但实际上,只是不要写这样的C代码。始终使用调用者分配的内存,因此永远不会猜测谁拥有内存。这需要类似于此的函数签名:

extern "C" __declspec(dllexport) 
int hello(char* buffer, int bufferSize);