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

原文地址:https://www.cnblogs.com/foohack/p/8625356.html