Windows平台不同机器字长(x86/x64)程序/库互操作解决方法

发布时间 2023-10-08 10:56:28作者: 永恒月华

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

具体语法约定参考微软官方文档。

⑤编译项目,并注册(启动服务)。如果是以管理员身份启动的VS则无需注册。

c) 调用COM对象

已经启用刚刚创建的COM服务后,C#程序可以引用之,左边栏选COM,名称是MyComSvrLib,后面自动加上了“Lib”。使用下面这段代码验证:

 1 using System;
 2 using ComSvrLib;
 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的值,验证正确。