采用ATL模型代替lib dll 的调用

来源:互联网 发布:大型网络数据中心 编辑:程序博客网 时间:2024/06/06 05:43

转载请标明是引用于 http://blog.csdn.net/chenyujing1234

 例子代码:

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

http://www.rayfile.com/zh-cn/files/c638241c-df8f-11e1-90c1-0015c55db73d/ 

 一、调用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<MyHandler, &IID_IMyHandler, &LIBID_MyAdapterLib>, public ISupportErrorInfo,public CComObjectRoot,public CComCoClass<MyHandler,&CLSID_MyHandler>{


编译提示:

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");// 使用 ProgIDbRet = 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 <winx.h>

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

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();


 

原创粉丝点击