编组从C#到C ++ DLL的结构

时间:2012-09-17 12:57:17

标签: c# c interop

我有一个访问专有数据库的C DLL。我想从C#应用程序访问它,该应用程序将用于将数据转换为SQL数据库。

我目前陷入困境,从C#编组一个特别复杂的结构。

我的C结构定义如下

typedef struct iseg {
    short   soffset;        /* segment offset   */
    short   slength;        /* segment length   */
    short   segmode;        /* segment mode     */
} ISEG, *LPISEG;


typedef struct iidx {
    short     ikeylen;        /* key length     */
    short     ikeytyp;        /* key type       */
    short     ikeydup;        /* duplicate flag */
    short     inumseg;        /* number of segments */
    LPISEG    seg;            /* segment information    */
    char      *ridxnam;       /* r-tree symbolic name   */
} IIDX, *LPIIDX;


typedef struct ifil {
    char        *pfilnam;       /* file name (w/o ext)  */
    char        *pfildes;       /* file description     */
    unsigned short  dreclen;        /* data record length   */
    unsigned short  dxtdsiz;        /* data file ext size   */
    short       dfilmod;        /* data file mode   */
    short       dnumidx;        /* number of indices    */
    unsigned short  ixtdsiz;        /* index file ext size  */
    short       ifilmod;        /* index file mode  */
    LPIIDX      ix;             /* index information    */
    unsigned short  rfstfld;        /* r-tree 1st fld name  */
    unsigned short  rlstfld;        /* r-tree last fld name */
    int     tfilno;         /* temporary file number*/
    char        datetime;       /* Update Date & Time Fields */ 
} IFIL, *LPIFIL;

我尝试了一堆不同的变种,但这就是我的C#结构的样子

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, Pack=1)]
public unsafe struct iseg
{
    public short soffset;
    public short slength;
    public short segmode;
}

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, Pack=1)]
public unsafe struct iidx
{
    public short ikeylen;
    public short ikeytyp;
    public short ikeydup;
    public short inumseg;

    [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValArray, ArraySubType = System.Runtime.InteropServices.UnmanagedType.Struct)]
    public iseg[] seg;

    [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)]
    public string ridxnam;
}

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, Pack=1)]
public unsafe struct ifil
{
    [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)]
    public string pfilnam;

    [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)]
    public string pfildes;

    public ushort dreclen;
    public ushort dxtdsiz;
    public short dfilmod;
    public short dnumidx;
    public ushort ixtdsiz;
    public short ifilmod;

    [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValArray, ArraySubType = System.Runtime.InteropServices.UnmanagedType.Struct)]
    public iidx[] ix;
    public ushort rfstfld;
    public ushort rlstfld;
    public int tfilno;
    public byte datetime;
}

我收到以下异常

Conversion.dll中出现'System.AccessViolationException'类型的第一次机会异常 Conversion.dll

中发生了未处理的“System.AccessViolationException”类型异常

附加信息:尝试读取或写入受保护的内存。这通常表明其他内存已损坏。

即使我在项目中选择了Debug Unmanaged Code选项,我也无法调试C DLL。也许问题出现在编组代码中?

我对c代码的调用是

public class NativeMethods
{
    /// Return Type: int
    ///lpIfil: LPIFIL->IFIL*
    [System.Runtime.InteropServices.DllImportAttribute(@"c:\db\debug\db.dll", EntryPoint = "OPNIFIL")]
    public static extern int OPNIFIL(ref ifil lpIfil);
}

if (NativeMethods.OPNIFIL(ref ifil) == 0)
{
    // No error occured
}

2 个答案:

答案 0 :(得分:1)

抱歉,我没有足够的代表发表评论。

可是:

你在C中初始化这个结构吗?因为你的字符串只是指针,检查你的C代码是否为它想要填写的字符串分配内存。也许你只是在C中检查它们不是NULL并且试图找到字符串的结尾...如果是这样的话,在将结构传递给C代码之前初始化结构。

解决这种互操作问题的有效方法是编写一个非常简单的C程序或dll,它打印您传递的结构的每个字段。当你弄清楚结构是如何到达C时,你可以用真实的DLL替换它。

要尝试的另一件事是在C中获取字符串的sizeof并与C#上报告的sizeof进行比较。即使是一个字节的偏移也会导致许多问题。编写一个健全功能并将其导出到DLL上:

int sanity()
{
    return sizeof(IIDX);
}

然后,您可以在C#中进行健全性检查,使用在C#上计算的结构大小测试健全性返回的值。对齐问题可能很难看到,如果将来结构大小发生变化,您可以发出警告信息。

此外,如果字符串是在C中分配的,请考虑以后如何释放这些字符串。

参考: http://msdn.microsoft.com/en-us/library/s9ts558h(v=vs.100).aspx#cpcondefaultmarshalingforstringsanchor2

答案 1 :(得分:0)

您错误地声明了成员iidx.seg和ifil.ix 1 。您已将它们声明为byval或静态数组。因为你还没有初始化SizeConst,我认为运行时将它们编组为单元素数组。

这意味着C#运行时认为你有一个6字节宽的字段用于iidx.seg(一个iseg的大小),以及18或22字节宽(取决于平台)ifil.ix.但是C结构中两个字段的大小都是指针的大小(4或8个字节,具体取决于平台)。

顺便说一句,您不需要使用unsafe关键字。使用指针fixedsizeof时,您只需要这样做。一般来说,封送使您不必使用不安全的代码。

1 您是否考虑过使用与实际词语更相似的名称?