HRESULT值的使用比典型的布尔值要麻烦一点,当然也更能提供一些额外信息:成功代码有多个,失败的代码也有多个;失败代码可能会发生变化。
错误代码的直接传递会造成一些客户不识别错误代码的情况,例子在书上85页有一个有力的说明,可以参考。
对于自定义的HRSULT的一些一般性规:不要将0x0000及0x01FF范围内的值作为返回代码,因为这些值是为COM所定义的FACILITY_ITF而保留的;不要传播FACILITY_ITF错误代码;尽可能地使用通用的COM成功及失败代码;避免定义自己的HRESULT,而可以在函数中使用一个输出函数。
GUID的结构不是什么重点,只需要知道创建GUID的函数可以有足够的理由创建一个唯一的串值GUID,与时间空间有关。
Windows注册表的键值结构,也没有什么难点,看着就懂了。注意CLSID的子关键字InprocServer32指示了dll所在的全路径;ProgID对应CLSID,以及反过来用CLSID对应ProgID的名称原理以及两个方向获取的函数:CLSIDFromProgID, ProgIDFromCLSID。
注册过程需要实现的两个接口函数:DllRegisterServer() 和 DllUnregisterServer()。这两个函数主要处理上面说到的CLSID键与ProgID键的创建和对应关系。当然就使用到注册表操作的API,这也不是难点。
组件类别的实现:这里只有一个小段来描述:组件类别最吸引人的地方可能是开发人员无需要自己完成注册表的处理。这些工作可以由Windows附带的一个名为Component Category Manager来完成。程序代码中有对应的两个接口类的使用示范:ICatRegister,ICatInformation。按个人理解,先不使用这些函数完成注册,才能深入理解注册过程,后续使用可以使用这种函数。
OleView给用户提供关于注册表信息中一个更为高级的视图,在VS自带的程序例子中有,我已经跟踪调试好了,发上来供参考。
COM库函数。对于这些所谓的初始化函数,我觉得是很鸡肋的说明,告诉你开始的时候需要初始化,结束需要释放——为什么不把原理讲明白?是因为需要分配什么内存么?还是加载一些系统资源?书上没有说,当然我就觉得这一节像是废话。
附组件类别实现代码(原书代码版本在新的开发环境上需要修改的细节,目前在vs2008上编译通过)
//
// Category.cpp -
// Component category demonstration program
//
// This program is not designed to be compiled for UNICODE.
#include <stdlib.h>
#include <iostream>
#include <assert.h>
#include <tchar.h>
#include <comcat.h>
using namespace std;
/
//
// Function declarations
//
// Worker functions
// List the registered component categories.
BOOL ListCategories() ;
// Register our sample component category.
BOOL RegisterCategory() ;
// Unregister our sample component category.
void UnregisterCategory() ;
// Add component to our sample component category.
void AddComponent() ;
// Remove component from our sample component category.
void RemoveComponent() ;
// List all of the components which are members of our sample category.
void ListCategoryMembers() ;
// Helper functions
// Print out the COM/OLE error string for an HRESULT.
void ErrorMessage(const char* str, HRESULT hr) ;
// Get friendly name for a CLSID from the Registry and display.
void OutputFriendlyName(const CLSID& clsid) ;
/
//
// Global Data and constants
//
// Global interface pointers
ICatInformation* g_pICatInformation = NULL ;
ICatRegister* g_pICatRegister = NULL ;
// Array of category IDs
// {f2484d60-e8d0-11cf-a6bb-0080c7b2d682}
static GUID CATID_SampleCategories[1] =
{0xf2484d60, 0xe8d0, 0x11cf,
{0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;
// Category Information structure used to register categories
CATEGORYINFO g_SampleCategoryInfo =
{
{0xf2484d60, 0xe8d0, 0x11cf,
{0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}}, // CATID catid ;
LOCALE_SYSTEM_DEFAULT, // LCID lcid ;
L"This is the sample category" // OLECHAR szDescription[128] ;
} ;
// Component to add to the sample category defined above
// {0c092c20-882c-11cf-a6bb-0080c7b2d682}
extern "C" const CLSID CLSID_Component1 =
{0x0c092c20, 0x882c, 0x11cf,
{0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;
/
//
// main
//
int main()
{
// Initialize COM Library.
OleInitialize(NULL) ;
// Create the standard COM Category manager.
HRESULT hr = ::CoCreateInstance(CLSID_StdComponentCategoriesMgr,
NULL,
CLSCTX_ALL,
IID_ICatInformation,
(void**)&g_pICatInformation) ;
if (FAILED(hr))
{
ErrorMessage("main: Could not create component category manager.", hr) ;
goto Uninitialize ;
}
// List the categories.
if (!ListCategories())
{
goto Release ;
}
// Get the Category Registration interface.
hr = g_pICatInformation->QueryInterface(IID_ICatRegister,
(void**)&g_pICatRegister) ;
if (FAILED(hr))
{
ErrorMessage("Attempt to query for ICatRegister failed.", hr) ;
goto Release ;
}
//
// Register our sample component category.
//
cout << "--------Register---------------" << endl ;
if (!RegisterCategory())
{
goto Release ;
}
// List the categories again to show the newly registered category.
ListCategories() ;
// Add component to our sample category.
cout << "--------Add Component----------" << endl ;
AddComponent() ;
// List all components implementing our sample category.
cout << "--------List Category----------" << endl ;
ListCategoryMembers();
// Remove class from our sample category.
cout << "--------Remove Component-------" << endl ;
RemoveComponent();
// List all components implementing our sample category.
// Should be empty.
cout << "--------List Category----------" << endl ;
ListCategoryMembers() ;
// Unregister our sample component category.
cout << "--------Unregister-------------" << endl ;
UnregisterCategory() ;
// List the categories to show that the component category
// has been removed.
ListCategories() ;
Release:
// Release the interface pointers.
if (g_pICatInformation != NULL)
{
g_pICatInformation->Release() ;
}
if (g_pICatRegister != NULL)
{
g_pICatRegister->Release() ;
}
Uninitialize:
// Unintialize COM Library
OleUninitialize() ;
return 0 ;
}
/
//
// Worker functions
//
//
// List the registered component categories.
//
BOOL ListCategories()
{
BOOL bReturn = TRUE ;
// Get an enumerator for the categories.
IEnumCATEGORYINFO* pIEnumCATEGORYINFO = NULL ;
HRESULT hr = g_pICatInformation->EnumCategories(::GetUserDefaultLCID(),
&pIEnumCATEGORYINFO) ;
if (FAILED(hr))
{
ErrorMessage("ListCategories: ICatInformation::EnumCategories failed.", hr) ;
return FALSE ;
}
// Prepare for loop.
char szDescription[128] ;
CATEGORYINFO CategoryInfo ;
// Enumerate the categories.
while ((hr = pIEnumCATEGORYINFO->Next(1, &CategoryInfo, NULL)) == S_OK)
{
// Convert from wchar_t to char.
::wcstombs(szDescription, CategoryInfo.szDescription,
sizeof(szDescription)) ;
//_tcscpy(szDescription,CategoryInfo.szDescription);
// Print out description.
cout << szDescription << "\r\n" ;
}
// Did Next fail or finish?
if (FAILED(hr))
{
ErrorMessage("ListCategories: IEnumCATEGORYINFO::Next failed.", hr) ;
bReturn = FALSE ;
}
// Release Interfaces.
if (pIEnumCATEGORYINFO != NULL)
{
pIEnumCATEGORYINFO->Release() ;
}
return bReturn ;
}
//
// Register the component category.
//
BOOL RegisterCategory()
{
HRESULT hr = g_pICatRegister->RegisterCategories(1, &g_SampleCategoryInfo) ;
if (FAILED(hr))
{
ErrorMessage("RegisterCategory: Registering the component category failed.",
hr) ;
return FALSE ;
}
return TRUE ;
}
//
// Unregister the component category.
//
void UnregisterCategory()
{
HRESULT hr = g_pICatRegister->UnRegisterCategories(1, CATID_SampleCategories) ;
if (FAILED(hr))
{
ErrorMessage("UnregisterCategory: Unregistering component category failed.",
hr) ;
}
}
//
// Add component to component category.
//
void AddComponent()
{
HRESULT hr = g_pICatRegister->RegisterClassImplCategories(CLSID_Component1,
1,
CATID_SampleCategories) ;
if (FAILED(hr))
{
ErrorMessage("AddComponent: Adding component to category failed.", hr) ;
}
}
//
// Remove component from component category.
//
void RemoveComponent()
{
HRESULT hr =
g_pICatRegister->UnRegisterClassImplCategories(CLSID_Component1,
1,
CATID_SampleCategories) ;
if (FAILED(hr))
{
ErrorMessage("RemoveComponent: Removing component to category failed.",
hr) ;
}
}
//
// List all of the components which are members of a particular category.
//
void ListCategoryMembers()
{
// Get an enumerator for the categories.
IEnumCLSID* pIEnumCLSID = NULL;
HRESULT hr
= g_pICatInformation->EnumClassesOfCategories(1,
CATID_SampleCategories,
0,
NULL,
&pIEnumCLSID) ;
if (FAILED(hr))
{
ErrorMessage("ListCategoryMembers: EnumClassesOfCategories failed.", hr) ;
}
// Prepare for loop.
char szDescription[128] ;
CLSID clsid ;
// Enumerate the categories.
while (TRUE)
{
// Get the next category.
hr = pIEnumCLSID->Next( 1, &clsid, NULL) ;
if (hr != S_OK)
{
// Did Next fail or finish?
if (FAILED(hr))
{
ErrorMessage("ListCategoryMembers: IEnumCLSID::Next failed.",
hr) ;
}
break ;
}
// Print the friendly name.
OutputFriendlyName(clsid) ;
}
// Release interfaces.
if (pIEnumCLSID != NULL)
{
pIEnumCLSID->Release() ;
}
}
/
//
// Helper function declarations
//
//
// Print out the COM/OLE error string for an HRESULT.
//
void ErrorMessage(const char* str, HRESULT hr)
{
void* pMsgBuf ;
::FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
hr,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &pMsgBuf,
0,
NULL
) ;
// Display the string.
cout << str << "\r\n" ;
cout << "Error (" << hex << hr << "): "
<< (char*)pMsgBuf << endl ;
// Free the buffer.
LocalFree( pMsgBuf ) ;
}
//
// Get friendly name for a CLSID from the Registry and display.
//
void OutputFriendlyName(const CLSID& clsid)
{
// Convert clsid to a string.
wchar_t wszCLSID[80] ;
int r = ::StringFromGUID2(clsid, wszCLSID, 80) ;
assert(r != 0) ;
// Convert to single byte char.
TCHAR szCLSID[40] ;
//wcstombs(szCLSID, wszCLSID, 40) ;
_tcscpy(szCLSID,wszCLSID);
// Open the Registry key.
HKEY key = NULL ;
long l = ::RegOpenKeyEx(HKEY_CLASSES_ROOT,
_T("CLSID"),
0,
KEY_ALL_ACCESS,
&key) ;
assert(l == ERROR_SUCCESS) ;
// Get the friendly name.
TCHAR szFriendly[256] ;
long size = sizeof(szFriendly) ;
l = ::RegQueryValue(key,
szCLSID,
szFriendly,
&size) ;
assert(l == ERROR_SUCCESS) ;
// Output the friendly name.
cout << szFriendly << endl ;
// Clean up.
::RegCloseKey(key) ;
}