【发布时间】:2013-12-10 12:28:10
【问题描述】:
我已经在 stackoverflow 上搜索过这个问题,但没有找到这个确切的问题。特别是,当 C# 端管理内存时,我发现了大量关于检索 C++ 字符串引用 (char**) 的问题,而不是相反。
情况如下。我有一个我自己编写的 DLL(在 VC++ 2012 上编写)。它从文件中加载和解析数据,然后让使用 DLL 的任何人以多种方式访问这些数据(出于性能原因,通过实际的直接内存访问)。任何使用此 DLL 的 C++ 程序显然都没有问题。 C# 看起来确实如此......
一点上下文:
DLL 提供接受 char**(char* 数组)参数的导出函数,其元素被设置到 DLL 中的内存位置,然后在调用后由 DLL 的用户进一步处理。
示例可能如下所示(示例性实现):
int myFunction(char* dataArray[], int row)
{
for (int i= 0; i < _something; ++i)
{
dataArray[i] = _some_char_ptr
}
}
这就是 C++ 中被调用者的实现可能的样子:
char** data = new char*[__num_fields];
myFunction(data, __some_row);
for (int i= 0; i < __something; ++i)
{
cout << data[i] << endl;
}
特殊 - 因此与我发现的所有问题不同 - 关于这一点,被调用者分配了一个 char* 列表,该列表在调用点之后指向 DLL 本身分配的内存位置。
现在,我必须说,我只使用 C#,因为您可以立即获得非常简单的 GUI。我正在使用的 C# GUI 工具用于对 DLL 加载的数据进行完整性测试。我对 C# 一点也不深。到目前为止,我所尝试的根本没有成功,而且我知道由于 C# 没有任何类型的指针概念,可能无法解决这个问题。我在 Java 中使用 JNA 加载 DLL 时发现的类似问题导致我不再使用 Java 进行此类测试。
现在我尝试了一些 C# 的东西,包括。上下文:
public static class DLLTest
{
// delegate object for the function:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate int MyFunction_Type(out string[] dataArray, int row);
public static MyFunction_Type MyFunction { get; private set; }
public static void Load()
{
IntPtr dllAddress = DllUtilities.LoadLibrary(@"__dll_path");
IntPtr procAddress = DllUtilities.GetProcAddress(dllAddress, "myFunction");
MyFunction = (MyFunction_Type)Marshal.GetDelegateForFunctionPointer(procAddress, typeof(MyFunction_Type));
}
}
然后我像这样调用函数:
string[] data = new string[__num_fields];
DLLTest.MyFunction(out data, __row)
使用普通字符串、整数、布尔值之类的类似代码的代码就像魅力一样。只是这些字符串数组一直困扰着我:-)
注意:我无法更改 DLL 的工作方式,因为我是为我的老板编写的,他在 C++ 和 Delphi 程序中都很好地使用了它。它运行良好,我们不打算更改 DLL 本身的任何内容,因为它确实很好。
非常感谢任何帮助或澄清,非常感谢,问候
【问题讨论】:
-
CallingConvention.StdCall为什么? -
这是来自互联网的复制粘贴行。我使用'extern "C"' 导出DLL 的函数,它利用stdcall 调用约定,从而禁用名称修饰。这条确切的线也适用于所有其他有效的 DLL 函数。
-
extern "C"禁用 C++ 修改。它没有设置调用约定。 -
在 x64 上 Std 和 C 是相同的约定,但在 x86 上它们是不同的,因此 C# 必须匹配 C++ 声明
-
这是非常危险的代码,你的 C++ 代码很容易破坏 GC 堆,因为它不知道传递的数组到底有多长。它需要知道 __num_fields 才能安全地执行此操作,但它不需要。还有一个重要的内存管理问题,这些字符串需要再次释放,而 C# 代码无法做到这一点。不要这样做。