Windows平台不同机器字长(x86/x64)程序/库互操作解决方法
1. 问题背景
某系统集成软件研发项目中,有设备厂家仅提供了C# 可调用的x64动态库,而另有一厂家仅提供x86动态库,均是C++编译版本。x86动态库直接导出函数失败,环境是VS2019,dotnet framework 4.7.2。
2. 解决思路
网上查阅大量资料也就几篇博文后,给出的解决方案是:主程序改为x64编译,所有设备的程序库均使用x64版本;另建一x86 C++编译版COM服务程序(.exe)调用x86动态库,封装出C#可调用接口。x86的这两个文件需置于程序根目录下,于主程序启动的时机启动该COM服务(命令方式:xxx.exe /regserver)。开发过程中,注册服务后,程序可直接引用该COM服务。
3. 操作示例
a) 创建COM服务
VS新建项目 → 搜索模板“ATL” → 选择“ATL项目” → 输入项目名称并创建(例如“MyComSvr”) → 在弹出的对话框中选择“应用程序类型”为“服务(.exe)” → 确定并等待项目构建完成
b) 功能封装
①项目创建完成后出来两个项目,一个是输入的项目名MyComSvr,另一个是项目名后加了“PS”,PS这个不需要操作
②右键项目添加新建项,左边栏Visual C++下选择“ATL”,选中“ATL简单对象”,配置名称,例如“Adder”,创建完成后出来对应名称的头文件和源文件
③在类中添加对应的方法,例如:
1 class ATL_NO_VTABLE CAdder : 2 …… 3 public: 4 STDMETHODIMP PlusOne(LONG i, PLONG res); 5 …… 6 };
adder.h
1 STDMETHODIMP_(HRESULT __stdcall) CAdder::PlusOne(LONG i, PLONG res) 2 { 3 *res = ++i; 4 return S_OK; 5 }
adder.cpp
④在idl文件中添加需要导出的方法:
interface IAdder : IDispatch { [id(1)] HRESULT PlusOne([in] LONG i, [out, retval] LONG* res); };
MyComSvr.idl
这里的[in]\[out]等特性标注决定了调用时参数的传输形式。[in]直接传值,[in, out]搭配指针则是ref,[out]搭配指针是out,[out, retval]此项唯一代表函数返回值;[id(1)]是自定义的接口顺序编号,从1开始往后面排即可;具体语法约定参考微软官方文档。
⑤编译项目,并注册(启动服务)。如果是以管理员身份启动的VS则无需注册。
c) 调用COM对象
已经启用刚刚创建的COM服务后,C#程序可以引用之,左边栏选COM,名称是MyComSvrLib,后面自动加上了“Lib”。使用下面这段代码验证:
1 using System; 2 using MyComSvrLib; 3 4 namespace CshCalling 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 Adder adder = new Adder(); 11 string cmd = string.Empty; 12 int i = 0; 13 while (cmd != "q") 14 { 15 Console.WriteLine(i); 16 i = adder.PlusOne(i); 17 cmd = Console.ReadLine(); 18 } 19 } 20 } 21 }
Program.cs
连续按回车,输出不断+1的值,验证正确。