C#的PInvoke结构与工会和数组 [英] C# pinvoke structs with union and arrays

查看:113
本文介绍了C#的PInvoke结构与工会和数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

IM不能确定正确的C#代码与C工作++库,它定义了与工会和数组一个复杂的结构,我不断收到一些内存异常在执行的C ++代码,和IM肯定它缘于此。



C ++的结构如下所示,忽略枚举和其他结构的定义(我将发布他们,如果需要的,但他们很可拓)

  typedef结构
{
DG_CCTALK_APP_EVT_CODE eEventCode;
DG_CCTALK_APP_EVT_TYPE eEventType;
INT ITIME;
INT iHandle;
unsigned char型uchAddress;
DG_CCTALK_BILL_INFO sBillInfo;
INT iBillAmount;
工会
{
长LDATA;
INT IDATA;
unsigned char型ucArray [128];
的char * CString的;
无效* PVOID;
} UDATA;
无效* _private; //由cctalk应用层
} DG_CCTALK_APP_EVT使用,* PDG_CCTALK_APP_EVT;

和C#代码是这样的:

  [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Explicit)
公共结构Anonymous_92d21a81_2a8b_42f4_af32_f7606aa9cf40
{
// deberian llevar待办事项0,佩罗EL阵列属problemas人ponerle 0,Y cambiandolo explota恩OTRO LADO ...

/// INT
[System.Runtime.InteropServices.FieldOffsetAttribute(0)
酒店的公共INT LDATA;

/// INT
[System.Runtime.InteropServices.FieldOffsetAttribute(0)]
公众诠释IDATA;

/// unsigned char型[128]
[System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValArray,SizeConst = 128,ArraySubType = System.Runtime.InteropServices .UnmanagedType.I1)]
[System.Runtime.InteropServices.FieldOffsetAttribute(4)]
公共字节[] ucArray;

///的char *
[System.Runtime.InteropServices.FieldOffsetAttribute(0)]
公共System.IntPtr CString的;

///无效*
[System.Runtime.InteropServices.FieldOffsetAttribute(0)]
公共System.IntPtr PVOID;
}

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)
公共结构DG_CCTALK_APP_EVT
{

/// DG_CCTALK_APP_EVT_CODE-> _DG_CCTALK_APP_EVT_CODE
公共DG_CCTALK_APP_EVT_CODE eEventCode;

/// DG_CCTALK_APP_EVT_TYPE-> _DG_CCTALK_APP_EVT_TYPE
公共DG_CCTALK_APP_EVT_TYPE eEventType;

/// INT
公众诠释ITIME;

/// INT
公众诠释iHandle;

/// unsigned char型
公共字节uchAddress;

/// DG_CCTALK_BILL_INFO-> _DG_CCTALK_BILL_INFO
公共DG_CCTALK_BILL_INFO sBillInfo;

/// INT
公众诠释iBillAmount;

/// Anonymous_92d21a81_2a8b_42f4_af32_f7606aa9cf40
公共Anonymous_92d21a81_2a8b_42f4_af32_f7606aa9cf40 UDATA;

///无效*
公共System.IntPtr _private;
}



代表和函数导入

 公共委托无效DG_CCTALK_APP_EVT_HANDLER(参考DG_CCTALK_APP_EVT pEVT); 

[System.Runtime.InteropServices.DllImportAttribute(cctalk.dll,入口点=cctalk_app_enable_device,CallingConvention = CallingConvention.Cdecl)]
公共静态外部INT cctalk_app_enable_device(REF DG_CCTALK_APP_EVT参数0) ;



更新:一个asnwer新规则后,仍然没有工作:
的错误是:尝试读取或写入受保护的内存这通常是一个表明其他内存已损坏



什么是奇怪的是,我可以使用结构上的第一个事件调用,我打印的各个领域,他们似乎是正确的(LDATA和IDATA有相同的价值观,CDATA有一个字符串我送),但在事件发生后结束程序死亡。好像我的C#代码更是打破了结构或东西...

  [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime .InteropServices.LayoutKind.Sequential,字符集= System.Runtime.InteropServices.CharSet.Ansi)] 
公共结构DG_CCTALK_APP_EVT
{

/// DG_CCTALK_APP_EVT_CODE-> _DG_CCTALK_APP_EVT_CODE
公共DG_CCTALK_APP_EVT_CODE eEventCode;

/// DG_CCTALK_APP_EVT_TYPE-> _DG_CCTALK_APP_EVT_TYPE
公共DG_CCTALK_APP_EVT_TYPE eEventType;

/// INT
公众诠释ITIME;

/// INT
公众诠释iHandle;

/// unsigned char型
公共字节uchAddress;

/// DG_CCTALK_BILL_INFO-> _DG_CCTALK_BILL_INFO
公共DG_CCTALK_BILL_INFO sBillInfo;

/// INT
公众诠释iBillAmount;

[的MarshalAs(UnmanagedType.ByValArray,SizeConst = 128)]
公共字节[] UDATA;

#地区制定者Ÿ干将对mapeo

公众诠释LDATA
{

{
Array.Copy(BitConverter .GetBytes(值),UDATA,sizeof的(INT));
}
得到
{
返回BitConverter.ToInt32(UDATA,0);
}
}

公众诠释IDATA
{

{
Array.Copy(BitConverter.GetBytes(值) ,UDATA,sizeof的(INT));
}
得到
{
返回BitConverter.ToInt32(UDATA,0);
}
}

公共字节[] ucArray
{

{
Array.Copy(价值,UDATA, 128);
}
得到
{
返回UDATA;

}
}

公众的IntPtr CSTRING
{

{
Array.Copy(BitConverter。 GetBytes会(value.ToInt32()),UDATA,32);
}
得到
{
回报(IntPtr的)BitConverter.ToInt32(UDATA,0);
}
}

公众的IntPtr PVOID
{

{
Array.Copy(BitConverter.GetBytes(价值。 ToInt32()),UDATA,32);
}
得到
{
回报(IntPtr的)BitConverter.ToInt32(UDATA,0);
}
}


#endregion

///无效*
公共System.IntPtr _private;
}



C ++声明:

 无效的typedef(* DG_CCTALK_APP_EVT_HANDLER)(PDG_CCTALK_APP_EVT pEVT); 

DGCCTALK_PREFIX DG_ERROR cctalk_app_init(DG_CCTALK_APP_EVT_HANDLER pfnHandler);
DGCCTALK_PREFIX DG_ERROR cctalk_app_add_link(字符* szPortName);

请注意爸DGCCTALK_PREFIX被定义为__declspec(dllexport)的



对于那些C#代码:

 公共委托无效DG_CCTALK_APP_EVT_HANDLER(参考DG_CCTALK_APP_EVT pEVT); 

[System.Runtime.InteropServices.DllImportAttribute(cctalk.dll,入口点=cctalk_app_init,CallingConvention = CallingConvention.Cdecl)]
公共静态外部INT cctalk_app_init(DG_CCTALK_APP_EVT_HANDLER pfnHandler);

[System.Runtime.InteropServices.DllImportAttribute(cctalk.dll,入口点=cctalk_app_add_link,CallingConvention = CallingConvention.Cdecl)]
公共静态外部INT cctalk_app_add_link(System.IntPtr szPortName );



C#代码调用它们。



  VAR处理器=新DG_CCTALK_APP_EVT_HANDLER(this._handler); 
VAR resultado = cctalk_app_init(处理);
cctalk_app_add_link(Marshal.StringToHGlobalAnsi(端口=+端口));



C#处理头

 私人无效_handler(参考DG_CCTALK_APP_EVT pEVT)


解决方案

正如你所发现的,该封送不喜欢你试图覆盖与其他领域的数组中的工会翻译。你通过改变偏移量数组是4,而不是0抑制问题,不过这并不能帮助。



现在,因为封送不会对你的工会打交道,你必须做手工。我想通过省略工会的非数组成员处理这个问题。

  [StructLayout(LayoutKind.Sequential)] 
酒店的公共结构DG_CCTALK_APP_EVT
{
公共DG_CCTALK_APP_EVT_CODE eEventCode;
公共DG_CCTALK_APP_EVT_TYPE eEventType;
公众诠释ITIME;
公众诠释iHandle;
公共字节uchAddress;
公共DG_CCTALK_BILL_INFO sBillInfo;
公众诠释iBillAmount;
[的MarshalAs(UnmanagedType.ByValArray,SizeConst = 128)]
公共字节[] UDATA;
公众的IntPtr _private;
}

请注意,我已删除了很多你的宣言的详细程度。我怀疑类型已自动工具生成的。但所有的冗长使得它很难看。



这将产生对结构布局是否正确,但会离开你的工作要做来访问其它领域工会。



那么,怎样才能我们读这些领域?那么,数据包含在字节数组中 UDATA ,所以它只是一个从那里读值问题。例如,你可以在下面的属性添加到 DG_CCTALK_APP_EVT 结构:

 公众诠释LDATA 
{
{返回BitConverter.ToInt32(UDATA,0); }
集合{UDATA = BitConverter.GetBytes(值); }
}

公众诠释IDATA
{
{返回BitConverter.ToInt32(UDATA,0); }
集合{UDATA = BitConverter.GetBytes(值); }
}

//等



注意二传手将泯然众人了,但前4个字节数组,因为它会覆盖 UDATA 。我不知道有足够的了解本机代码的协议,以确保这是你想要的。如果这不是你想要的,那么你可能会这样写二传手:

  Array.Copy(BitConverter.GetBytes(值) ,UDATA,sizeof的(INT)); 






现在您已经添加的C ++的侧互操作边界。最明显的问题是,你的C#委托似乎有错误的调用约定。它采用 STDCALL 但是C ++代码期望 CDECL 。这肯定够了你的回调函数返回后解释了灾难性的崩溃。你需要确保你的委托指定调用约定。

  [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
酒店的公共委托无效DG_CCTALK_APP_EVT_HANDLER(参考DG_CCTALK_APP_EVT pEVT);






您的翻译 cctalk_app_add_link 是错了。它应该是:



 函数[DllImport(cctalk.dll,CallingConvention = CallingConvention.Cdecl)] 
公共静态EXTERN INT cctalk_app_add_link(字符串szPortName);

完成这样你可以简单地传递一个字符串,从而避免当前的实现有内存泄漏。


im failing to define the correct C# code to work with a C++ library which defines a complex struct with union and arrays, i keep getting some memory exception while executing the C++ code, and im pretty sure its due to this.

The c++ struct is as follows, ignoring enums and other struct definition (i will post them if needed but they are pretty extense)

typedef struct
{
DG_CCTALK_APP_EVT_CODE  eEventCode;
DG_CCTALK_APP_EVT_TYPE  eEventType;
int         iTime;
int         iHandle;
unsigned char       uchAddress;
DG_CCTALK_BILL_INFO sBillInfo;
int         iBillAmount;
union
{
    long        lData;
    int     iData;
    unsigned char   ucArray[128];
    char        *cString;
    void        *pvoid;
} uData;
void            *_private;  // for use by cctalk app layer
} DG_CCTALK_APP_EVT, *PDG_CCTALK_APP_EVT;

And the C# code is this:

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Explicit)]
    public struct Anonymous_92d21a81_2a8b_42f4_af32_f7606aa9cf40
    {
        //deberian llevar todos 0, pero el array genera problemas al ponerle 0, y cambiandolo explota en otro lado...

        /// int
        [System.Runtime.InteropServices.FieldOffsetAttribute(0)]
        public int lData;

        /// int
        [System.Runtime.InteropServices.FieldOffsetAttribute(0)]
        public int iData;

        /// unsigned char[128]
        [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst = 128, ArraySubType = System.Runtime.InteropServices.UnmanagedType.I1)]
        [System.Runtime.InteropServices.FieldOffsetAttribute(4)]
        public byte[] ucArray;

        /// char*
        [System.Runtime.InteropServices.FieldOffsetAttribute(0)]
        public System.IntPtr cString;

        /// void*
        [System.Runtime.InteropServices.FieldOffsetAttribute(0)]
        public System.IntPtr pvoid;
    }

    [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
    public struct DG_CCTALK_APP_EVT
    {

        /// DG_CCTALK_APP_EVT_CODE->_DG_CCTALK_APP_EVT_CODE
        public DG_CCTALK_APP_EVT_CODE eEventCode;

        /// DG_CCTALK_APP_EVT_TYPE->_DG_CCTALK_APP_EVT_TYPE
        public DG_CCTALK_APP_EVT_TYPE eEventType;

        /// int
        public int iTime;

        /// int
        public int iHandle;

        /// unsigned char
        public byte uchAddress;

        /// DG_CCTALK_BILL_INFO->_DG_CCTALK_BILL_INFO
        public DG_CCTALK_BILL_INFO sBillInfo;

        /// int
        public int iBillAmount;

        /// Anonymous_92d21a81_2a8b_42f4_af32_f7606aa9cf40
        public Anonymous_92d21a81_2a8b_42f4_af32_f7606aa9cf40 uData;

        /// void*
        public System.IntPtr _private;
    }

Delegates and a function import

public delegate void DG_CCTALK_APP_EVT_HANDLER(ref DG_CCTALK_APP_EVT pEVT);

[System.Runtime.InteropServices.DllImportAttribute("cctalk.dll", EntryPoint = "cctalk_app_enable_device", CallingConvention = CallingConvention.Cdecl)]
public static extern int cctalk_app_enable_device(ref DG_CCTALK_APP_EVT param0);

UPDATE: new code after an asnwer, still not working: The error is: "Attempt to read or write Protected Memory This is often an indicating that other memory is corrupt"

What is weird, is that i can use the struct on the first event call, i print all fields and they seem to be right (lData and iData have the same values, cData has a string I sent) but after the event ends the program dies. Seems like my C# code is breaking the struct or something...

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet = System.Runtime.InteropServices.CharSet.Ansi)]
    public struct DG_CCTALK_APP_EVT
    {

        /// DG_CCTALK_APP_EVT_CODE->_DG_CCTALK_APP_EVT_CODE
        public DG_CCTALK_APP_EVT_CODE eEventCode;

        /// DG_CCTALK_APP_EVT_TYPE->_DG_CCTALK_APP_EVT_TYPE
        public DG_CCTALK_APP_EVT_TYPE eEventType;

        /// int
        public int iTime;

        /// int
        public int iHandle;

        /// unsigned char
        public byte uchAddress;

        /// DG_CCTALK_BILL_INFO->_DG_CCTALK_BILL_INFO
        public DG_CCTALK_BILL_INFO sBillInfo;

        /// int            
        public int iBillAmount;            

        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
        public byte[] uData;

        #region setters y getters para mapeo

        public int lData
        {
            set
            {
                Array.Copy(BitConverter.GetBytes(value), uData, sizeof(int));
            }
            get
            {
                return BitConverter.ToInt32(uData, 0);
            }
        }

        public int iData
        {
            set
            {
                Array.Copy(BitConverter.GetBytes(value), uData, sizeof(int));
            }
            get
            {
                return BitConverter.ToInt32(uData, 0);
            }
        }

        public byte[] ucArray
        {
            set
            {
                Array.Copy(value, uData, 128);
            }
            get
            {
                return uData;

            }
        }

        public IntPtr cString
        {
            set
            {
                Array.Copy(BitConverter.GetBytes(value.ToInt32()), uData, 32);
            }
            get
            {
                return (IntPtr)BitConverter.ToInt32(uData, 0);
            }
        }

        public IntPtr pvoid
        {
            set
            {
                Array.Copy(BitConverter.GetBytes(value.ToInt32()), uData, 32);
            }
            get
            {
                return (IntPtr)BitConverter.ToInt32(uData, 0);
            }
        }


        #endregion

        /// void*
        public System.IntPtr _private;
    }

C++ declarations:

typedef void (*DG_CCTALK_APP_EVT_HANDLER)(PDG_CCTALK_APP_EVT pEVT);

DGCCTALK_PREFIX DG_ERROR cctalk_app_init(DG_CCTALK_APP_EVT_HANDLER pfnHandler);
DGCCTALK_PREFIX DG_ERROR cctalk_app_add_link(char *szPortName);

Note dad DGCCTALK_PREFIX is defined as __declspec(dllexport)

C# code for those:

public delegate void DG_CCTALK_APP_EVT_HANDLER(ref DG_CCTALK_APP_EVT pEVT);

[System.Runtime.InteropServices.DllImportAttribute("cctalk.dll", EntryPoint =     "cctalk_app_init", CallingConvention = CallingConvention.Cdecl)]
 public static extern int cctalk_app_init(DG_CCTALK_APP_EVT_HANDLER pfnHandler);

[System.Runtime.InteropServices.DllImportAttribute("cctalk.dll", EntryPoint = "cctalk_app_add_link", CallingConvention = CallingConvention.Cdecl)]
public static extern int cctalk_app_add_link(System.IntPtr szPortName);

C# code invoking them

var handler = new DG_CCTALK_APP_EVT_HANDLER(this._handler);
var resultado = cctalk_app_init(handler);
cctalk_app_add_link(Marshal.StringToHGlobalAnsi("port=" + port));

C# handler header

private void _handler(ref DG_CCTALK_APP_EVT pEVT)

解决方案

As you have discovered, the marshaler does not like you trying to overlay the array with other fields in your translation of the union. You've suppressed the problem by changing the offset for the array to be 4 rather than 0. But that doesn't help.

Now, since the marshaler won't deal with the union for you, you'll have to do it manually. I would handle this by omitting the non-array members of the union.

[StructLayout(LayoutKind.Sequential)]
public struct DG_CCTALK_APP_EVT
{
    public DG_CCTALK_APP_EVT_CODE eEventCode;
    public DG_CCTALK_APP_EVT_TYPE eEventType;
    public int iTime;
    public int iHandle;
    public byte uchAddress;
    public DG_CCTALK_BILL_INFO sBillInfo;
    public int iBillAmount;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
    public byte[] uData;
    public IntPtr _private;
}

Note that I have removed much of the verbosity of your declaration. I suspect that the type was automatically generated by a tool. But all that verbosity makes it very hard to read.

This will produce the correct layout for the struct, but will leave you with work to do to access the other fields in the union.

So, how can we read those fields? Well, the data is contained in the byte array uData, so it is just a matter of reading the values from there. For instance, you could add the following property to the DG_CCTALK_APP_EVT struct:

public int lData
{
    get { return BitConverter.ToInt32(uData, 0); }
    set { uData = BitConverter.GetBytes(value); }
}

public int iData
{
    get { return BitConverter.ToInt32(uData, 0); }
    set { uData = BitConverter.GetBytes(value); }
}

// etc.

Note that the setter will obliterate all the but first 4 bytes of the array, since it overwrites uData. I don't know enough about the protocol of the native code to be sure that this is what you want. If it's not what you want, then you might write the setter like this:

Array.Copy(BitConverter.GetBytes(value), uData, sizeof(int));


You've now added the C++ side of the interop boundary. The most obvious problem is that your C# delegate appears to have the wrong calling convention. It uses stdcall but the C++ code expects cdecl. That's certainly enough to explain a catastrophic crash after your callback returns. You'll need to make sure that your delegate specifies the calling convention.

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DG_CCTALK_APP_EVT_HANDLER(ref DG_CCTALK_APP_EVT pEVT);


Your translation of cctalk_app_add_link is wrong too. It should be:

[DllImport("cctalk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int cctalk_app_add_link(string szPortName);

Done like this you can simply pass a string and thus avoid the memory leak that your current implementation has.

这篇关于C#的PInvoke结构与工会和数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆