转载请标明是引用于 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,
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文件是嵌入到资源中的,它是为了注册用。
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
不然会提示找不到标识符。
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();