2

独自のデータベースにアクセスする C DLL があります。データを SQL データベースに変換するために使用される C# アプリケーションからこれにアクセスしたいと考えています。

私は現在、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;
}

次の例外が発生しています

タイプ 'System.AccessViolationException' の初回例外が Conversion.dll で発生しました タイプ 'System.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
}
4

2 に答える 2

1

申し訳ありませんが、コメントするのに十分な担当者がいません。

しかし:

この構造体を C で初期化しますか? 文字列は単なるポインタであるため、C コードが入力したい文字列にメモリを割り当てているかどうかを確認してください。おそらく、C でそれらが NULL ではないことを確認し、文字列の末尾を見つけようとしているだけです...もしそうなら、 C コードに渡す前に構造体を初期化します。

この種の相互運用性の問題に対する効果的なアプローチは、渡す構造体の各フィールドを出力する非常に単純な C プログラムまたは dll を作成することです。構造がどのように C に到着するかを理解したら、DLL を実際の DLL に置き換えることができます。

もう 1 つの試みは、C で文字列のサイズを取得し、C# で報告されたサイズと比較することです。1 バイトのオフセットでさえ、多くの問題を引き起こす可能性があります。サニティ関数を作成し、DLL にエクスポートします。

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

次に、C# でサニティ チェックを行い、C# で計算された構造体のサイズでサニティによって返される値をテストします。位置合わせの問題はわかりにくい場合があり、構造のサイズが将来変更された場合は、警告メッセージが表示されることがあります。

また、文字列が C で割り当てられている場合は、後でこれらの文字列を解放する方法を検討してください。

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

于 2012-09-18T12:50:41.703 に答える
0

メンバー iidx.seg および ifil.ix 1を誤って宣言しました。これらは両方とも byval (静的) 配列として宣言されています。SizeConst を初期化していないため、ランタイムはそれらを単一要素の配列としてマーシャリングすると思います。

これは、C# ランタイムが、iidx.seg のフィールドが 6 バイト幅 (1 つの iseg のサイズ) であり、ifil.ix のフィールドが 18 または 22 バイト幅 (プラットフォームによって異なる) であると認識していることを意味します。ただし、C 構造体の両方のフィールドのサイズはポインターのサイズです (プラットフォームに応じて 4 または 8 バイト)。

ところで、unsafeキーワードを使用する必要はありません。fixedポインタ、 、およびを使用する場合にのみ必要ですsizeof。一般に、マーシャリングにより、安全でないコードを使用する必要がなくなります。

1実際の言葉により似た名前を使用することを検討したことがありますか?

于 2012-09-18T15:21:53.110 に答える