c# - 在 C# 中编码一个 unsigned char * 从 dll 返回的函数

标签 c# pinvoke return marshalling

我在 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/

相关文章:

c# - 通过引用将 char** 从 C# 传递到非托管 C++

javascript - Gulp sass (libsass) 使用 return 时速度极慢

Python 函数未以预期格式返回日期

python:返回 bool 值和字符串在一起的错误

c# - 在 C# 中快速读取的集合

c# - double float 和 float 精度之间的差异

c# - DomElement.click() 事件在 chrome 中不起作用,但其他事件可以

c++ - 分析动态 pinvoke

c# - Base64UrlEncode 的使用语句

c# - 将 intptr 转换为 ulong 数组