鄙人最近从事COM开发,基本上得到一COM接口就要QueryInterface另外一个接口,都能获取到哪些接口就要查文档了。如果有个函数传入一接口指针,能输出其支持的所有接口那就好了。接口的注册表项位于HKEY_CLASSES_ROOT\Interface中,每个子项的名称都是以接口IID命名,其默认值就是接口的字符串名称。
先写个函数查注册表中接口IID对应的接口名称:
/*
* 获取COM接口的名称,返回获取成功与否
* 接口名为GUID注册表项默认的值
*/
template
<
int
t_nSize
>
ATLINLINE BOOL GetInterfaceName( REFIID riid, TCHAR (
&
szName)[t_nSize] )
{
BOOL bRet
=
FALSE;
#ifdef USER_ATL_
//
CRegKey版本
CRegKey regKey;
OLECHAR szGuid[
64
];
::StringFromGUID2(riid, szGuid,
sizeof
(szGuid)
/
sizeof
(OLECHAR));
TCHAR szSubKey[
128
];
wsprintf( szSubKey, _T(
"
Interface\\%s
"
), COLE2T(szGuid) );
if
( ERROR_SUCCESS
==
regKey.Open( HKEY_CLASSES_ROOT, szSubKey, KEY_QUERY_VALUE ) )
{
ULONG cb
=
127
;
if
( ERROR_SUCCESS
==
regKey.QueryStringValue( NULL, szName,
&
cb) )
{
bRet
=
TRUE;
}
regKey.Close();
}
#else
//
API版本:
OLECHAR szGuid[
64
];
::StringFromGUID2(riid, szGuid,
sizeof
(szGuid)
/
sizeof
(OLECHAR));
TCHAR szSubKey[
128
];
wsprintf( szSubKey, _T(
"
Interface\\%s
"
), COLE2T(szGuid) );
HKEY hKey;
if
( ERROR_SUCCESS
==
::RegOpenKeyEx( HKEY_CLASSES_ROOT, szSubKey,
0
, KEY_QUERY_VALUE,
&
hKey) )
{
DWORD dwType;
DWORD dwSize
=
t_nSize
*
sizeof
(TCHAR);
if
( ERROR_SUCCESS
==
::RegQueryValueEx(hKey, NULL, NULL,
&
dwType, reinterpret_cast
<
LPBYTE
>
(szName),
&
dwSize) )
{
bRet
=
TRUE;
}
::RegCloseKey(hKey);
}
#endif
return
bRet;
}
读注册表分别用了API版本和ATL的CRegKey版本(大家不要BS,鄙人一向信奉多练练手没害处)。
之后遍历注册表项HKEY_CLASSES_ROOT\Interface,获取每个接口的IID,如果传入的COM接口QueryInterface这个接口成功,则支持该接口,输出其接口名称。
/*
* 枚举接口所有名称
*/
ATLINLINE
void
ScanInterface( IUnknown
*
pUnk )
{
if
( pUnk
==
NULL )
return
;
#ifdef USER_ATL_
CRegKey regKey;
if
( ERROR_SUCCESS
==
regKey.Open( HKEY_CLASSES_ROOT, _T(
"
Interface
"
), KEY_QUERY_VALUE
|
KEY_ENUMERATE_SUB_KEYS ) )
{
TCHAR szGuid[
128
];
DWORD dwIndex
=
0
;
DWORD dwSize
=
_countof(szGuid)
*
sizeof
(TCHAR);
while
( ERROR_SUCCESS
==
regKey.EnumKey( dwIndex, szGuid,
&
dwSize ) )
{
IID iid;
::IIDFromString( CT2OLE(szGuid),
&
iid );
CComPtr
<
IUnknown
>
spUnk;
if
( SUCCEEDED( pUnk
->
QueryInterface( iid, (LPVOID
*
)
&
spUnk ) ) )
{
TCHAR szName[
128
];
if
( GetInterfaceName( iid, szName ) )
{
AtlTrace( _T(
"
%s\n
"
), szName );
}
}
dwSize
=
_countof(szGuid)
*
sizeof
(TCHAR);
dwIndex
++
;
}
regKey.Close();
}
#else
HKEY hKey;
if
( ERROR_SUCCESS
==
::RegOpenKeyEx( HKEY_CLASSES_ROOT, _T(
"
Interface
"
),
0
, KEY_QUERY_VALUE
|
KEY_ENUMERATE_SUB_KEYS,
&
hKey) )
{
TCHAR szGuid[
128
];
DWORD dwIndex
=
0
;
DWORD dwSize
=
_countof(szGuid)
*
sizeof
(TCHAR);
while
( ERROR_SUCCESS
==
::RegEnumKeyEx(hKey, dwIndex, szGuid,
&
dwSize, NULL, NULL,NULL, NULL ) )
{
IID iid;
::IIDFromString( CT2OLE(szGuid),
&
iid);
CComPtr
<
IUnknown
>
spUnk;
if
( SUCCEEDED( pUnk
->
QueryInterface( iid, (LPVOID
*
)
&
spUnk ) ) )
{
TCHAR szName[
128
];
if
( GetInterfaceName( iid, szName ) )
{
AtlTrace( _T(
"
%s\n
"
), szName );
}
}
dwSize
=
_countof(szGuid)
*
sizeof
(TCHAR);
dwIndex
++
;
}
::RegCloseKey(hKey);
}
#endif
}
至此已经完成,当然这种方案缺点在于还有很多定制的接口并没有在注册表中注册,因此不够彻底。不过有胜于无了