作者简介
很高兴认识您!
我叫卢家波,河海大学水文学及水资源博士研究生,研究兴趣为高效洪水淹没预测、洪水灾害预警、机器学习、替代模型和降阶模型。
变化环境下,极端洪水事件多发,我希望能通过研究为水灾害防御做出贡献,为人民服务。
我很乐意与您交流和研究合作,vx Jiabo_Lu。
主页 https://lujiabo98.github.io
简历 https://lujiabo98.github.io/file/CV_JiaboLu_zh.pdf
博客 https://blog.csdn.net/weixin_43012724?type=blog
来信请说明博客标题及链接,谢谢。
1 目标
在完成使用C++以面向对象方式编写三水源新安江模型后,希望进一步把新安江模型做成COM组件的形式(也就是.dll
的形式)。
为此,我先跟导师学习了静态库、动态库和COM的知识,也从网上找到案例复现其创建及调用过程。
现在尝试把新安江模型做成COM组件的步骤记录如下。
2 创建COM组件
-
打开Visual Studio 2019 新建ATL项目 → \rightarrow →项目名称:XAJ → \rightarrow →创建 → \rightarrow →勾选支持COM+ 1.0、支持组件注册 → \rightarrow →确定。
-
添加一个新的ATL对象。右键XAJ项目 → \rightarrow →添加 → \rightarrow →新建项 → \rightarrow →ATL → \rightarrow →ATL简单对象 → \rightarrow →名称为Model → \rightarrow →添加 → \rightarrow →ProgID取名为项目名+“.”+短名称 → \rightarrow →完成。
-
创建自己的方法,即在
CModel
类中添加调用新安江模型的函数。在头文件Model.h
添加函数声明Run()
,目的是为了调用原来新安江模型的main()
函数。
// Model.h: CModel 的声明
#pragma once
#include "resource.h" // 主符号
#include "XAJ_i.h"
#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
#error "Windows CE 平台(如不提供完全 DCOM 支持的 Windows Mobile 平台)上无法正确支持单线程 COM 对象。定义 _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA 可强制 ATL 支持创建单线程 COM 对象实现并允许使用其单线程 COM 对象实现。rgs 文件中的线程模型已被设置为“Free”,原因是该模型是非 DCOM Windows CE 平台支持的唯一线程模型。"
#endif
using namespace ATL;
// CModel
class ATL_NO_VTABLE CModel :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CModel, &CLSID_Model>,
public IDispatchImpl<IModel, &IID_IModel, &LIBID_XAJLib, /*wMajor =*/ 1, /*wMinor =*/ 0>
{
public:
CModel()
{
}
DECLARE_REGISTRY_RESOURCEID(106)
BEGIN_COM_MAP(CModel)
COM_INTERFACE_ENTRY(IModel)
COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()
DECLARE_PROTECT_FINAL_CONSTRUCT()
HRESULT FinalConstruct()
{
return S_OK;
}
void FinalRelease()
{
}
public:
//添加的函数声明
STDMETHODIMP Run(CHAR* argv[]);
};
OBJECT_ENTRY_AUTO(__uuidof(Model), CModel)
//XinanjiangModel.h
#pragma once
//主函数声明
int main(int argc, char* argv[]);
- 在源文件
Model.cpp
添加函数实现,以调用新安江模型main()
函数。
// Model.cpp: CModel 的实现
#include "pch.h"
#include "Model.h"
//添加新安江模型的头文件
#include "XinanjiangModel.h"
// CModel
STDMETHODIMP_(HRESULT __stdcall) CModel::Run(CHAR* argv[])
{
//运行新安江模型
main(0, argv);
//默认返回 函数执行正确
return S_OK;
}
- 在
XAJ.idl
中添加方法接口设置,如interface IModel : IDispatch
中所示,这里的Run()
函数为纯虚函数。
// XAJ.idl: XAJ 的 IDL 源
//
// 此文件将由 MIDL 工具处理以
// 生成类型库(XAJ.tlb)和封送处理代码。
import "oaidl.idl";
import "ocidl.idl";
[
object,
uuid(a817e7a2-43fa-11d0-9e44-00aa00b6770a),
dual,
pointer_default(unique)
]
interface IComponentRegistrar : IDispatch
{
[id(1)] HRESULT Attach([in] BSTR bstrPath);
[id(2)] HRESULT RegisterAll();
[id(3)] HRESULT UnregisterAll();
[id(4)] HRESULT GetComponents([out] SAFEARRAY(BSTR)* pbstrCLSIDs, [out] SAFEARRAY(BSTR)* pbstrDescriptions);
[id(5)] HRESULT RegisterComponent([in] BSTR bstrCLSID);
[id(6)] HRESULT UnregisterComponent([in] BSTR bstrCLSID);
};
[
object,
uuid(ea7ac902-5dc5-4732-b2dc-46f50fd9385b),
dual,
nonextensible,
pointer_default(unique)
]
interface IModel : IDispatch
{
//============方法接口设置============//
[id(1)] HRESULT Run([in] CHAR* argv[1]);
};
[
uuid(94233707-a67e-4579-9cba-8e92fbf8ee01),
version(1.0),
custom(a817e7a1-43fa-11d0-9e44-00aa00b6770a,"{fb867d6b-4a32-4e2f-addd-3047d0103833}")
]
library XAJLib
{
importlib("stdole2.tlb");
[
uuid(fb867d6b-4a32-4e2f-addd-3047d0103833)
]
coclass CompReg
{
[default] interface IComponentRegistrar;
};
[
uuid(e002b0ec-9a5c-44c8-8b96-bb7648d05f25)
]
coclass Model
{
[default] interface IModel;
};
};
import "shobjidl.idl";
- 解决方案配置选择
Release
,平台选择x64
,右键XAJ
项目生成。XAJ.dll
为 64位的COM 组件。XAJ.dll
或XAJ.tlb
是之后 C++ 调用COM所需要引入的文件。
3 注册COM组件
- 注意: 根据COM组件位数选择
regsvr32.exe
,如果COM组件为32位,用C:\Windows\System32\regsvr32.exe
注册;如果COM组件为64位,则用C:\Windows\SysWOW64\regsvr32.exe
注册。
因为上一步选择x64
,所以这里的COM组件是64位的,所以应该用C:\Windows\SysWOW64\regsvr32.exe
注册。
- 注册。以管理员身份打开powershell,依次输入如下代码,完成注册。
cd E:\Research\XinanjiangModel\Myself\COM\XAJ\x64\Release
C:\Windows\SysWOW64\regsvr32.exe -i XAJ.dll
- 或者以管理员身份打开CMD,依次输入如下代码,完成注册。
E:
cd E:\Research\XinanjiangModel\Myself\COM\XAJ\x64\Release
C:\Windows\SysWOW64\regsvr32.exe -i XAJ.dll
4 调用COM组件
- 打开Visual Studio 2019,文件
→
\rightarrow
→新建
→
\rightarrow
→项目
→
\rightarrow
→控制台应用
→
\rightarrow
→创建
XAJCOMAPP
项目。
- 在源文件
XAJCOMAPP.cpp
中引入动态库文件XAJ.dll
目录。解决方案配置选择Debug
→ \rightarrow →平台选择x64
→ \rightarrow →右键XAJCOMAPP
项目 → \rightarrow →配置属性 → \rightarrow →C/C++ → \rightarrow →常规 → \rightarrow →附加包含目录 → \rightarrow →新行 → \rightarrow →选择动态库目录。这里的平台位数必须与COM组件位数一致。
- 在
XAJCOMAPP.cpp
中输入如下代码:
// XAJCOMAPP.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#import "XAJ.dll" raw_interfaces_only raw_native_types no_namespace named_guids
int main(int argc, char* argv[])
{
//初始化 COM 库,用来告诉 Windows 以单线程的方式创建 COM 对象
::CoInitialize(0);
//指向ITEMP类的智能指针
IModelPtr pModel = nullptr;
//创建 COM 组件对象,有两种方式
//HRESULT hr = pModel.CreateInstance(CLSID_Model); //使用CLSID创建
HRESULT hr = pModel.CreateInstance("XAJ.Model"); //使用ProgID创建
//调用新安江模型
pModel->Run(argv);
return 0;
}
- 点击本地Windows调试器,成功调用新安江模型COM组件。在
x64\Debug\
文件夹内生成流域出口流量过程Q.txt
,但由于该路径下没有新安江模型的输入文件,所以Q.txt
为空文件,文件大小为0KB。
- 在该路径下创建如下图5个新安江模型输入文件,再点击本地Windows调试器或
XAJCOMAPP.exe
,即可生成流域出口流量过程Q.txt
。
5 参考资料
“类未注册(HRESULT异常:0x80040154(REGDB_E_CLASSNOTREG))”
lujiabo98/XAJCOMAPP
lujiabo98/XAJ
lujiabo98/XinanjiangModel
6 本机目录
创建和注册COM组件目录:E:\Research\XinanjiangModel\Myself\COM\XAJ\x64\Release
调用COM组件目录:E:\Master\study\Cpp\XAJCOMAPP\x64\Debug