【问题标题】:Which way of marshaling is correct for this C code?哪种编组方式对于这个 C 代码是正确的?
【发布时间】:2022-01-10 00:58:12
【问题描述】:

我有两种将 C 代码编组为 C# 的不同实现。

首先是C代码:

int GetVersion(char * pVer, size_t * piSize);

我在 C# 中的第一次尝试(我没有显示 DllImport 部分):

static extern int GetVersion( byte[] ver, ref UIntPtr size );

以及 C# 中的另一个尝试:

static extern int GetVersion([MarshalAs(UnmanagedType.LPStr)] StringBuilder ver, ref ulong size);

我从网上的不同例子中得到了这些。但我不能断定哪一个是正确的。

【问题讨论】:

  • 您做了什么来尝试并确定哪个(如果有的话)是“正确的”?
  • 我找到的例子里有这些方式。什么意思?
  • StringBuilder 风格更实用,您不需要 Encoding.GetString()。不要忘记在打电话之前设置它的容量,瞄准高。使用 int 作为第二个参数是实用的。
  • @HansPassant:你怎么知道最后有一个 NUL?越想越觉得没有。
  • 因为这是一个实用程序员会做的事情。

标签: c# c marshalling


【解决方案1】:

裸露的骨头:我们总是可以做到这一点

static extern int GetVersion(IntPtr ver, ref UIntPtr size );

我永远不会写这个,因为我不喜欢它为 A->W 转换生成的内容:

static extern int GetVersion([MarshalAs(UnmanagedType.LPStr)] StringBuilder ver, ref UIntPtr size);

此外,如果这是一个真正的计数字符串,则 P/Invoke 代码不起作用,因为它希望结尾后为 0。

我通常会写

static extern int GetVersion( byte[] ver, ref UIntPtr size );

您必须首先在其中创建最大大小的ver。然后,您可以使用System.Text.Encoding 中的内容将ver 转换为字符串。 GetVersion() 几乎肯定会传回一个常量,因此您可以硬编码正确的编码(无论如何可能是 ASCII)。

您很可能错过了[DllImport(..., CallingConvention=CallingConvention.Cdecl)],从而导致您出现一些内存损坏,但我实际上无法判断。

错误:不要将ulong 用于size_tsize_t 在所有支持的平台上都是 UIntPtr


评论暗示了一个完全不同的签名GetDevInfo(int Index, DevInfo * pdevInfo);这是完全不同的事情,显然有一个最佳选择:

const int IDLength = 100;
[StructLayout(LayoutKind.Sequential)]
struct DeviceInfo {
   [MarshalAs(UnmanagedType.ByValArray, SizeConst = IDLength)]
   byte[] ID;
}

static extern /* I don't know what's here because missing from comment */
    GetDevInfo(int index, ref strcut DeviceInfo info);

string GetInfo(int index)
{
    var info = new DeviceInfo();
    info.ID = new byte[IDLength];
    GetDevInfo(info.ID);
    return System.Text.Encoding.ASCII.GetString(info.ID, 0, strlen(info.ID));
}

int strlen(byte[] bytes)
{
    int i;
    for (i = 0; i < bytes.Length && bytes[i] != 0; i++)
        ;
    return i;
}
````

【讨论】:

  • 我明白了,非常好的答案。顺便说一句,我有时会看到 [Out] 而不是 ref。例如,在 DevInfo 是结构的 C 端,有一个带参数的函数:myFunction(DevInfo * devInfo)。现在在 C# 中,我看到它有时被翻译为“ref DevInfo devInfo”,有些写为“[Out] DevInfo devInfo”。你见过吗?
  • @GNZ:我有。 GetVersion 是读取参数还是只写入参数?如果读取它是ref,如果它写入它是out。像这样的 API 可能会将最大值作为初始值 size
  • 函数为:GetDevInfo(int Index, DevInfo * pdevInfo);在标题中它说这个函数检索设备信息。估计到时候就出来了。对吗?
  • @GNZ: GetDevInfo 不接受指向字符串的指针。答案将完全不同。
  • \in header param DeviceInfo [out] 设备信息结构
猜你喜欢
  • 1970-01-01
  • 2013-01-24
  • 2022-11-17
  • 2021-12-02
  • 2020-11-27
  • 2013-02-14
  • 1970-01-01
  • 2015-04-24
  • 1970-01-01
相关资源
最近更新 更多