我试图在C#中使用interop调用一些遗留的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;
}
记录实际上是指向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都与文本相同 .
================================================== ==第二次编辑
事实上,c结构
typedef struct RETURNBUFFER
{
RECORD *precord;
INFO infoA;
INFO infoB;
UINT number;
} CRB;
谢谢你的回复,这确实是我的问题 .
在某种程度上我的印象是,对于C中的每个结构/类/对象,我必须在C#中创建一个等效的IntPtr
最后一个问题,虽然我在这里,所以我不必在一个新问题中重新定义所有结构,
对于struct INFO中的IntPtr . 在C中,它是HANDLE类型
我在这里是否正确将其定义为IntPtr?它只是一个句柄,但它不是一个*句柄思想,或者我应该让它成为一个uint值?
这是我从msdn网站上读到的内容“请记住,任何返回或接受句柄的API函数都使用不透明的指针 . 您的代码应该将Windows中的句柄编组为System.IntPtr值”
如果我将其定义为IntPtr,
我应该如何为它分配内存?以下是正确的吗?
returnBuffer.infoA.doc = Marshal.AllocCoTaskMem(System.IntPtr.Size);
要解散
Marshal.PtrToStructure(returnBuffer.infoA.doc,typeof(IntPtr));
这是正确的方法吗?
非常感谢
3 回答
可能是因为以下任何一种情况:
您的C编译为16字节对齐
infoA的类型在C和C#之间是不同的,或者类型的大小不同
可能的方案包括:
要么
将
IntPtr.Size
与C上的sizeof(infoB)
进行比较Edit :似乎infoA是16个字节,但您的INFO声明不是16个字节 . 您的C#和C声明很可能是不同的 . 如果您可以在问题中包含C声明,那将是件好事 .
与此同时,我只能猜测最佳匹配:
你假设RETURNBUFFER包含结构指针必须是错误的 . 这是infoA和infoB占用16个字节的唯一方法 . INFO结构肯定是16个字节长,我看不到infoB的类型 . 从而:
如果仍有问题,请使用C声明更新您的问题 . 应该很容易从中看到它们 .
尝试确保win32结构和c#结构是位(位大小)映射 . 这可以通过对win32类型使用精确的c#类型来实现 .