c# - 将结构从 C# 编码到 C++ DLL

标签 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”类型的未处理异常

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

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

最佳答案

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

但是:

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

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

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

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

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

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

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

关于c# - 将结构从 C# 编码到 C++ DLL,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12459691/

相关文章:

c# - 为什么不在构造函数中调用可覆盖的方法?

c - 如何解析两个同名的结构体?

java - Clojure 的自定义访问器策略 `from-java`

.net - 如何调试具有使用app.config文件的.net互操作项目的VB6项目?

c# - 使用 MVC 3 以正确的方式在数据库中存储和检索文件

c# - 在SQL中选择TOP行,得到5行尾表

c++ - 如何在数组上执行环绕遍历?

c++ - 动态改变开关盒的尺寸

c# - 正确使用 P/Invoke

c# - System.Action<T> 作为 EventHandler