VS2017写的exe调用Delphi 7写的DLL
公司有个很古老的系统,代码量很大,并且稳定线上运行10几年,这系统是公司的核心,公司收入基本靠它,系统几乎都是Delphi 7写的,要重写是不可能的。因为Delphi 7编译出来的DLL默认的导出符号就是二进制稳定的C符号。
所以,理论上任何语言都可以调用该DLL导出的API。
值得注意的是,在调用导出API的时候任何语言都是利用LoadLlibrary,GetProcAddress的原理来进行调用的。如果用C++来调用,最好这个干。
调用该API的输入输出参数最好要是平坦内存结构,比如C语言类型的结构体,注意结构体字段与Delphi的导出的结构体的字段长度对应一致。
如果是C#,最后用Marshal相关的函数对参数对象进行转换成平台内存结构来做输入输出,这样才能保证不出错。
如果用C# ,以下是代码参考:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Runtime.InteropServices; 6 7 namespace CSharpCallDelphiDLL 8 { 9 class Program 10 { 11 12 [StructLayout(LayoutKind.Sequential)] 13 public struct PReadPatientInfoIn 14 { 15 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4001)] 16 public byte[] Xmlin; 17 } 18 19 [StructLayout(LayoutKind.Sequential)] 20 public struct PReadPatientInfoOut 21 { 22 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4001)] 23 public byte[] Xmlout; 24 } 25 26 [StructLayout(LayoutKind.Sequential)] 27 public struct PErrorInfo 28 { 29 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 201)] 30 public byte[] ErrorXml; 31 } 32 33 [DllImport(@"C:/Users/MathxH/Desktop/CSharpCallDelphiDLL/CSharpCallDelphiDLL/bin/x86/Debug/Hisint.dll", 34 EntryPoint = "ReadPatientInfo", CharSet = CharSet.Ansi, 35 CallingConvention = CallingConvention.StdCall)] 36 extern static void ReadPatientInfo(ref PReadPatientInfoIn pIn, ref PReadPatientInfoOut pOut, ref PErrorInfo pErr); 37 38 static void Main(string[] args) 39 { 40 Console.WriteLine("Enry"); 41 42 PErrorInfo err; 43 PReadPatientInfoIn sss; 44 PReadPatientInfoOut ooo; 45 46 47 String kk = "<ROOT><HOSPITALCODE>0003</HOSPITALCODE><PERSONNO></PERSONNO><ARRANGER>陈哈哈</ARRANGER><SECTIONNAME>骨科</SECTIONNAME><ZFLB>11</ZFLB><MZZDMC>癌症</MZZDMC><IDENTIFYNO>532625194704222925</IDENTIFYNO><MSGNO>71</MSGNO></ROOT>"; 48 49 String emm = ""; 50 51 Encoding gb2312; 52 53 System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding(); 54 gb2312 = Encoding.GetEncoding("GB2312"); 55 Byte[] bytes = gb2312.GetBytes(kk.PadRight(4001)); 56 sss.Xmlin = bytes; 57 // sss.Xmlin = PadRightEx(kk,4002).ToCharArray(); 58 59 ooo.Xmlout = encoding.GetBytes(emm.PadRight(4001)); 60 err.ErrorXml = encoding.GetBytes(emm.PadRight(201)); 61 62 63 64 ReadPatientInfo(ref sss, ref ooo, ref err); 65 66 // String outss = new String(ooo.Xmlout); 67 String outss = gb2312.GetString(ooo.Xmlout); 68 69 70 Console.ReadLine(); 71 72 } 73 } 74 }
以上代码期间出了一些错误:
1. 抛出BadImageFormatException的异常,也就是exe的代码要与所调用的DLL的机器位数一致,x86只能调用x86的,x64只能是x64.
2. 未能封送类型,因为嵌入数组实例的长度与布局中声明的长度不匹配。 这个需要C#这边的array长度与声明的长度一致,需要Padding补齐
3. System.Runtime.InteropServices.COMException”类型的未经处理的异常在 CSharpCallDelphiDLL.exe 中发生
传递给系统调用的数据区域太小。 (异常来自 HRESULT:0x8007007A)。这个是PadRight的时候出现中文编码导致填充的长度出现问题。把中文改成英文就不会出错了。
4. 针对问题3,因为参数肯定会有中文,所以,需要把编码转换成GB2312 locale
references:
https://www.cnblogs.com/wintalen/archive/2010/12/20/1911599.html
https://blog.csdn.net/cnhk1225/article/details/53265042
http://blog.51cto.com/andwp/1352739
https://www.cnblogs.com/Robert-huge/p/5130284.html
http://www.myexception.cn/h/1381235.html