枚举COM指针所能查询的接口
鄙人最近从事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;
}
* 获取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
}
* 枚举接口所有名称
*/
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
}
至此已经完成,当然这种方案缺点在于还有很多定制的接口并没有在注册表中注册,因此不够彻底。不过有胜于无了