2012-09-17 161 views
2

我有一個訪問專有數據庫的C DLL。我想從一個C#應用程序訪問它,它將用於將數據轉換爲SQL數據庫。從C#到C++ DLL的編組結構

我困在此刻編組從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'的第一次機會異常 'Sy'類型的未處理異常「Conversion.AccessViolationException」發生在Conversion.dll中

附加信息:試圖讀取或寫入受保護的內存。這通常表明其他內存已損壞。

即使我在項目中選擇了「調試非託管代碼」選項,我也無法調試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

也許你有一個針腳問題?你的字符串是隱式指針,請看這裏:http://stackoverflow.com/questions/5589945/net-c-sharp-unsafe-fixed-doesnt-pin-passthrough-array-element – Michel

+0

你確定事物的C方有一包的大小是1? –

+0

是的我在C項目編譯是/ Zp1 –

回答

1

對不起,我沒有足夠的代表處發表評論。

但是:

您是否在C中初始化這個結構?由於你的字符串只是指針,請檢查你的C代碼是否爲它想要填充的字符串分配內存。也許你只是在C中檢查它們不是NULL並試圖找到字符串的結尾......如果是這樣,在將它傳遞給C代碼之前初始化該結構。

這種互操作問題的一個有效方法是編寫一個非常簡單的C程序或dll,它打印您傳遞的結構的每個字段。當你弄清楚結構是如何到達C的時候,你可以用真正的DLL代替DLL。

另一個嘗試的方法是在C中獲取字符串的大小,並與C#中報告的sizeof進行比較。即使是一個字節的偏移也會導致很多問題。撰寫理智功能,導出的DLL:

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

然後,你可以在C#中的完整性檢查,測試通過理智與C#計算結構的大小返回的值。對齊問題難以查看,如果將來結構大小發生變化,您可以收到警告消息。

另外,如果字符串是用C分配的,那麼請考慮一下如何釋放這些字符串。

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

0

您誤聲明的成員iidx.seg和ifil.ix 。你已經將它們都聲明爲byval或靜態數組。因爲你還沒有初始化SizeConst,所以我認爲運行時將它們編組爲單元素數組。

這意味着C#運行時認爲您有一個6字節寬的字段用於iidx.seg(一個iseg的大小),以及ifil.ix的18或22個字節寬(取決於平臺)。但是C結構中兩個字段的大小都是指針的大小(4或8個字節,取決於平臺)。

順便說一句,您不需要使用unsafe關鍵字。您只需要使用指針時,fixedsizeof。總的來說,封送不會讓你不必使用不安全的代碼。

您是否考慮過使用與實際單詞更爲相似的名稱?

+0

我想這個問題是在自動編組。我最終創建了一個新的非託管結構,其中指針變爲IntPtr類型,然後手動編組值。數組不是固定的大小,這就是爲什麼我不能使用SizeConst。無論如何,這工作如果不理想 –