我试图在C#中使用互操作调用一些旧式C代码。
我不太了解interop在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;
}
记录实际上是指向这样的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(文本,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 ++中的每个struct / class / object
我必须在C#中创建等效的IntPtr
最后一个问题,虽然我在这里,所以我不必在一个新问题中重新定义所有结构,
用于结构INFO中的IntPtr。
在C ++中,它的类型为HANDLE
我在这里将其定义为IntPtr是否正确?它只是一个句柄,而不是*句柄思想,还是应该让它成为一个uint值?
这是我从msdn网站上读到的内容:“请记住,任何返回或接受句柄的API函数实际上都在使用不透明指针。您的代码应将Windows中的句柄编组为System.IntPtr值”
如果我将其定义为IntPtr,
我应该如何为其分配内存?以下内容正确吗?
returnBuffer.infoA.doc = Marshal.AllocCoTaskMem(System.IntPtr.Size);
解组
元帅.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;
}