2017-02-22 6 views
1
に変換する

私は現在、アンマネージDLL(C++で書かれた、私が聞いているもの)とインターフェイスするDelphi(XE、特に)で書かれたプログラムを持っています。 Delphiアプリケーションは廃止されていますが、アンマネージDLLを使用する必要があるため、C#インターフェイスを記述する必要があります。大きな問題ではない。 DLLのソースやそれに関する良いドキュメントにアクセスできないことを除いて、これは私の最初のinteropへの進出であり、私を殺しています。 (また、付属のカスタム構造を持つ)Delphi以外のCOM DLLインターフェイスをC#

Delphiで
extern EXTERNC __declspec(dllexport) long initiate (
    double conc1,    
    double conc2,     
    long temp, 
    const char* NT 
); 

    extern EXTERNC __declspec(dllexport) long DoWork (
    long NumItems, 
    struct Structure* Items 
); 

、これらが正常にこのように実装されています:しばらくその

function CustomInitialize 
    (
    Concentration1 : double; 
    Concentration2 : double; 
    Temperature : LongInt; 
    const Type : PAnsiChar 
    ) : LongInt; cdecl; external 'CustomDLL.dll' name '_initiate'; 

    procedure DoWork 
    (
    NumItems : LongInt; 
    Items : PStructure 
    ); cdecl; external 'CustomDLL.dll' name '_DoWork'; 

    TStructure = record 
    ValueName : PAnsiChar; 
    Value : PAnsiChar; 
    Status : LongInt;  
    ReturnVal1 : Double; 
    ReturnVal2 : Double; 
    ReturnVal3 : Double; 
    Temp : Double; 
    end; 
    PStructure = ^TStructure; 

注ここで

は、DLLで利用可能な機能のドキュメントですDoWorkメソッドは一連の項目を取り込むように見え、すべての実装でNumItemsを1に設定し、C++に渡すのではなく、Delphiでオブジェクトをループします。

C#では、どのサンプルを投稿すべきかもわかりません。私は数日間グーグルで試してみましたが、私が試すことができるコードのすべてのバージョンのように感じましたが、役に立たなかったのです。最新のバージョンは次のとおりです。

namespace JunkProject 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      int test = _initiate(.05, .05, 60, "1"); 

      Console.WriteLine(test); 
      if (test != 1) 
       Console.ReadLine(); 

      var structure = new FoldStructure() { ValueName = "Test1", Value = "TESTTESTTESTTESTTESTTESTTEST", Status = 0, ReturnVal1 = 0.0, ReturnVal2 = 0.0, ReturnVal3 = 0.0, Temp = 0.0 }; 

      test = _DoWork(1, structure); 

      Console.WriteLine(structure.Value); 
      Console.ReadLine(); 
     } 

     private const string DLL_LOCATION = "CustomDLL.dll"; 

     [DllImport(DLL_LOCATION, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] 
     private static extern int _initiate(double Conc1, double Conc2, int Temp, [MarshalAs(UnmanagedType.LPStr, SizeConst = 5)] string Type); 

     [DllImport(DLL_LOCATION, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] 
     private static extern int _DoWork(int NumItems, [In, Out] Structure Struct); 

     [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
     public class Structure 
     { 
      [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)] 
      public string ValueName; 
      [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 28)] 
      public string Value; 

      public int Status; 
      public double ReturnVal1; 
      public double ReturnVal2; 
      public double ReturnVal3; 
      public double Temp; 
     } 
    } 
} 

これを行うと、アクセス違反が発生します。私はメソッドシグネチャをIntPtrにしようとしましたが、これは失敗します。私はメソッドのシグネチャを構造体へのポインタにしようとしましたが、これは通常、あらゆる方法でひどく間違っていますが、私は非常に長い間 '正しい方法'私はその方法が何であるか分からないからです。私は、正しい方法の署名が何であるかを知ることができれば、それはトンを助けるだろうと思う。

また、ソースを少し難読化することについてお詫び申し上げます。私が使用しているDLLは、独自仕様のものです。

助けてくれてありがとう!

答えて

1

属性のMarshalAsTypeパラメータの宣言には、_initiate()の属性を使用しないでください。入力は単なる文字列へのポインタなので、C#はそのようにマーシャリングします。

_DoWork()は、実際の配列をマーシャリングする必要があります(実装で要素が1つしか渡らない場合でも)。

Structureタイプではclassの代わりにstructを使用する必要があります。 ValueNameValueフィールドの宣言は、あなたのDelphiコードと一致しません。あなたのDelphiコードでは、それらは生のポインタにすぎず、おそらく割り当てられた文字バッファになります。しかし、あなたのC#コードでは、固定長文字配列であるかのように可変長のstring値を整列しています。

代わりに、より多くのこのような何かを試してみてください:

namespace JunkProject 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      int test = initiate(.05, .05, 60, "1"); 

      Console.WriteLine(test); 
      if (test != 1) 
       Console.ReadLine(); 

      Structure[] structure = new Structure[1]; 
      structure[0].ValueName = "Test1"; 
      structure[0].Value = "TESTTESTTESTTESTTESTTESTTEST"; 
      structure[0].Status = 0; 
      structure[0].ReturnVal1 = 0.0; 
      structure[0].ReturnVal2 = 0.0; 
      structure[0].ReturnVal3 = 0.0; 
      structure[0].Temp = 0.0; 

      test = DoWork(1, structure); 

      Console.WriteLine(structure[0].Value); 
      Console.ReadLine(); 
     } 

     private const string DLL_LOCATION = "CustomDLL.dll"; 

     [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
     public struct Structure 
     { 
      [MarshalAs(UnmanagedType.LPStr)] 
      public string ValueName; 
      [MarshalAs(UnmanagedType.LPStr)] 
      public string Value; 
      public int Status; 
      public double ReturnVal1; 
      public double ReturnVal2; 
      public double ReturnVal3; 
      public double Temp; 
     } 

     [DllImport(DLL_LOCATION, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "_initiate")] 
     private static extern int initiate(double Conc1, double Conc2, int Temp, [MarshalAs(UnmanagedType.LPStr)] string Type); 

     [DllImport(DLL_LOCATION, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "_DoWork")] 
     private static extern int DoWork(int NumItems, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] Structure[] Struct); 
    } 
} 

は、しかし、あなたが_DoWork()終了後Structure.Valueフィールドを読んでいるので、あなたがより多くの何かをする必要があるかもしれませんので、おそらくそれは、そのフィールドに新しいデータを書き込むことになります代わりに次のようにしてください:

namespace JunkProject 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      int test = initiate(.05, .05, 60, "1"); 

      Console.WriteLine(test); 
      if (test != 1) 
       Console.ReadLine(); 

      Structure[] structure = new Structure[1]; 

      structure[0].ValueName = "Test1"; 
      structure[0].Value = Marshal.AllocHGlobal(28); 
      // alternatively: 
      // structure[0].Value = (IntPtr) Marshal.StringToHGlobalAnsi("TESTTESTTESTTESTTESTTESTTEST"); 
      structure[0].Status = 0; 
      structure[0].ReturnVal1 = 0.0; 
      structure[0].ReturnVal2 = 0.0; 
      structure[0].ReturnVal3 = 0.0; 
      structure[0].Temp = 0.0; 

      test = DoWork(1, structure); 

      String Value = Marshal.PtrToStringAnsi(structure[0].Value); 
      Console.WriteLine(Value); 
      Console.ReadLine(); 

      Marshal.FreeHGlobal(structure[0].Value); 
     } 

     private const string DLL_LOCATION = "CustomDLL.dll"; 

     [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
     public struct Structure 
     { 
      [MarshalAs(UnmanagedType.LPStr)] 
      public string ValueName; 
      public IntPtr Value; 
      public int Status; 
      public double ReturnVal1; 
      public double ReturnVal2; 
      public double ReturnVal3; 
      public double Temp; 
     } 

     [DllImport(DLL_LOCATION, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "_initiate")] 
     private static extern int initiate(double Conc1, double Conc2, int Temp, [MarshalAs(UnmanagedType.LPStr)] string Type); 

     [DllImport(DLL_LOCATION, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "_DoWork")] 
     private static extern int DoWork(int NumItems, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] Structure[] Struct); 
    } 
} 
+0

ありがとうございます!アレイのコンセプトは私が先に試したものでしたが、私が実際にやっていることを理解する上ではまだ緑でしたので、間違っている可能性があります。これらのコメントは間違いなく意味があり、私が欠けていたものを明確にするのに役立ちます。私は間違いなくこれらのそれぞれについて前後に行っていましたが、私はフィードバック機構がなく、何かがうまくいっているかどうかを確認するための「前後の」プログラミングの悪夢に悩まされました。 –

関連する問題