我有一个访问专有数据库的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
}
答案 0 :(得分:1)
抱歉,我没有足够的代表发表评论。
可是:
你在C中初始化这个结构吗?因为你的字符串只是指针,检查你的C代码是否为它想要填写的字符串分配内存。也许你只是在C中检查它们不是NULL并且试图找到字符串的结尾...如果是这样的话,在将结构传递给C代码之前初始化结构。
解决这种互操作问题的有效方法是编写一个非常简单的C程序或dll,它打印您传递的结构的每个字段。当你弄清楚结构是如何到达C时,你可以用真实的DLL替换它。
要尝试的另一件事是在C中获取字符串的sizeof并与C#上报告的sizeof进行比较。即使是一个字节的偏移也会导致许多问题。编写一个健全功能并将其导出到DLL上:
int sanity()
{
return sizeof(IIDX);
}
然后,您可以在C#中进行健全性检查,使用在C#上计算的结构大小测试健全性返回的值。对齐问题可能很难看到,如果将来结构大小发生变化,您可以发出警告信息。
此外,如果字符串是在C中分配的,请考虑以后如何释放这些字符串。
答案 1 :(得分:0)
您错误地声明了成员iidx.seg和ifil.ix 1 。您已将它们声明为byval或静态数组。因为你还没有初始化SizeConst,我认为运行时将它们编组为单元素数组。
这意味着C#运行时认为你有一个6字节宽的字段用于iidx.seg(一个iseg的大小),以及18或22字节宽(取决于平台)ifil.ix.但是C结构中两个字段的大小都是指针的大小(4或8个字节,具体取决于平台)。
顺便说一句,您不需要使用unsafe
关键字。使用指针fixed
和sizeof
时,您只需要这样做。一般来说,封送使您不必使用不安全的代码。
1 您是否考虑过使用与实际词语更相似的名称?