2017-02-22 63 views
1

我/我们目前有一个用Delphi(XE,特别是)编写的程序,它与一个非托管DLL(用C++编写,从我所听到的)接口。 Delphi应用程序正在退役,但需要与非托管DLL一起工作,所以需要编写一个C#接口。没什么大不了。除了没有访问DLL的源代码或任何有关它的良好文档,以及这是我第一次进入互操作性,正在杀死我。将Delphi非COM DLL接口转换为C#

这里是在DLL中可使用功能的文档:

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 
); 

在Delphi中,这些都是成功地实施这样的(与自定义结构也包括在内):

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; 

注意的是,虽然DoWork方法似乎需要一系列项目,所有实现都将NumItems设置为1,并在Delphi中循环遍历对象,而不是将其传递给C++。

在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,但失败。我试图让方法签名成为一个指向struct的指针,在我尝试的每种方式中,这通常都会出现可怕的错误,尽管我无法确定我是否已经很长时间地以“正确的方式”因为我不确定那是怎么回事。我想如果我能弄清楚正确的方法签名是什么,那将会有好处。

此外,道歉轻微混淆来源。我正在使用的DLL是专有和whatnot。

任何帮助先行感谢!

回答

1

您不应该在_initiate()Type参数的MarshalAs声明中使用SizeConst属性。输入只是一个指向字符串的指针,所以让C#将它编组为这样。

_DoWork()需要一个数组(即使你的实现只传递1个元素),所以你应该编组一个实际的数组。

对于Structure类型,您应该使用struct而不是class。并且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

谢谢!数组的概念是我早些时候尝试过的,但是我对我实际上在做什么的理解仍然是绿色的,所以我可能只是错了。这些评论绝对有意义,有助于澄清我错过的东西。我确实在每一个这样的事情上来回奔走,但我陷入了一种“反复的”编程梦魇,没有反馈机制,看看有没有更好的工作。 –