首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >带有联合和数组的C# pinvoke结构

带有联合和数组的C# pinvoke结构
EN

Stack Overflow用户
提问于 2013-11-28 20:28:27
回答 2查看 1.5K关注 0票数 3

我未能定义正确的C#代码来处理包含联合和数组的复杂结构的C++库,在执行C++代码时,我一直得到一些内存异常,而且我很确定这是由于这个原因。

c++结构如下所示,忽略枚举和其他结构定义(如果需要的话,我会发布它们,但它们是相当扩展的)

代码语言:javascript
复制
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;

C#代码是:

代码语言:javascript
复制
[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;
    }

委托和函数导入

代码语言:javascript
复制
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);

更新:在asnwer之后的新代码,仍然无法工作:错误是:“试图读取或写入受保护的内存--这通常是指示其他内存已损坏的”。

奇怪的是,我可以在第一个事件调用中使用结构,打印所有字段,它们似乎都是正确的(lData和iData有相同的值,cData有我发送的字符串),但是在事件结束后程序就会死。好像我的C#代码破坏了结构什么的..。

代码语言:javascript
复制
[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++声明:

代码语言:javascript
复制
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);

注意: dad DGCCTALK_PREFIX被定义为__declspec(dllexport)

以下代码的C#代码:

代码语言:javascript
复制
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#代码

代码语言:javascript
复制
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#处理程序头

代码语言:javascript
复制
private void _handler(ref DG_CCTALK_APP_EVT pEVT)
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-11-29 09:30:20

正如您已经发现的,封送员不喜欢您在联合翻译中尝试将数组与其他字段重叠。您已经通过将数组的偏移量更改为4而不是0来解决这个问题。但这没什么用。

现在,由于编组员不会为您处理工会,所以您必须手动处理。我会通过省略工会的非数组成员来处理这件事。

代码语言:javascript
复制
[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;
}

请注意,我已经删除了您声明的大部分详细内容。我怀疑该类型是由工具自动生成的。但所有这些细节都让人很难读懂。

这将为结构生成正确的布局,但将留给您访问联合中的其他字段的工作。

那么,我们怎么读那些字段呢?好的,数据包含在字节数组uData中,所以它只是从那里读取值的问题。例如,可以将以下属性添加到DG_CCTALK_APP_EVT结构中:

代码语言:javascript
复制
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.

注意,setter将删除数组的所有(但前4个字节),因为它覆盖了uData。我对本机代码的协议不太了解,无法确定这是您想要的。如果这不是您想要的,那么您可以这样写setter:

代码语言:javascript
复制
Array.Copy(BitConverter.GetBytes(value), uData, sizeof(int));

现在您已经添加了interop边界的C++侧。最明显的问题是您的C#委托似乎有错误的调用约定。它使用stdcall,但C++代码需要cdecl。这当然足以解释回调返回后的灾难性崩溃。您需要确保委托指定调用约定。

代码语言:javascript
复制
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DG_CCTALK_APP_EVT_HANDLER(ref DG_CCTALK_APP_EVT pEVT);

你对cctalk_app_add_link的翻译也是错误的。它应该是:

代码语言:javascript
复制
[DllImport("cctalk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int cctalk_app_add_link(string szPortName);

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

票数 3
EN

Stack Overflow用户

发布于 2013-11-28 20:45:26

检查您的int和长字段。我认为that是16位,而C# int是32位:

  • 如果是这样的话,那么iTime、iHandle和iBilAmount在C#和C定义中都定义为int。
  • --如果我错了,那么lData在C中定义为长,而在C#中定义为int (如果这是问题所在,那么不要忘记修复在lData后面出现的字段的偏移量)。
票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20274284

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档