博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
采用ATL模型代替lib dll 的调用
阅读量:2397 次
发布时间:2019-05-10

本文共 5915 字,大约阅读时间需要 19 分钟。

转载请标明是引用于

 例子代码:

(为WCE SDK下的例子,转为win32,自己移植)

 

 一、调用COM与调用DLL的区别和联系

不要把COM看成是DLL,DLL只是用来装载COM的,但是COM不一定要用DLL作载体,也可以用exe或者其他的。

而COM组件的调用是通过接口,只有通过CreateInstance获得接口指针,才可以使用该接口。所有的一切调用都是通过接口实现。

COM是更好的DLL,所以COM是可以通过传统的DLL调用方法来实现的。 COM里有一全局导出函数 DllGetClassObject.

COM库在加载COM组件时也是 LoadLibrary   GetProcAddress( "DllGetClassObject ") 开始的,只不过COM把Dll所在路径等等信息把他注册到注册表里了而已。

二、建立自己的COM接口,并导出DLL

新建项目时,选择ATL项目,然后我们在此ATL项目基础上添加自己的COM接口;当然也可以选择Win32空项目,然后在它基础上添加也可以。下面我们来讲一下步骤:

 1、在MyAdapter.idl文件中加入

interface IMyHandler : IDispatch{ [id(1), helpstring("method ScanBaudRate")] HRESULT ScanBaudRate([out]UINT* pnBaudRate, [in]UINT nComPort, [out, retval]BOOL* bRet);};

 

编译报错

1>ocidl.idl

1>Processing C:\Program Files\Windows CE Tools\wce500\CE2443I\include\ARMV4I\oleidl.idl
1>oleidl.idl
1>.\MyAdapter.idl(11) : error MIDL2079 : no [uuid] specified : [ Interface 'IMyHandler'  ]

在它上面增加

[ object, uuid(74DC3D5F-CE91-4E88-93C7-6E2D111B18BE), dual, helpstring("IMyHandler Interface"), pointer_default(unique)]interface IMyHandler : IDispatch{ [id(1), helpstring("method ScanBaudRate")] HRESULT ScanBaudRate([out]UINT* pnBaudRate, [in]UINT nComPort, [out, retval]BOOL* bRet);};

2、

library MyAdapterLib

{

}里增加以下代码

coclass MyHandler	{		[default] interface IMyHandler;	};

此时编译报以下错

1>oleidl.idl

1>.\MyAdapter.idl(38) : error MIDL2079 : no [uuid] specified : [ Coclass 'MyHandler'  ]

 

在它上面增加

 

[		uuid(D966A194-DC7E-4648-9654-AA872BB84B2B),		helpstring("MyHandler Class")	]

3、当MyAdapter.idl编译通过时,在MyAdapter_i.c里看到多了以下:

MIDL_DEFINE_GUID(IID, IID_IMyHandler,0x74DC3D5F,0xCE91,0x4E88,0x93,0xC7,0x6E,0x2D,0x11,0x1B,0x18,0xBE);MIDL_DEFINE_GUID(IID, LIBID_MyAdapterLib,0x9F897696,0x196F,0x476C,0xBC,0xDE,0xF5,0x1A,0x47,0x28,0xD3,0xB9);MIDL_DEFINE_GUID(CLSID, CLSID_MyHandler,0xD966A194,0xDC7E,0x4648,0x96,0x54,0xAA,0x87,0x2B,0xB8,0x4B,0x2B);

这是因为在idl文件中我们指定了uuid,所以生成的MyAdapter_i.c中能成生对应的IID_IMyHandler、LIBID_MyAdapterLib、CLSID_MyHandler。

 

4、在MyHandler.h里建类

// GPSHandlerclass ATL_NO_VTABLE MyHandler : 	public IDispatchImpl
, public ISupportErrorInfo, public CComObjectRoot, public CComCoClass
{

编译提示:

1>正在编译...

1>MyAdapter.cpp
1>GPSHandler.cpp
1>c:\documents and settings\administrator\桌面\myadapter\myadapter\MyHandler.h(19) : error C2065: “IID_IMyHandler”: 未声明的标识符
1>c:\documents and settings\administrator\桌面\myadapter\myadapter\MyHandler.h(19) : error C2065: “LIBID_MyAdapterLib”: 未声明的标识符
1>c:\documents and settings\administrator\桌面\myadapter\myadapter\MyHandler.h(19) : error C2955: “ATL::IDispatchImpl”: 使用类 模板 需要 模板 参数列表
1>        C:\Program Files\Microsoft Visual Studio 9.0\VC\ce\atlmfc\include\atlcom.h(4624) : 参见“ATL::IDispatchImpl”的声明
1>c:\documents and settings\administrator\桌面\myadapter\myadapter\MyHandler.h(21) : fatal error C1903: 无法从以前的错误中恢复;正在停止编译

在MyHandler.h里加入以下头文件

#include "MyAdapter.h"

MyAdapter.h为idl文件自动生成的文件,它有对CLSID及类IMyHanlder的定义(当然我们也要以直接引用MyAdapter_i.c):

EXTERN_C const IID IID_IMyHandler;EXTERN_C const IID LIBID_MyAdapterLib;EXTERN_C const CLSID CLSID_MyHandler;  MIDL_INTERFACE("74DC3D5F-CE91-4E88-93C7-6E2D111B18BE")    IMyHandler : public IDispatch    {    public:    };

 

=====================

终于编译生成MyAdapter.dll了

5、在MyAdapter.rc文件里有 IDR_MYADAPTER REGISTRY "MyHandler.rgs" ,说明此rgs文件是嵌入到资源中的,它是为了注册用。

 

三、在VC中使用编译出来的COM组件

法一、导入MyAdapter.dll的方法(这种方法不推荐,因为完全不采用COM的特性了)

1、在新建的工程MyLogger 的 test.cpp时添加

#import "../MyAdapter/MyAdapter/CE2443I (ARMV4I)/Debug/MyAdapter.dll" no_namespace

目的是告诉编译器去

C:\Documents and Settings\Administrator\Local Settings\Temp

目录下找myadapter.tlh。这样我们就可以不引用.h文件,这样在用到IMyHandlerPtr类时就不会报以下错误了:

error C2065: “IMyHandlerPtr”: 未声明的标识符

 

2、接下来就是应用了

typedef HRESULT (*REGISTER)(void);	typedef HRESULT (*UNREGISTER)(void);	bool bRet	= false;	HMODULE hModule = LoadLibrary(L"MyAdapter.dll");	if (NULL != hModule)	{		REGISTER DllRegisterServer = (REGISTER)GetProcAddress(hModule, _T("DllRegisterServer"));		UNREGISTER DllUnregisterServer = (REGISTER)GetProcAddress(hModule, _T("DllUnregisterServer"));			if (NULL != DllRegisterServer && NULL != DllUnregisterServer)		{			DllUnregisterServer();			bRet = (S_OK == DllRegisterServer());		}		HRESULT hr = spMyHandler.CreateInstance(L"MyAdapter.MyHandler");	// 使用 ProgID		bRet = SUCCEEDED(hr);	}	spMyHandler->ScanBaudRate(2);

 

我们是采用ProgID来获得接口的,ProgID是一个字符串,采用  库名.类名.版本   的形式:

(1)库名是.reg文件中定义的AppID,eg:

HKCR{	NoRemove AppID	{		'%APPID%' = s 'MyAdapter'		'MyAdapter.DLL'		{			val AppID = s '%APPID%'		}	}}

 (2)类名是接口的类名。

但是通过ProgID得到接口的方法是不好的,因为:

过名字创建对象可能会出现冲突;而通过uuid创建对象,这个uuid是唯一的。两种都是符合COM规范的。如在JS 中使用第一种方式创建COM对象的。

通过COM API函数CLSIDFromProgID和ProgIDFromCLSID,客户可以在ProgID和CLSID两者之间进行转换,这两个函数的原形如

下:

HRESULT CLSIDFromProgID([in,string]LPCOLESTR lpszProgID,[out] LPCLSID pclsid);HRESULT ProgIDFromCLSID([in]REFCLSID clsid,[out,string]LPOLESTaR* lplpszProgID);

为了把一个ProgID转换为CLSID,我们只需简单的调用CLSIDFromProgID:

HRESULT GetCGorillaCLSID(CLSID& rclsid){const OLECHAR wszProgID[] = OLESTR("Apes.Gorilla.l");return CLSIDFromProgID(wszProgID,&rclsid);}
改进:

所以我们可以把项目中的

HRESULT hr = spMyHandler.CreateInstance(L"MyAdapter.MyHandler");	// 使用 ProgID

改为:

CLSID clsid;	CLSIDFromProgID(OLESTR("MyAdapter.MyHandler"),&clsid);	spMyHandler.CreateInstance(clsid);
法二、通过CLSID找到接口

在WCE中使用CoInitialilze或CoCreateInstance等COM接口时得先包含头文件:

#include 

不然会提示找不到标识符。

const IID IID_IMyHandler = {0x74DC3D5F,0xCE91,0x4E88,{0x93,0xC7,0x6E,0x2D,0x11,0x1B,0x18,0xBE}};	const IID LIBID_MyHandlerLib = {0x9F897696,0x196F,0x476C,{0xBC,0xDE,0xF5,0x1A,0x47,0x28,0xD3,0xB9}};	const CLSID CLSID_MyHandler = {0xD966A194,0xDC7E,0x4648,{0x96,0x54,0xAA,0x87,0x2B,0xB8,0x4B,0x2B}};	IUnknown *pUnknown;	HRESULT hr = CoInitialize(NULL);	if(FAILED(hr))	{ 		return FALSE;	}	// 创建对象实例,并返回IUnknown 指针	hr = CoCreateInstance(CLSID_MyHandler, NULL, 		CLSCTX_LOCAL_SERVER , IID_IUnknown, (void**)&pUnknown);	if(FAILED(hr))	{		return FALSE;	}	//通过IUnkonwn指针去查询接口指针,返回IMyHandlerPtr指针	hr = pUnknown->QueryInterface(IID_IMyHandler,(void**)&spMyHandler);	if(FAILED(hr))	{		return FALSE;	}	pUnknown->Release();

 

你可能感兴趣的文章
布隆过滤器过时了,未来属于布谷鸟过滤器?
查看>>
面试题 —— 数字幻方
查看>>
5折抢购最后一天 | 戴尔顶级配置电脑,限时秒!
查看>>
SpringBoot 究竟是如何跑起来的?
查看>>
阿里开源限流组件 Sentinel 集群流控全解析
查看>>
深度解密HTTP通信细节
查看>>
日活亿级用户的服务器架构要怎么搭?
查看>>
MySQL 是怎样运行的:从根儿上理解 MySQL
查看>>
开源搜索技术的核心引擎 —— Lucene
查看>>
码洞技术文章大全
查看>>
RPC 服务器之【多进程描述符传递】高阶模型
查看>>
程序员年龄增大后的职业出路是什么?
查看>>
快学 Go 语言 第 1 课 —— Hello World
查看>>
《快学 Go 语言》第 4 课 —— 低调的数组
查看>>
作为程序员,你是如何在工作以后找到女朋友的?
查看>>
Channel最佳实践之基本规则【译】
查看>>
天下无难试之HTTP协议面试刁难大全
查看>>
深入Python多进程编程基础
查看>>
深入理解RPC——RPC在企业服务中的核心价值
查看>>
Lettuce快速入门
查看>>