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

转载请标明是引用于 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里建类

// 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文件是嵌入到资源中的,它是为了注册用。

 

三、在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 <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();


 

你可能感兴趣的:(Microsoft,null,dll,processing,include,interface)