转载请标明是引用于 http://blog.csdn.net/chenyujing1234
例子代码:
(为WCE SDK下的例子,转为win32,自己移植)
http://www.rayfile.com/zh-cn/files/c638241c-df8f-11e1-90c1-0015c55db73d/
不要把COM看成是DLL,DLL只是用来装载COM的,但是COM不一定要用DLL作载体,也可以用exe或者其他的。
而COM组件的调用是通过接口,只有通过CreateInstance获得接口指针,才可以使用该接口。所有的一切调用都是通过接口实现。
COM是更好的DLL,所以COM是可以通过传统的DLL调用方法来实现的。 COM里有一全局导出函数 DllGetClassObject.
COM库在加载COM组件时也是 LoadLibrary GetProcAddress( "DllGetClassObject ") 开始的,只不过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里建类
// GPSHandler class 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文件是嵌入到资源中的,它是为了注册用。
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);
在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();