首页 文章

Interop C#到C struct

提问于
浏览
3

我试图在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 回答

  • 2

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

    • 您的C编译为16字节对齐

    • infoA的类型在C和C#之间是不同的,或者类型的大小不同

    可能的方案包括:

    [FieldOffset(24)] public IntPtr infoB;
    

    要么

    IntPtr.Size 与C上的 sizeof(infoB) 进行比较


    Edit :似乎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;
    }
    
  • 2

    你假设RETURNBUFFER包含结构指针必须是错误的 . 这是infoA和infoB占用16个字节的唯一方法 . INFO结构肯定是16个字节长,我看不到infoB的类型 . 从而:

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct RETURNBUFFER
        public IntPtr records;
        public INFO infoA;
        public DUNNO infoB; 
        public int number;
    }
    

    如果仍有问题,请使用C声明更新您的问题 . 应该很容易从中看到它们 .

  • 0

    尝试确保win32结构和c#结构是位(位大小)映射 . 这可以通过对win32类型使用精确的c#类型来实现 .

相关问题