我在 native DLL 中有以下函数头:
unsigned char* Version_String()
我正在尝试从 C# 项目中调用它,我尝试了以下调用(如在此处其他类似问题中找到的那样):
[DllImport("BSL430.dll", CharSet=CharSet.Ansi)]
public extern static UIntPtr Version_String();
而且我不断收到以下异常:
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
下一次尝试如下,我得到了同样的异常:
[DllImport("BSL430.dll", CharSet=CharSet.Ansi)]
[return : MarshalAs(UnmanagedType.LPStr)]
public extern static string Version_String();
我似乎无法解决这个问题。任何帮助将不胜感激!
编辑:
我不能在这里给出 DLL 代码,因为它属于保密协议(protocol),但我调用的函数如下所示:
unsigned char versionString[50];
__declspec(dllexport) unsigned char* Version_String()
{
if(check_hardware_stuff())
{
strcpy((char *) versionString, "version_string_bla_bla");
versionString[5] = stuff;
}
else if (other_check())
{
//will return empty string, that should be filled with '\0'
}
else
{
strcpy( (char *) versionString, "ERROR" );
}
return versionString;
}
我不是特别喜欢 DLL 实现,但我需要“按原样”使用它。
无论我如何处理返回值,每当我尝试调用 VersionString()
时都会抛出异常。
最佳答案
更新
看过更新后的问题、各种评论和您的 native 函数的代码后,您似乎在调用 check_hardware_stuff()
时引发了异常。调试起来很简单。我会用这样的函数替换您的函数:
unsigned char versionString[50];
__declspec(dllexport) unsigned char* Version_String()
{
strcpy(versionString, "testing");
return versionString;
}
如果仍然失败,那么我猜测错误是在您的 DLL 的 DllMain
中引发的。通过将上述函数放入不执行任何其他操作的普通 DLL 中进行调试。
原始答案
调用约定是最明显的问题。您的 native 代码很可能使用 cdecl
,但 p/invoke 默认是 stdcall
。将您的 p/invoke 签名更改为如下所示:
[DllImport("BSL430.dll", CallingConvention=CallingConvention.Cdecl)]
public extern static IntPtr Version_String();
您可以安全地省略 CharSet
参数,因为所有参数都没有文本,因为您将返回值视为指针。
编辑: Hans 在评论中正确地指出,由于没有参数,调用约定不匹配并不重要。所以这不是问题。
调用Marshal.PtrToStringAnsi
转换为 .net 字符串。
string version = Marshal.PtrToStringAnsi(Version_String());
由于 PtrToStringAnsi
需要一个 IntPtr
参数,我建议您使用 IntPtr
作为 p/invoke 签名的返回类型。
这一切都假定从您的 native 函数返回的内存已在 native DLL 中分配和释放。如果它是堆分配的,并且您希望调用者解除分配它,那么您就会遇到一个小问题。由于您无权访问 native DLL 的堆,您如何从 C# 释放内存?
简单的解决方案是使用共享 COM 堆来分配内存。调用 CoTaskMemAlloc
为字符串分配缓冲区。然后将返回值声明为 string
类型,p/invoke 编码器将使用 COM 分配器解除分配。
[DllImport("BSL430.dll", CallingConvention=CallingConvention.Cdecl)]
public extern static string Version_String();
...
string version = Version_String();
当然,这仅在您返回堆分配的内存且您希望调用者解除分配时适用。
关于c# - 在 C# 中编码一个 unsigned char * 从 dll 返回的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8925815/