1. 为什么我们需要免注册加载COM DLL?
在上一篇文章里,我们聊了怎么用批处理文件(bat)来注册大漠插件(dm.dll),让它变成一个标准的COM组件,这样在Qt里就能直接用了。这个方法简单直接,但用久了,你就会发现它有几个挺“膈应人”的地方。
首先,很多开发者,包括我自己,都有点“注册表洁癖”。Windows的注册表是个挺精贵的地方,你装个软件、安个驱动,它都会往里面写东西。时间一长,注册表越来越臃肿,系统速度就可能受影响。为了一个自动化脚本工具,就要在系统里永久性地“安家落户”,总觉得不太干净。万一哪天这个dll有问题,或者你想彻底清理掉它,手动去注册表里翻找、删除那些CLSID键值,可不是件轻松活,一不小心删错了,系统都可能出问题。
其次,COM注册这事儿本身就不太靠谱。你有没有遇到过这种情况?明明用管理员权限运行了注册命令(regsvr32),系统也提示成功了,可程序一跑起来,还是报“类未注册”的错误。这可能是因为权限问题、系统策略限制,或者是dll依赖的其他组件没到位。特别是在一些对安全要求比较高的环境,或者使用了某些“优化”过的Windows系统上,COM注册失败简直是家常便饭。更麻烦的是,如果你需要同时使用大漠插件的多个版本(比如老项目依赖v7.0,新项目想用v7.2),COM注册机制就傻眼了。你只能注册一个版本,另一个版本一注册就把前一个给覆盖了,版本冲突问题根本无解。
所以,动态加载,免注册这条路就非走不可了。它的核心思想是:我们不通过系统注册表来告诉Windows“我这个dll是个COM组件”,而是由我们的程序自己,在运行时,像加载一个普通函数库一样,手动把这个dll请进内存,然后自己动手去创建里面的COM对象。这样一来,dll完全不需要接触注册表,实现了“绿色”使用。多个版本也可以放在不同的文件夹里,程序想用哪个就加载哪个,完美解决了共存问题。这个思路,就是我们今天要深入探讨的实战内容。
2. 准备工作:获取“钥匙”——COM接口定义文件
要想动态创建一个COM对象,你首先得知道这个对象长什么样、有哪些“手脚”(接口和方法)。对于标准的、已注册的COM组件,开发环境(如Visual Studio)可以通过读取注册表自动获得这些信息。但对于我们这种“黑户”dll,就得自己动手,从dll里把它的“蓝图”给提取出来。
这个“蓝图”,就是类型库(Type Library)。大漠插件的dm.dll本身已经内嵌了它的类型库。在微软的开发体系里,有一个非常方便的工具指令,叫做#import。它虽然不是C++标准的一部分,但在MSVC编译器里被广泛支持,我们的Qt项目如果使用MSVC编译套件,就能直接用上。
具体操作步骤,我一步步带你走一遍:
- 新建一个干净的Qt控制台项目。选择MSVC编译器(比如msvc2015 32位),项目类型选
Qt Console Application就行。这里用控制台项目是为了演示过程更清晰,没有GUI那些干扰项。 - 把
dm.dll放到项目目录下。你可以直接复制过来。为了方便,我通常会在项目根目录建一个3rdparty之类的文件夹,专门放这些第三方库。 - 在任一个源文件(比如
main.cpp)的开头,添加一行神奇的代码:
例如,如果dll就在项目根目录,直接写#import "你的路径/dm.dll" no_namespace#import "dm.dll"。no_namespace的意思是让编译器生成的头文件里,所有的类和接口都不要放在命名空间里,这样我们后面用起来省事,不用老写DM::之类的。 - 编译一下这个项目。不需要写任何其他代码,只需要有这一句
#import并成功编译。 - 去项目的输出目录(一般是
debug或release文件夹)里找“宝藏”。编译成功后,编译器会自动解析dm.dll中的类型库,并生成两个至关重要的C++头文件:dm.tlh(Type Library Header):这里包含了所有接口、类、方法、枚举的定义,相当于这个COM组件的C++语言说明书。dm.tli(Type Library Implementation):这里包含了一些编译器生成的包装函数代码,主要是为了简化Smart Pointer(智能指针)的使用。
拿到这两个文件,就等于拿到了打开dm.dll功能大门的“钥匙”。接下来,你就可以把这两个文件复制到你的项目源码目录(比如一个include文件夹),然后在真正的项目里引用它们,而不再需要原始的#import "dm.dll"语句了。这样做的好处是编译更快

183

被折叠的 条评论
为什么被折叠?



