2011-11-13 5 views
3

からダイナミックアレイでデルファイDLLの使用:私は次のタイプが含まれているDelphiのDLLを有しているC#

type 
    TStepModeType = (smSingle, smMultiStep); 

    TParameter = record 
    Number: Integer; 
    end; 

    TStruct = record 
    ModType: PAnsiChar; 
    ModTypeRev: Integer; 
    ModTypeID: Integer; 
    RecipeName: PAnsiChar; 
    RecipeID: Double; 
    RootParamCount: Integer; 
    StepMode: TStepModeType; 
    ParamCount: Integer; 
    Parameters: array of TParameter; 
    end; 

私はDLLは、意志Delphiのタイプに対応するRefオブジェクトを渡すC位からこのDLLを呼び出す必要塗りつぶして返します。私はこのような私のC#コードで構造体を定義している:

enum stepModeType 
{ 
    Single, 
    MultiStep 
} 

[StructLayout(LayoutKind.Sequential)] 
struct parameter 
{ 
    public int Number; 
} 

[StructLayout(LayoutKind.Sequential)] 
struct recipe 
{ 
    public string modType; 
    public int modTypeRev; 
    public int modTypeId; 
    public string recipeName; 
    public double recipeId; 
    public int rootParamCount; 
    public stepModeType stepMode; 
    public int paramCount; 
    public IntPtr parameters; 
} 

私は動的配列(パラメータ:たTParameterの配列)に走ったまで、私は大丈夫やっていたDelphiコードで。私は動的配列はDelphiのみの構造だと理解しています。そのため、配列へのポインタを取得してコンテンツを取得するために、C#コードでIntPtrを使用することを選択しました。残念ながら、私はこのinteropのものにはむしろ新しいです。私はIntPtrに対処する方法がわかりません。

たとえば、Delphi DLLは、2つのパラメータ項目を使用して動的配列を生成します。誰かが、Delphi DLLからC#呼び出しアプリケーションに戻ってくると、2つのパラメータ項目を配列から取得するC#コードを表示することはできますか?

更新:まあ、私が与えられたデルファイコードは、単純化されたバージョンでした。デルファイの開発者の一人は、ダイナミックアレイのダイナミックアレイのダイナミックアレイを含む実質的により複雑なリアルバージョンよりも簡単なバージョンを使い始める方が簡単だと考えていました。とにかく、私は今、完全に私の頭の上です。私は、デルファイが危険であると知っているだけです。以下は、Delphiコードの実際の構造体のコードです。私のC#アプリケーションからこれらの構造をどう扱うかについてのさらなるガイダンスは、非常に高く評価されます。ダイナミック配列がネストされていても、それは可能ではないかもしれません。

type 
    TStepModeType = (smSingle, smMultiStep); 

    TParamValue = record 
    strVal: String; 
    fVal: Double; 
    Changed: Boolean; 
    end; 

    TSteps = array of TParamValue; 

    TRule = record 
    Value: String; 
    TargetEnabled: Boolean; 
    end; 

    TParamInfo = record 
    Caption: String; 
    Units: String; 
    RuleCount: Integer; 
    Rules: array of TRule; 
    end; 

    TParameter = record 
    Info: TParamInfo; 
    Steps: TSteps; 
    end; 

    TStruct = record 
    ModType: PAnsiChar; 
    ModTypeRev: Integer; 
    ModTypeID: Integer; 
    RecipeName: PAnsiChar; 
    RecipeID: Double; 
    RootParamCount: Integer; 
    StepMode: TStepModeType; 
    ParamCount: Integer; 
    Parameters: array of TParameter; 
    end; 
+0

Delphi DLLに構造体のダイナミックメモリの割り当てを解除する関数が含まれていますか? –

+0

それはありますが、あなたの答えで指摘されているように、私はメモリの問題を引き起こしてしまった文字列フィールドを間違って使用していました。 – meyousikmann

答えて

5

私は、DLLがrecipe構造体の割り当てを解除する機能を持っている信頼を想定しています。それは、あなたがC#からやりたいことができないことです。この点については、後で詳しく説明します。

デルファイの動的配列は、有効なinterop型ではありません。実際には、コンパイラの単一バージョンでコンパイルされたDelphiコードの内部でのみ使用するべきです。公に公開するのは、C++クラスをDLLからエクスポートするのと同じです。

適切なinterop型を使用して配列をエクスポートするように、Delphiコードを再加工するのが理想的です。ただし、この場合は、Delphiコードを調整せずにマーシャリングを行うのは比較的簡単です。

Delphiの動的配列はDelphi 4で導入され、その実装はそれ以来変更されていません。 array of T動的配列変数は実質的に最初の要素へのポインタです。要素は順次メモリに配置されます。動的配列変数は、(負のオフセットで)参照カウントと配列のサイズも維持します。ダイナミックアレイを変更したり、サイズを確認したりする必要がないので、これらは無視しても問題ありません。

ParametersフィールドにIntPtrを使用すると、フィールドが完璧です。 TParameterにはただ1つの32ビット整数が含まれているため、を使用して、int[]アレイにまっすぐにコピーできます。

したがって、Delphi DLLが返ってきたら、Marshal.Copyを使用して最終的なマーシャリングステップを実行できます。動的配列を扱うが、それが起こるように、そのままで、あなたのコードで別の問題を抱えている

if (theRecipe.paramCount>0) 
{ 
    int[] parameters = new int[theRecipe.paramCount]; 
    Marshal.Copy(theRecipe.parameters, parameters, 0, theRecipe.paramCount); 
    ... do something with parameters 
} 

。 2つの文字列をC#構造体でstringと宣言しています。これは、2つのPAnsiCharフィールドでDelphi DLLによって返されたメモリを解放する責任をMarshallerが負うことを意味します。それはCoTaskMemFreeを呼び出すことによってそうするでしょう。私は、Delphiコードで行われたPAnsiCharフィールドの割り当てと一致させないと確信しています。

上記のように、このインターフェイスのコントラクトは、recipe構造体によって参照されるヒープメモリの割り当てを解除するために、別のDLL関数を呼び出すことです。つまり、2つの文字列と動的配列です。

この問題をC#で処理するには、marshallerがPAnsiCharフィールドの割り当てを解除しようとしていないことを確認する必要があります。これは、C#構造体の中でIntPtrと宣言することで実現できます。次にMarshal.PtrToStringAnsiを呼び出してC#文字列に変換します。


私は、上記を書くためにDelphiコードとC#コードの間の契約についていくつかの仮定をしなければなりませんでした。私の前提のどれかが間違っている場合は、質問を更新してください。私はこの答を一致させようとします!私はこれが役立つことを願っています

+0

絶対完璧!本当にありがとう。また、現在のコードでメモリの問題をキャッチするためにも感謝します。レシピ構造体のメモリを解放する別のDLL関数がありますが、C#コードで文字列型を間違って使用していました。 – meyousikmann

0

専門用語混乱私は最初の考えは単純だったと考えます。

public parameter [] parameters;

+0

私が最初に思ったことはありますが、うまくいきません。動的ではなく静的なサイズの配列を使用するようにDelphiコードを変更すると、そのコードは完全に機能します。 Delphiの配列が動的にサイズ調整されている限り、私はパラメータのためにガーベジを取得します。 – meyousikmann

+0

私よりも洗礼を受けた人のように見えます。 –

0

2つのオプション:ダイナミックアレイがどのように格納され、c#側のものと一致するかを把握するか、C#側から呼び出すことができるDelphi側の基本メソッドのセットを作成するか、たとえば、getItemやsetItemなどを記録します。これは、通常、言語障壁を越えて互換性のない型がある場合に実行されます。将来のある時点で動的配列のメモリ構造が変更されるかどうかわからないため、後のアプローチを使用します。

ところで、TParameterをレコードとして定義したのはなぜですか?TParameter = integer?

私は、Delphi動的配列の構造について言うべき何かを持っているこのリンクが見つかりました:

http://www.programmersheaven.com/mb/delphikylix/262971/262971/dynamic-array-memory-storage/

をそしてこのリンクは、さらに多くの詳細を持っています。構造は単純な配列よりも少し複雑です。

Dynamic Array Structure

+0

私は実際にはDelphiコードを書いていませんでした。私はC#アプリケーションからインターフェイスする必要があるレガシープロジェクトから継承しました。 TParameterオブジェクトには、一度に多くのものが含まれていたと思います。私はそれを変更せずにDelphiコードとのインターフェースを試みるように求められました。だからこそ、動的配列をそのまま扱う方法を探していたのです。私は非常に単純な解決策を持っており、Delphiコードの周りにラッパークラスを作成し、動的配列を静的配列に変換してから呼び出し元のアプリケーションに送り返します。私はこれをする必要がないことを望んでいた。 – meyousikmann

+0

さて、あなたは以前のデザインに悩まされています。私はヘファナンからの答えを見て、それはうまくいくはずです。私は、コールバックを含むDelphiとC#の間のかなり複雑なインターフェイスに接続しました。マーシャリングを正しく行うためには注意が必要です。 – rhody

関連する問題