我正在尝试使用 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));
这是正确的方法吗?
非常感谢
最佳答案
这可能是因为以下任何一种情况:
- 你的 C++ 编译为 16 字节对齐
- 您的 infoA 类型在 C++ 和 C# 中不同,或者类型的大小不同
可能的解决方案包括:
[FieldOffset(24)] public IntPtr infoB;
或
在 C++ 上比较 IntPtr.Size
和 sizeof(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/