我试图在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.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;
}

08-24 18:26