方法:
64位windows支持64位和32位进程(包括本机或跨机)间进程间通信(RPC)。在64位windows中,一个进程外32位COM服务器能够与64位客户端进行通信,同样一个进程外64位COM服务器也能与32位客户端进行通信。因此,如果你有一个32位COM无法识别的DLL,你可以将它封装到一个进程外COM服务器中并在一个64位进程中用COM配置调用DLL。
原文链接:https://blog.csdn.net/nie2314550441/java/article/details/49867735
使用工具:
VS2017
步骤:
1.Devin已经验证64位exe能调用32位dll,从他手中拿来Demo分析。
一共三个项目:
ATLProject12->COM exe 32位 (64位程序通过COM exe 调用32位dll)
ConsoleApplication-> 64位程序
MFCLibrary3->32位dll
问题:项目copy后vs打开,把依赖项路径什么的改了,发现代码中的类或函数都不能直接F12。
原因:.vs这个文件夹没有删除,导致VS打开项目时,是根据之前同事电脑上的配置进行的加载。
.vs文件夹:用来存储当前用户在解决方案中的工作配置,具体包括VS关闭前最后的窗口布局、最后打开的选项卡/操作记录/文件文档、某些自定义配置/开发环境、调试断点等这类设置信息和状态
参考链接:https://shiyousan.com/post/636441130259624698
2.
MFCLibrary:32位dll,会导出一个接口供ATLProject项目调用。
ATLProject12:
ATL(类型库):类型库是idl文件、COM类文件,.h文件,.cpp文件,.def文件的综合体。类型库里面包含了我们所要用的COM组件(COM类)。我们经常使用的word、excel的COM组件,都是通过他们的类型库导入客户程序的,然后我们才能通过CoCreateInstance()来构建组件对象。类型库就是COM类的容器,里面包含了若干COM类,是一个独立的能被客户程序导入的dll文件,是对COM组件的打包。
ATL作用:
ATL专门用来生成COM组件的,编译后生成dll文件或者exe文件。
com组件和普通的dll文件有什么区别呢?
com组件优点:1.语言无关性;2.便于升级扩展;3.有很好的继承封装多态特性,即面向对象能力强;4.完成进程间,分布式功能;5.接口调用,便于组织。
VS创建一个ATL项目(必须要用管理员权限打开,不然最后编译时会报错 error MSB8011: 未能注册输出。PS:报这个错也有可能是依赖项没有全部配好)
选择服务(.exe) 。
生成项目后新建项
ATL简单对象
ProgID:
是在定义COM类时为类起的别名,方便程序员记住。命名规则为:ProjectName.ClassName.VersionNumber,即:工程名(类型库名称).类名(COM类名称).(版本号)
在这里填一个自己能记住的先。
生成ATLSimpleObject.cpp和ATLSimpleObject.h
在.h和.cpp中添加需要导出的接口及实现
源文件中有一个idl文件
在interface里面添加前面写好的需要导出的接口,[in]:表示传入参数,[out]:表示传出参数。基础变量不用改变,string字符串需要用BSTR转。或使用VARIANT变量传递参数
生成,产生一个ATLProject_i.c 和 ATLProject_i.h(.h中有导出的函数的声明)。把这两个文件添加到64位项目下,就能直接调用接口。
验证过程中参数传递的问题:
需要传图片进32位dll进行处理,并返回处理后的图片,使用OpenCV3.0
返回Mat。
使用COM组件提供的VARIANT和SAFEARRY(安全数组)进行Mat数据的传递。
VARIANT* matBackData;
SAFEARRAY *pArray1 = nullptr;
HRESULT hr = SafeArrayAllocDescriptor(1, &pArray1);//创建SAFEARRAY结构对象,在栈上
pArray1->cbElements = sizeof(unsigned char); //每个元素占用的字节数
pArray1->rgsabound[0].cElements = 5120 * 5120; //第0维数组元素个数
pArray1->rgsabound[0].lLbound = 0; //第0维数组起始下标
hr = SafeArrayAllocData(pArray1);//在堆上申请内存存放SAFEARRAY
上面这段代码创建了一个5120 * 5120大小的安全数组
memcpy(pArray1->pvData, umatBackData, 5120 * 5120);
//pArray1->pvData = umatBackData; //不能直接=。
umatBackData为需要放入安全数组的数据。这里umatBackData = mMat.data。mMat是处理后的图片
matBackData->vt = VT_ARRAY | VT_UI1;//VT_VARIANT //SAFEARRAY的类型必须为VT_ARRAY, VT_UI1: 指明数组数据为unsigned char
matBackData->parray = pArray1;
matBackData可以成功返回数据。在调用端使用完后,需要释放安全数组
SafeArrayUnaccessData(matBackData.parray);
SafeArrayDestroy(matBackData.parray);
VariantClear(&matBackData);
总结:
使用COM进行64位32位间的接口相互调用,和普通的32调32,64调64方法基本一样,主要问题在于复杂结构的参数传递,可以使用COM提供的VARIANT和SAFEARRY对参数进行打包,然后传递。