我今天确实成功地使用 QT+mingw-w64 创建和调用进程内和进程外服务器,所以这里是一篇文章。
这是基于 https://doc.qt.io/qt-5/activeqt-server.html 的说明,尽管它们是为 QT 的 Windows 原生构建并使用 MSVC 编写的,因此需要进行一些更改。
已安装工具
- MSYS2 已更新至最新版本(截至 2020 年 10 月 29 日)。
-
qtcreator 和 mingw64/mingw-w64(64 位目标),通过 MSYS2 安装。
- 已安装包
mingw64/mingw-w64-x86_64-qt5-static(这会导致启用 qtcreator 中的“Qt5 Static”套件)。
- Visual Studio 2019 社区版(这里提供
midl.exe)。
-
https://github.com/lucasg/Dependencies - lucasg Dependencies walker。 (不是必需的,但有助于检查您的静态构建是否有效)。
创建 COM DLL(还没有类型库)
这些步骤假定您事先熟悉用于“普通”可执行文件的 QtCreator+qmake。
- 从
qtactiveqt/examples/activeqt/simple 复制示例项目。我实际上在 QT 的 MSYS2 安装中的任何地方都找不到这个,所以我直接克隆了 QT 源并选择了示例。
- 在 QtCreator 的套件管理中,我选择了 qt5-static 64 位套件。这是为了避免由于在运行时找不到 DLL 而导致的任何问题。
- 此示例是进程外服务器,因此要更改为进程内服务器,请将以下行添加到
simple.pro 文件中:TEMPLATE=lib 和 CONFIG += dll。
- 在 GCC 中启用静态链接,正如我在此答案中所述:https://stackoverflow.com/a/64583309/1505939
- 构建项目。这将给出一个警告,它无法读取
simple_res.o,但可以忽略。这应该会成功创建输出文件simpleax.dll,但随后会出现一些与widl 未找到有关的错误消息——我们将在下一节中解决。
将类型库写入 DLL
其他 Windows 工具(例如 regsvr32,以及任何其他想要使用 DLL 的客户端)希望能够从 DLL 中读取类型库。
在前面步骤中创建的 DLL 不包含类型库。我认为这是因为我们的工具链工具都不知道如何创建类型库。相反,我们必须使用 VS 社区提供的midl.exe 来生成类型库。
在 QtCreator 中有一个应该调用 midl 的构建后脚本。然而,这在 QtCreator 的 MSYS2 风格中不起作用。它似乎只是使用 MSVC 作为工具包为原生风味编写的。
所以我们必须手动进行后处理,这涉及到:
- 在MSYS2 shell中,进入
simpleax.dll的目录。
- 运行此命令:
idc simpleax.dll -idl simpleax.idl -version 1.0
- 启动 VS Community 安装附带的“x64 Native Tools Command Prompt”开始菜单项。
- 导航到
simpleax.dll的目录
- 运行命令:
midl simpleax.idl /nologo /tlb simpleax.tlb。 (注意 - 仅将 midl 添加到 MSYS2 路径是行不通的,因为它找不到一堆其他依赖项)。
- 返回 MSYS2 shell,转至
idc simpleax.dll -tlb simpleax.tlb。
请注意,只有在类型库发生更改时才需要执行所有这些操作(即,您对导出的 COM 对象的类定义进行了更改)。如果只是重建项目,那么只需要最后一步,您可能可以将其作为手动构建步骤添加到 QtCreator 配置中。
注册并运行测试
恭喜!您现在应该能够打开提升的命令提示符并运行 regsvr32 simpleax.dll 并使其成功。 (如果不起作用,请运行dependencies -chain simpleax.tlb,然后在最后它会列出所有 DLL 依赖项)。
注册成功后,我可以使用 VS Community 调用 COM 对象(新建 C++ 控制台项目和 #import "D:\path\to\simpleax.dll",构建,然后创建一个包含 DLL 包装器的 .tlh C++ 头文件)。
清理类型库
创建的类型库包含一大堆烦人的 QT 废话。我发现如果你想创建一个不是 ActiveX 控件的 COM 对象,你可以使用QObject 而不是QWidget 作为基类。那么你就不会了解与 GUI 元素有关的大部分内容了。
此外,从 COM 对象的类定义中取出 Q_CLASSINFO("EventsID",... 行意味着您不会在类型库中获得所有源/接收器的废话(出于某种原因,它决定必须将所有 QT事件沉没在那里)。
在这样做之后,只剩下大约一屏废话,包括 QPoint 的定义等,我认为这是 QT 的一个错误(因为不需要这些东西)。我发现我可以在调用 midl 之前从 .idl 文件中删除它,嘿,我们有一个可以发布的干净界面。
进程外服务器
这也对我有用,与上述步骤相同,但没有第 3 步(即保留为 app 而不是 lib)。通过midl附加类型库的过程是一样的。
通过testobject.exe -regserver注册类型库不会给你任何消息告诉你它是成功还是失败,所以另一种方法是在提升的MSYS2提示符下使用idc -regserver testobject.exe,然后它会给你一个失败消息。
我能够通过 VS Community 调用该对象,就像调用进程内服务器一样。
还有待解决的问题
- 弄清楚如何在 CMake 中执行此操作。
- 自动化构建后类型库过程。
- 让非静态链接正常工作。
- 自动删除类型库 guff(或报告错误)