2

我有一个访问专有数据库的 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”类型的未处理异常

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

我无法调试 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
}
4

2 回答 2

1

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

但:

你用C初始化这个结构吗?由于您的字符串只是指针,请检查您的 C 代码是否正在为要填充的字符串分配内存。也许您只是在 C 中检查它们不是 NULL 并试图找到字符串的结尾......如果是这样,在将结构传递给 C 代码之前对其进行初始化。

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

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

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

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

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

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

于 2012-09-18T12:50:41.703 回答
0

You've incorrectly declared the members iidx.seg and ifil.ix1. You've declared them both as byval, or static, arrays. Because you haven't initialized SizeConst, I think the runtime marshals them as single-element arrays.

This means that the C# runtime thinks that you have a field 6 bytes wide for iidx.seg (the size of one iseg), and 18 or 22 bytes wide (depending on platform) for ifil.ix. But the size of both fields in your C structures is the size of a pointer (4 or 8 bytes, depending on platform).

By the way, you don't need to use the unsafe keyword. You only need that when using pointers, fixed, and sizeof. Marshaling, in general, keeps you from having to use unsafe code.

1 Have you considered using names that bear a stronger resemblance to actual words?

于 2012-09-18T15:21:53.110 回答