com技术内幕--读书笔记(7)

第7章 类厂

相信有了前六章的知识积累,学些以后的章节将会很顺利。本章实现了一个真正的COM组件,并通过client客户端来使用这个组件。学完这章,对COM组件的最基本的东西就已经学完了,可以实现一个真正意义上的组件,后续几章就是在此基础上进行的升华,例如组件的复用,EXE中的组件,多线程等等,也同样重要。


本章将介绍类厂,类厂是能够创建其他组件的组件,并且可以使用同一个类厂来创建不同的CLSID所对应的组件,也就是说,在同一个DLL中,可以包含多个组件(组件是用C++类来实现的),并可以使用同一个类厂来创建它们,我想这就是类厂名字的由来。


CoCreateInstance函数是创建组件并获得IUnknown接口,虽然在使用过程中并没有使用类厂,但是它也是按照一定方法通过类厂来创建组件的。

CoCreateInstance

CoCreateInstance函数是COM库的函数,函数原型如下

HRESULT __stdcall CoCreateInstance(const CLSID &clsid, 
		                   IUnknown *pIUnkownOuter, //Outer Component
	                           DWORD dwClsContext,      //Server Context
                                   const IID &iid,
		                   void **ppv);

其中前四个是输入参数,最后一个是输出参数。第一个参数clsid是所创建组件的CLSID,第二个参数pIUnkownOuter是聚合组件需要用的,将在第8章介绍,第三个参数dwClsContext是限定所创建组件的执行上下文,第四个参数iid是所创建组件的接口的IID,第五个参数ppv将返回该接口的指针。

dwClsContext

dwClsContext可以控制所创建是与客户在相同的进程中运行,还是在不同的进程中运行,或者是在另外一台机器上运行。此参数可以是如下值的组合

CLSCTX_INPROC_SERVER         客户希望创建在同一进程中运行的组件,因此组件必须是在DLL中实现。

CLSCTX_INPROC_HANDLER      客户希望创建进程中处理器。

CLSCTX_LOCAL_SERVER           客户希望创建一个在同一机器上的另外一个进程中运行的组件。组件是由EXE实现的。

CLSCTX_REMOTE_SERVER       客户希望创建一个在远程机器上运行的组件。分布式COM组件。

在OBJBASE.H中定义了一些上述值的组合。


CoCreateInstance的具有一定的不灵活性,解决问题的办法是使用专门用于创建所需组件的组件,这个组件就是类厂。


类厂

某个特定的类厂可以创建某个特定CLSID相对应的组件,客户可以通过类厂提供的接口来对组件的创建过程进行控制。客户使用CoCreateInstance所创建的组件实际上是通过类厂的IClassFactory创建的,使用类厂创建组件的步骤是首先创建类厂,然后使用IClassFactory创建所需的组件。

1.创建类厂

COM库函数CoGetClassObject创建同某个CLSID相应的类厂。函数原型如下

HRESULT __stdcall CoGetClassObject(const CLSID &clsid,
		                   DWORD dwClsContext,
                                   COSERVERINFO *pServerInfo,
                                   const IID &iid,
                                   void **ppv);

同CoCreateInstance非常相似。第一个参数const CLSID&待创建组件的CLSID,第二个参数DWORD dwClsContext是待创建组件的执行上下文,第三个参数COSERVERINFO*用于远程组件的访问,将在第10章讨论,第四个参数const IID&是IClassFactory接口的IID,第五个参数返回IClassFactory接口的指针。

2.类厂组件

IClassFactory

大多数组件是使用IClassFactory来创建的,原型如下

interface IClassFactory:IUnknown
{
	HRESULT __stdcall CreateInstance(IUnkown *pUnknownOuter, const IID &id, void **ppv);
	HRESULT __stdcall LockServer(BOOL bLock);
};

IClassFactory::CreateInstance函数的第一个参数IUnknown*是组件聚合使用的,将在第8章介绍,后两个参数跟CoCreateInstance后两个参数作用相同,将在创建组件的

同时返回此组件的某个接口指针。可以看到IClassFactory::CreateInstance并没有接收一个CLSID参数,这意味着此函数将只能创建同某个CLSID——即传给CoGetClassObject的参数CLSID相应的组件。


IClassFactory::CreateInstance和DllGetClassObject的实现是相同的,这两个函数都将创建一个组件然后向它查询某个接口。


在两种情况下使用创建类厂再创建组件的方法,而不是直接使用CoCreateInstance的方法直接创建组件

(1)想使用IClassFactory2来创建组件。IClassFactory2是Microsoft定义的另外一个接口,此接口在IClassFactory的基础上增加了获取组件接口的许可权限功能。

(2)需要创建一个组件的多个实例。这样只需创建相应的类厂一次,而CoCreateInstance需要为每一个组件实例分别创建并释放相应的类厂。

类厂的特性

(1)类厂将只能给你创建同某个CLSID相应的组件。

(2)与某个特定CLSID相应的类厂是由组件开发人员来实现的。大多数情况下,类厂组件包含在它所创建的组件的同一个的DLL中。

类厂的创建

客户通过CoGetClassObject来创建类厂,这就需要在组件的DLL中实现一个特定的函数,此函数名为DllGetClassObject,由COM库函数CoGetClassObject调用,函数原型如下

STDAPI DllGetClassObject(const CLSID &clsid, const IID &iid, void **ppv);
函数的三个参数同CoGetClassObject中传入的参数的意义相同。

通过类厂来创建组件的示意图如下,COM库函数CoGetClassObject将根据传入参数CLSID查询注册表,装载组件所在的DLL库。

首先客户调用CoGetClassObject,COM库实现CoGetClassObject函数,组件所在的DLL输出DllGetClassObject函数由CoGetClassObject函数调用,它返回IClassFactory指针,客户调用IClassFactory来创建相应的组件。IClassFactory如何实现,也是由组件的创建者来实现的。




组件的注册

实现组件的DLL中输出四个函数,除了DllGetClassObject外,DllRegisterServer和DllUnregisterServer将用于组件在注册表中注册和取消注册(链接的时候需要链接Advapi32.lib),调用regsvr32来完成注册和取消。

类厂的复用

在设计类厂和组件的时候,可以做到只用一个类厂的实现来完成所有组件的创建。将在第9章中实现。但即使是这样,类厂CFactory的一个实例也仅能创建一个同某个CLSID相应的组件。


DLL的卸载

利用类厂创建组件,那什么时候卸载组件呢,卸载组件需要两个引用计数,一个是类厂的计数,一个是组件的计数。


COM库中实现了一个CoFreeUnusedLibraries的函数,以释放那些不再需要使用的DLL库所占用的内存,由组件的客户进行调用。

DllCanUnloadNow函数

在实现组件的DLL中的输出函数。使用者应该周期性调用CoFreeUnusedLibraries函数,它将调用组件的DllCanUnloadNow函数,以确定Dll是否可以被卸载。代码中g_lComponents的作用是对组件计数,因为DllCanUnloadNow是个全局函数,而且一个DLL中可能有一个类厂,但是多个组件。所以需要一个全局变量来对组件进行计数。可以看到IClassFactory::CreateInstance也就是说在创建其中一个组件的时候,组件的构造函数将g_lComponents增大,组件的析构函数将g_lComponents的值减小。若g_lComponets值为0,CoFreeUnusedLibraries可以将包含组件的DLL卸载掉。


此处需要分清组件的引用计数m_cRef和g_lComponent的关系。组件的引用计数,是一个组件自己的引用计数,调用AddRef和Release对它进行加减,控制这个组件的创建和析构。而g_lComponent是Dll的引用计数,是所有组件的计数,当一个组件创建的时候+1,析构的时候-1,当所有组件(不包括类厂)都析构完成后,g_lComponent==0;


LockServer函数

使用g_lComponents只是对DLL中的组件进行了记数,对DLL中的另一个组件类厂并没有记数。对类厂的记数使用了IFactory::LockServer函数,组件Server内部设置了另外一个与g_IComponents不同的计数值进行计数。(将在第10章进行讨论,主要原因是因为第10章将会讲到的进程外组件(exe实现的)的类厂无法像进程中的组件(dll实现的)一样方便的进行记数)。DllCanUnloadNow函数,需要对这两个数值同时进行统计,以确定组件及类厂所在的DLL是否可以卸载掉。


本章代码

组件端:

cmpnt.cpp

//
//cmpnt.cpp
//use: cl /LD cmpnt.cpp guids.cpp registry.cpp cmpnt.def uuid.lib ole32.lib Advapi32.lib 
//     regsvr32 -s cmpnt.dll

#include <objbase.h>
#include "iface.h"    //interface declarations
#include "Registry.h"  //Registry helper function
#include <iostream>
#include <string>
using namespace std;

//trace function
void trace(string msg)
{
    cout<<msg<<endl;
} 

//global variable
//
static HMODULE g_hModule = NULL;        //DLL Module handle
static long    g_lComponent = 0;        //Count of active component
static long    g_lServerLocks = 0;      //Count of locks

//Friendly name of component
const char g_szFriendlyName[] = "InsideCOM Chapter 7 Example";

//Version independent ProgID
const char g_szVerIndProgID[] = "InsideCOM.Chap07";

//ProgID
const char g_szProgID[] = "InsideCOM.Chap07.1";

//Component
class CA:public IX, public IY
{
public:
    //IUnknown
    virtual HRESULT __stdcall QueryInterface(const IID &iid, void **ppv);
    virtual ULONG   __stdcall AddRef();
    virtual ULONG   __stdcall Release();
    virtual void    __stdcall Fx() {cout<<"Fx"<<endl;}
    virtual void    __stdcall Fy() {cout<<"Fy"<<endl;}
    CA();
    ~CA();
protected:
    long m_cRef;
};

CA::CA()
{
    m_cRef = 1;   //注意这里与前几章不一样,m_cRef=1意味着只要CA组件被创建就计数为1,这样IFactory::CreateInstance创建CA组
                  //件时,就不用调用AddRef()了
    InterlockedIncrement(&g_lComponent);
}

CA::~CA()
{
    InterlockedDecrement(&g_lComponent);
    trace("Component:destroy self");
}

//IUnknown implement
HRESULT __stdcall CA::QueryInterface(const IID &iid, void **ppv)
{
    if(iid == IID_IUnknown)
    {
        *ppv = static_cast<IX*>(this);
    }
    else if(iid == IID_IX)
    {
        *ppv = static_cast<IX*>(this);
        trace("component: return pointer to ix");
    }
    else if(iid == IID_IY)
    {
        *ppv = static_cast<IY*>(this);
        trace("component: return pointer to iy");
    }
    else
    {
        *ppv = NULL;
        return E_NOINTERFACE;
    }
    reinterpret_cast<IUnknown*>(*ppv)->AddRef();
    return S_OK;
}

ULONG __stdcall CA::AddRef()
{
    return InterlockedIncrement(&m_cRef);
}

ULONG __stdcall CA::Release()
{
    if(InterlockedDecrement(&m_cRef)== 0)
    {
        delete this;
        return 0;
    }
    return m_cRef;
}

///////////////////////////////////////////
//class factory
//
class CFactory:public IClassFactory
{
public:
    //iunknown
    virtual HRESULT __stdcall QueryInterface(const IID &iid, void **ppv);
    virtual ULONG   __stdcall AddRef();
    virtual ULONG   __stdcall Release();
    //interface IClassFactory
    virtual HRESULT __stdcall CreateInstance(IUnknown *pUnknownOuter, const IID &iid, void **ppv);
    virtual HRESULT __stdcall LockServer(BOOL bLock);
    //constructor
    CFactory():m_cRef(1){}
    //destructor
    ~CFactory()
    {
        trace("class factory :destory self");
    }
private:
    long m_cRef;
};

//
//class factory IUnknown implementation
//
HRESULT __stdcall CFactory::QueryInterface(const IID &iid, void **ppv)
{
    if((iid == IID_IUnknown) || (iid == IID_IClassFactory))
    {
        *ppv = static_cast<IClassFactory*>(this);
    }
    else
    {
        *ppv = NULL;
        return E_NOINTERFACE;
    }
    reinterpret_cast<IUnknown*>(*ppv)->AddRef();
    return S_OK;
}

ULONG __stdcall CFactory::AddRef()
{
    return InterlockedIncrement(&m_cRef);
}

ULONG __stdcall CFactory::Release()
{
    if(InterlockedDecrement(&m_cRef) == 0)
    {
        delete this;
        return 0;
    }
    return m_cRef;
}

//
//IClassFactory implementation
//
HRESULT __stdcall CFactory::CreateInstance(IUnknown *pUnknownOuter, const IID &iid, void **ppv)
{
    trace("class factory : create component");
    //Can not aggregate
    if(pUnknownOuter != NULL)
    {
        return CLASS_E_NOAGGREGATION;
    }
    //create component
    CA *pA = new CA();
    if(pA == NULL)
    {
        return E_OUTOFMEMORY;
    }
    //Get requested interface
    HRESULT hr = pA->QueryInterface(iid, ppv);

    //Release the IUnknown pointer
    pA->Release();
    return hr;
}

//lock server
HRESULT __stdcall CFactory::LockServer(BOOL bLock)
{
    if(bLock)
    {
        InterlockedIncrement(&g_lServerLocks);
    }
    else
    {
        InterlockedDecrement(&g_lServerLocks);
    }
    return S_OK;
}

/////////////////////////////////////////////
//export function
//
//get class factory
STDAPI DllGetClassObject(const CLSID &clsid, const IID &iid, void **ppv)
{
    trace("DllGetClassObeject: create class factory");
    if(clsid != CLSID_Component1)
    {
        return CLASS_E_CLASSNOTAVAILABLE;
    }
    //create class factory
    CFactory *pFactory = new CFactory;
    if(pFactory == NULL)
    {
        return E_OUTOFMEMORY;
    }
    HRESULT hr = pFactory->QueryInterface(iid, ppv);
    pFactory->Release();
    return hr;
}

STDAPI DllCanUnloadNow()
{
    if((g_lComponent == 0) && (g_lServerLocks == 0))
    {
        return S_OK;
    }
    else
    {
        return S_FALSE;
    }
}

//server registration
STDAPI DllRegisterServer()
{
    return RegisterServer(g_hModule,
                          CLSID_Component1,
                          g_szFriendlyName,
                          g_szVerIndProgID,
                          g_szProgID);
}

//server unregistration
STDAPI DllUnregisterServer()
{
    return UnregisterServer(CLSID_Component1,
                            g_szVerIndProgID,
                            g_szProgID);
}

BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD dwReason, void *lpReserved)
{
    if(dwReason == DLL_PROCESS_ATTACH)
    {
        g_hModule = hModule;
    }
    return TRUE;
}

 

cmpnt.def

LIBRARY         Cmpnt.dll
DESCRIPTION     'Chapter 7 Example COM Component'

EXPORTS			DllGetClassObject   @2	PRIVATE
                DllRegisterServer   @3	PRIVATE
                DllUnregisterServer @4	PRIVATE
				DllCanUnloadNow     @5	PRIVATE

registry.h

#ifndef _REGISTRY_H_
#define _REGISTRY_H_

HRESULT RegisterServer(HMODULE hModule, 
				       const CLSID &clsid,
					   const char *szFriendlyName,
					   const char *szVerIndProgID,
					   const char *szProgID);
HRESULT UnregisterServer(const CLSID &clsid,
				         const char *szVerIndProgID,
						 const char *szProgID);
#endif

registry.cpp

//
//registry.cpp
//
#include <objbase.h>
#include <cassert>
#include "registry.h"

//
//Internal functions 

//Set the key and its value
BOOL SetKeyAndValue(const char *szKey, const char *szSubKey, const char *szValue);

//Convert a clsid to a char string
void CLSIDtochar(const CLSID &clsid, char *szClsID, int nLength);

//Delete szKeyChild and all its child
LONG RecursiveDeleteKey(HKEY hKeyParent, const char *szKeyChild);

const int CLSID_STRING_SIZE = 39 ;

//
// Register the component in the registry.
//
HRESULT RegisterServer(HMODULE hModule,            // DLL module handle
                       const CLSID& clsid,         // Class ID
                       const char* szFriendlyName, // Friendly Name
                       const char* szVerIndProgID, // Programmatic
                       const char* szProgID)       //   IDs
{
	char szModule[512] ;
	DWORD dwResult =::GetModuleFileName(hModule, szModule, sizeof(szModule)/sizeof(char));

	assert(dwResult != 0) ;

	// Convert the CLSID into a char.
	char szCLSID[CLSID_STRING_SIZE] ;
	CLSIDtochar(clsid, szCLSID, sizeof(szCLSID)) ;

	// Build the key CLSID\\{...}
	char szKey[64] ;
	strcpy(szKey, "CLSID\\") ;
	strcat(szKey, szCLSID) ;
  
	// Add the CLSID to the registry.
	SetKeyAndValue(szKey, NULL, szFriendlyName) ;

	// Add the server filename subkey under the CLSID key.
	SetKeyAndValue(szKey, "InprocServer32", szModule) ;

	// Add the ProgID subkey under the CLSID key.
	SetKeyAndValue(szKey, "ProgID", szProgID) ;

	// Add the version-independent ProgID subkey under CLSID key.
	SetKeyAndValue(szKey, "VersionIndependentProgID",
	               szVerIndProgID) ;

	// Add the version-independent ProgID subkey under HKEY_CLASSES_ROOT.
	SetKeyAndValue(szVerIndProgID, NULL, szFriendlyName) ; 
	SetKeyAndValue(szVerIndProgID, "CLSID", szCLSID) ;
	SetKeyAndValue(szVerIndProgID, "CurVer", szProgID) ;

	// Add the versioned ProgID subkey under HKEY_CLASSES_ROOT.
	SetKeyAndValue(szProgID, NULL, szFriendlyName) ; 
	SetKeyAndValue(szProgID, "CLSID", szCLSID) ;

	return S_OK ;
}

//
// Remove the component from the registry.
//
LONG UnregisterServer(const CLSID& clsid,         // Class ID
                      const char* szVerIndProgID, // Programmatic
                      const char* szProgID)       //   IDs
{
	// Convert the CLSID into a char.
	char szCLSID[CLSID_STRING_SIZE] ;
	CLSIDtochar(clsid, szCLSID, sizeof(szCLSID)) ;

	// Build the key CLSID\\{...}
	char szKey[64] ;
	strcpy(szKey, "CLSID\\") ;
	strcat(szKey, szCLSID) ;

	// Delete the CLSID Key - CLSID\{...}
	LONG lResult = RecursiveDeleteKey(HKEY_CLASSES_ROOT, szKey) ;
	assert( (lResult == ERROR_SUCCESS) || (lResult == ERROR_FILE_NOT_FOUND) ) ; // Subkey may not exist.

	// Delete the version-independent ProgID Key.
	lResult = RecursiveDeleteKey(HKEY_CLASSES_ROOT, szVerIndProgID) ;
	assert((lResult == ERROR_SUCCESS) ||
	       (lResult == ERROR_FILE_NOT_FOUND)) ; // Subkey may not exist.

	// Delete the ProgID key.
	lResult = RecursiveDeleteKey(HKEY_CLASSES_ROOT, szProgID) ;
	assert((lResult == ERROR_SUCCESS) ||
	       (lResult == ERROR_FILE_NOT_FOUND)) ; // Subkey may not exist.

	return S_OK ;
}


BOOL SetKeyAndValue(const char *szKey, const char *szSubKey, const char *szValue)
{
	HKEY hKey;
	char szKeyBuf[1024] ;

	// Copy keyname into buffer.
	strcpy(szKeyBuf, szKey) ;

	// Add subkey name to buffer.
	if (szSubKey != NULL)
	{
		strcat(szKeyBuf, "\\") ;
		strcat(szKeyBuf, szSubKey ) ;
	}

	// Create and open key and subkey.
	long lResult = RegCreateKeyEx(HKEY_CLASSES_ROOT ,
	                              szKeyBuf, 
	                              0, NULL, REG_OPTION_NON_VOLATILE,
	                              KEY_ALL_ACCESS, NULL, 
	                              &hKey, NULL) ;
	if (lResult != ERROR_SUCCESS)
	{
		return FALSE ;
	}

	// Set the Value.
	if (szValue != NULL)
	{
		RegSetValueEx(hKey, NULL, 0, REG_SZ, 
		              (BYTE *)szValue, 
		              strlen(szValue)+1) ;
	}

	RegCloseKey(hKey) ;
	return TRUE ;
	
}


LONG RecursiveDeleteKey(HKEY hKeyParent, const char *szKeyChild)
{
	// Open the child.
	HKEY hKeyChild ;
	LONG lRes = RegOpenKeyEx(hKeyParent, szKeyChild, 0,
	                         KEY_ALL_ACCESS, &hKeyChild) ;
	if (lRes != ERROR_SUCCESS)
	{
		return lRes ;
	}

	// Enumerate all of the decendents of this child.
	FILETIME time ;
	char szBuffer[256] ;
	DWORD dwSize = 256 ;
	while (RegEnumKeyEx(hKeyChild, 0, szBuffer, &dwSize, NULL,
	                    NULL, NULL, &time) == S_OK)
	{
		// Delete the decendents of this child.
		lRes = RecursiveDeleteKey(hKeyChild, szBuffer) ;
		if (lRes != ERROR_SUCCESS)
		{
			// Cleanup before exiting.
			RegCloseKey(hKeyChild) ;
			return lRes;
		}
		dwSize = 256 ;
	}

	// Close the child.
	RegCloseKey(hKeyChild) ;

	// Delete this child.
	return RegDeleteKey(hKeyParent, szKeyChild) ;
}

void CLSIDtochar(const CLSID &clsid, char *szClSID, int nLength)
{
	assert(nLength >= CLSID_STRING_SIZE) ;
	// Get CLSID
	LPOLESTR wszCLSID = NULL ;
	HRESULT hr = StringFromCLSID(clsid, &wszCLSID) ;
	assert(SUCCEEDED(hr)) ;

	// Covert from wide characters to non-wide.
	wcstombs(szClSID, wszCLSID, nLength);

	// Free memory.
	CoTaskMemFree(wszCLSID) ;
}

iface.h

//iface.h
//
#include <objbase.h>
interface IX:IUnknown
{
	virtual void __stdcall Fx() = 0;
};

interface IY:IUnknown
{
	virtual void __stdcall Fy() = 0;
};

interface IZ:IUnknown
{
	virtual void __stdcall Fz() = 0;
};

extern const IID   IID_IX;
extern const IID   IID_IY;
extern const IID   IID_IZ;
extern const CLSID CLSID_Component1;
 

guids.cpp

//
// GUIDs.cpp
// 
#include <objbase.h>

// {32bb8320-b41b-11cf-a6bb-0080c7b2d682}
extern const IID IID_IX = 
    {0x32bb8320, 0xb41b, 0x11cf,
    {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;

// {32bb8321-b41b-11cf-a6bb-0080c7b2d682}
extern const IID IID_IY = 
    {0x32bb8321, 0xb41b, 0x11cf,
    {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;

// {32bb8322-b41b-11cf-a6bb-0080c7b2d682}
extern const IID IID_IZ = 
    {0x32bb8322, 0xb41b, 0x11cf,
    {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;

// {0c092c21-882c-11cf-a6bb-0080c7b2d682}
extern const CLSID CLSID_Component1 =
    {0x0c092c21, 0x882c, 0x11cf,
    {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;

 

客户端

clients.cpp

//
//Client.cpp - client implementation
//use:cl client.cpp guids.cpp uuid.lib ole32.lib
//
#include <objbase.h>
#include "iface.h"
#include <iostream>
#include <string>
using namespace std;

void trace(string msg)
{
    cout<<msg<<endl;
}

int main(void)
{
    CoInitialize(NULL);
    trace("client:call CoCreateInstance to create component and get interface ix");
    IX *pIx = NULL;
    HRESULT hr = ::CoCreateInstance(CLSID_Component1, NULL, CLSCTX_INPROC_SERVER, IID_IX, (void**)&pIx);
    if(SUCCEEDED(hr))
    {
        trace("client:Succeeded getting IX");
        pIx->Fx();
        trace("client:Ask for interface IY");
        IY *pIy = NULL;
        hr = pIx->QueryInterface(IID_IY, (void**)&pIy);
        if(SUCCEEDED(hr))
        {
            trace("client:Succeeded getting IY");
            pIy->Fy();
            pIy->Release();
            trace("client:Release IY interface");
        }
        else
        {
            trace("client:Could not get interface IY");
        }
        trace("client:Ask for interface IZ");
        IZ *pIz = NULL;
        hr = pIx->QueryInterface(IID_IZ, (void**)&pIz);
        if(SUCCEEDED(hr))
        {
            trace("client:Succeeded getting IZ");
            pIz->Fz();
            pIz->Release();
            trace("client:Release IZ interface");
        }
        else
        {
            trace("client:Could not get interface IZ");
        }
        trace("client:Release IX interface");
        pIx->Release();
    }
    else
    {
        cout<<"Client: Could not create component.hr ="<<hex<<hr<<endl;
    }
    CoUninitialize();
    return 0;
}



运行结果

com技术内幕--读书笔记(7)_第1张图片

你可能感兴趣的:(null,读书,delete,dll,interface)