c# - 互操作 C# 到 C++ 结构

标签 c# interop struct

我正在尝试使用 C# 中的互操作调用一些遗留 C 代码。 我不太熟悉互操作在 C# 上的工作方式,但必须处理一些令人困惑的结构。 我得到了它的一部分工作,但是当我尝试将结构放入 C 层时地址弄乱了。

我正在尝试将一个结构传递给 C 代码,它会对它做一些事情,我需要得到一个结果

我在 C# 中有这些结构

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct RETURNBUFFER
    public IntPtr records; //this is the struct RECORD
    public IntPtr infoA; // this is the struct INFO
    public IntPtr infoB; 
    public int number;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct INFO
{
    public IntPtr doc; //this is a handle in C code
    public int cpFirst; 
    public int cpLim; 
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct RECORD
{
    public int size;
}

Records实际上是一个指针,指向另一个用C#定义的Struct STATS,

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct STATS 
{
    public int size;
    public int a;
    public int b;
    public int c;
    public int d;
    public int e;
    public int f;
    public int g;
}

在 C# 层,我创建如下结构

        RETURNBUFFER returnBuffer = new RETURNBUFFER();
        returnBuffer.infoA = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(INFO)));
        returnBuffer.infoB = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(INFO)));
        returnBuffer.records = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(STATS)));

当我运行我的代码时,我只能检索到 returnBuffer 中的第一项,即 returnBuffer.records, 包括 returnBuffer 中的 int 值在内的所有其他项目都搞砸了。

我尝试调试它并查看地址值, 我发现当来自 C# -> C 的代码代码发生偏移时

我不知道为什么地址是关闭的,

下面是一个在64位环境下发生的例子

C#
&ReturnBuffer
0x00000000046f05f8
&ReturnBuffer.records
0x00000000046f05f8
&ReturnBuffer.infoA
0x00000000046f0600
&ReturnBuffer.infoB
0x00000000046f0608
&ReturnBuffer.number
0x00000000046f0610

在 C 中,假设我正在调用的函数采用参数 RETURNBUFFER *pReturnBuffer,

我得到这些地址,

pReturnBuffer
0x00000000046F05F8 
&pReturnBuffer->records
0x00000000046F05F8 
&pReturnBuffer->infoA
0x00000000046F0600
&pReturnBuffer->infoB
0x00000000046F0610    **This address is off by 8**
&pReturnBuffer->number
0x00000000046F0620 **this is off by 16**

因此, 当代码移回 C# 函数时,

我可以正确构造 returnBuffer.records, 但无法构建 infoA 和 infoB,也无法获得 returnBuffer.number 的正确值

不确定我在这里遗漏了什么。

=============================================

我在 Fun Mun Pieng 的帮助下编辑了我的代码

[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
public struct CRB
{
    [FieldOffset(0)]
    public IntPtr pcstat;//CSTAT
    [FieldOffset(8)]
    public IntPtr caProc;//NLCA
    [FieldOffset(24)] 
    public IntPtr caSent;//NLCA
    [FieldOffset(40)]
    public int cnlch;
}

现在,本地址转到 C# -> C++ -> C# 时,地址会匹配 但是我仍然得到一些垃圾数据。

我做了一些调查,这是我发现的不稳定行为。

在 C# 代码中我这样调用

IntPtr text = Marshal.StringToCoTaskMemUni("我在这里");

legacyFunction(text, ref returnBuffer)

在这里,当我调用 GetHashCode 函数时,我得到以下值

returnBuffer.records.GetHashCode()  473881344
returnBuffer.infoA.GetHashCode()  473898944
returnBuffer.infoB.GetHashCode()  473898784

text.GetHashCode() 468770816

从函数返回后,这些哈希值会发生变化,

returnBuffer.records.GetHashCode()  543431240
returnBuffer.infoA.GetHashCode()  473799988
returnBuffer.infoB.GetHashCode()  473799988

text.GetHashCode() 473799988

现在,我真的可以做到, Marshal.PtrToStringUni(checkReturnBuffer.infoA) 我得到“我在这里”

C# 现在认为,infoA 和 infoB 都与 text 相同。

============================================= ===== 第二次编辑

c++结构其实就是

typedef struct RETURNBUFFER
{
    RECORD *precord;
    INFO infoA;      
    INFO    infoB;       
    UINT    number;
 } CRB;

谢谢回复,确实是我的问题。

不知何故,我的印象是, 对于 C++ 中的每个结构/类/对象 我必须在 C# 中创建等效的 IntPtr

最后一个问题,因为我在这里,所以我不必在新问题中重新定义所有结构,

对于 struct INFO 中的 IntPtr。 在 C++ 中,它是 HANDLE 类型

我在这里将它定义为 IntPtr 是否正确?它只是一个句柄,但它不是 *handle 思想,还是我应该让它成为一个 uint 值?

这是我从 msdn 网站上读到的内容“记住,任何返回或接受句柄的 API 函数实际上都是在使用不透明指针。您的代码应该在 Windows 中将句柄编码为 System.IntPtr 值”

如果我将它定义为 IntPtr,

我应该如何为它分配内存?以下是否正确?

returnBuffer.infoA.doc = Marshal.AllocCoTaskMem(System.IntPtr.Size);

解码

Marshal.PtrToStructure(returnBuffer.infoA.doc, typeof(IntPtr));

这是正确的方法吗?

非常感谢

最佳答案

这可能是因为以下任何一种情况:

  1. 你的 C++ 编译为 16 字节对齐
  2. 您的 infoA 类型在 C++ 和 C# 中不同,或者类型的大小不同

可能的解决方案包括:

[FieldOffset(24)] public IntPtr infoB;

在 C++ 上比较 IntPtr.Sizesizeof(infoB)


编辑: infoA 似乎是 16 字节,但您的 INFO 声明不是 16 字节。您的 C# 和 C++ 声明很可能不同。如果您可以在问题中包含您的 C++ 声明,那就太好了。

与此同时,我只能猜测最佳匹配是:

public struct RETURNBUFFER
{
    public RECORD records; //this is the struct RECORD
    public INFO infoA; // this is the struct INFO
    public INFO infoB; 
    public int number;
}

关于c# - 互操作 C# 到 C++ 结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5413774/

相关文章:

c# - 使用 C# 的更大文件流

c# - 以互操作 C++ 的数组形式返回数据的更快方法

c# - C#中的值类型类定义?

c# - 为什么我的文件从 Angular 上传到 .NET core Web api 不起作用?

c# - 如何使用基类设置控件和 WinForm 样式?

c# - 更改目标框架后 Windows 服务不会启动

c - Swift:UnsafeMutablePointer.deallocate(capacity:) 与 free() 的互操作性

c# - 查找具有常量的所有单元格会使 Excel 崩溃

c - 每个结构有不同类型的原因可以解释吗?

C# 从 byte[] 转换为 struct。字节顺序错误