/******************************************************************** Copyright (c) 1999 Microsoft Corporation Module Name: PCH_Codec.CPP Abstract: WBEM provider class implementation for PCH_CODEC class Revision History: Ghim-Sim Chua (gschua) 04/27/99 - created Ghim-Sim Chua (gschua) 05/02/99 - Modified code to use CopyProperty function - Use CComBSTR instead of USES_CONVERSION Jim Martin (a-jammar) 05/13/99 - Picked up the remaining properties (groupname and key from the registry. ********************************************************************/ #include "pchealth.h" #include "PCH_Codec.h" ///////////////////////////////////////////////////////////////////////////// // tracing stuff #ifdef THIS_FILE #undef THIS_FILE #endif static char __szTraceSourceFile[] = __FILE__; #define THIS_FILE __szTraceSourceFile #define TRACE_ID DCID_CODEC CPCH_Codec MyPCH_CodecSet (PROVIDER_NAME_PCH_CODEC, PCH_NAMESPACE) ; // Property names //=============== const static WCHAR* pCategory = L"Category" ; const static WCHAR* pTimeStamp = L"TimeStamp" ; const static WCHAR* pChange = L"Change" ; const static WCHAR* pCodecDriver = L"CodecDriver" ; const static WCHAR* pDate = L"Date" ; const static WCHAR* pDescription = L"Description" ; const static WCHAR* pGroupName = L"GroupName" ; const static WCHAR* pkey = L"key" ; const static WCHAR* pSize = L"Size" ; const static WCHAR* pVersion = L"Version" ; //----------------------------------------------------------------------------- // Part of the data from the PCH_CODEC class does not come from the cimv2 // Win32_CODECFile class, but from the registry. The "GroupName" and "Key" // properties are found at: // // HKLM\System\CurrentControlSet\Control\MediaResources\\:driver // // Where the "driver" value is equal to the filename of the CODEC. Due to the // way this part of the registry is constructed, we can't find the // and given the driver name. We'll need to build a map from driver // to and - building the map requires traversing the registry. // // This class is used as a helper for that lookup. When it's created, it // scans the registry, processing all of the CODEC entries. It can then // be queried for the key and group associated with a driver. //----------------------------------------------------------------------------- #define MAX_DRIVER_LEN MAX_PATH #define MAX_KEY_LEN MAX_PATH #define MAX_GROUP_LEN MAX_PATH class CCODECInfo { public: CCODECInfo(); ~CCODECInfo(); BOOL QueryCODECInfo(LPCTSTR szDriver, LPCSTR * pszKey, LPCSTR * pszGroup); private: struct SCODECNode { TCHAR m_szDriver[MAX_DRIVER_LEN]; TCHAR m_szKey[MAX_KEY_LEN]; TCHAR m_szGroup[MAX_GROUP_LEN]; SCODECNode * m_pNext; }; SCODECNode * m_pCODECList; }; //----------------------------------------------------------------------------- // The constructor reads the CODEC info from the registry and builds a linked // list (unsorted) of entries. The destructor deletes it. //----------------------------------------------------------------------------- CCODECInfo::CCODECInfo() : m_pCODECList(NULL) { TraceFunctEnter("CCODECInfo::CCODECInfo"); LPCTSTR szCODECKey = _T("System\\CurrentControlSet\\Control\\MediaResources"); LPTSTR szDrvValue = _T("driver"); HKEY hkeyCODEC; if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, szCODECKey, 0, KEY_READ, &hkeyCODEC)) ErrorTrace(TRACE_ID, "RegOpenKeyEx failed on CODEC key."); else { // Enumerate each subkey of the CODEC key. Each subkey corresponds to a group. DWORD dwGroupIndex = 0; DWORD dwSize = MAX_GROUP_LEN; FILETIME ft; TCHAR szGroup[MAX_GROUP_LEN]; TCHAR szKey[MAX_KEY_LEN]; TCHAR szDriver[MAX_DRIVER_LEN]; while (ERROR_SUCCESS == RegEnumKeyEx(hkeyCODEC, dwGroupIndex, szGroup, &dwSize, 0, NULL, NULL, &ft)) { // Open the group subkey. Then enumerate it's subkeys. These will be the keys. HKEY hkeyGroup; if (ERROR_SUCCESS != RegOpenKeyEx(hkeyCODEC, szGroup, 0, KEY_READ, &hkeyGroup)) ErrorTrace(TRACE_ID, "RegOpenKeyEx failed on group key = %s.", szGroup); else { dwSize = MAX_KEY_LEN; DWORD dwKeyIndex = 0; while (ERROR_SUCCESS == RegEnumKeyEx(hkeyGroup, dwKeyIndex, szKey, &dwSize, 0, NULL, NULL, &ft)) { // For each key, attempt to get the value named "driver". This is the // filename for the driver for this CODEC. HKEY hkeyKey; if (ERROR_SUCCESS != RegOpenKeyEx(hkeyGroup, szKey, 0, KEY_READ, &hkeyKey)) ErrorTrace(TRACE_ID, "RegOpenKeyEx failed on key = %s.", szKey); else { // Note - there's no trace here because sometimes there may not be // a driver value, and this is not an error for us. dwSize = MAX_DRIVER_LEN * sizeof(TCHAR); // this wants the size in bytes DWORD dwType = REG_SZ; if (ERROR_SUCCESS == RegQueryValueEx(hkeyKey, szDrvValue, NULL, &dwType, (LPBYTE) szDriver, &dwSize)) { if (*szGroup && *szKey && *szDriver) { // Here's where we insert a value into the map, using // the strings szDriver, szKey and szGroup. SCODECNode * pNew = new SCODECNode; if (!pNew) { ErrorTrace(TRACE_ID, "Out of memory."); RegCloseKey(hkeyKey); RegCloseKey(hkeyGroup); RegCloseKey(hkeyCODEC); throw CHeap_Exception(CHeap_Exception::E_ALLOCATION_ERROR); } _tcscpy(pNew->m_szDriver, szDriver); _tcscpy(pNew->m_szKey, szKey); _tcscpy(pNew->m_szGroup, szGroup); pNew->m_pNext = m_pCODECList; m_pCODECList = pNew; } } if (ERROR_SUCCESS != RegCloseKey(hkeyKey)) ErrorTrace(TRACE_ID, "RegCloseKey failed on key."); } dwSize = MAX_KEY_LEN; dwKeyIndex += 1; } if (ERROR_SUCCESS != RegCloseKey(hkeyGroup)) ErrorTrace(TRACE_ID, "RegCloseKey failed on key."); } dwSize = MAX_GROUP_LEN; dwGroupIndex += 1; } if (ERROR_SUCCESS != RegCloseKey(hkeyCODEC)) ErrorTrace(TRACE_ID, "RegCloseKey failed on CODEC key."); } TraceFunctLeave(); } CCODECInfo::~CCODECInfo() { TraceFunctEnter("CCODECInfo::~CCODECInfo"); while (m_pCODECList) { SCODECNode * pNext = m_pCODECList->m_pNext; delete m_pCODECList; m_pCODECList = pNext; } TraceFunctLeave(); } //----------------------------------------------------------------------------- // Search for the requested driver in the list of CODEC information entries. // If it's found, set pszKey and pszGroup to point to the key and group strings // in the entry and return TRUE, otherwise return FALSE. Note: copies of the // strings are not made, so the caller is not responsible for deallocating // the strings. Another note: the string pointers won't be valid after the // CCODECInfo object is destructed. //----------------------------------------------------------------------------- BOOL CCODECInfo::QueryCODECInfo(LPCTSTR szDriver, LPCTSTR * pszKey, LPCTSTR * pszGroup) { TraceFunctEnter("CCODECInfo::QueryCODECInfo"); _ASSERT(szDriver && pszKey && pszGroup); SCODECNode * pScan = m_pCODECList; BOOL fReturn = FALSE; while (pScan) { if (0 == _tcscmp(szDriver, pScan->m_szDriver)) { *pszKey = pScan->m_szKey; *pszGroup = pScan->m_szGroup; fReturn = TRUE; break; } pScan = pScan->m_pNext; } TraceFunctLeave(); return fReturn; } /***************************************************************************** * * FUNCTION : CPCH_Codec::EnumerateInstances * * DESCRIPTION : Returns all the instances of this class. * * INPUTS : A pointer to the MethodContext for communication with WinMgmt. * A long that contains the flags described in * IWbemServices::CreateInstanceEnumAsync. Note that the following * flags are handled by (and filtered out by) WinMgmt: * WBEM_FLAG_DEEP * WBEM_FLAG_SHALLOW * WBEM_FLAG_RETURN_IMMEDIATELY * WBEM_FLAG_FORWARD_ONLY * WBEM_FLAG_BIDIRECTIONAL * * RETURNS : WBEM_S_NO_ERROR if successful * * COMMENTS : TO DO: All instances on the machine should be returned here. * If there are no instances, return WBEM_S_NO_ERROR. * It is not an error to have no instances. * *****************************************************************************/ HRESULT CPCH_Codec::EnumerateInstances(MethodContext * pMethodContext, long lFlags) { TraceFunctEnter("CPCH_Codec::EnumerateInstances"); USES_CONVERSION; HRESULT hRes = WBEM_S_NO_ERROR; REFPTRCOLLECTION_POSITION posList; CComPtr pEnumInst; IWbemClassObjectPtr pObj; ULONG ulRetVal; // This instance of CCODECInfo will provide some of the missing information // about each CODEC. Constructing it queries the registry for CODEC info. CCODECInfo codecinfo; // Get the date and time SYSTEMTIME stUTCTime; GetSystemTime(&stUTCTime); // Execute the query hRes = ExecWQLQuery(&pEnumInst, CComBSTR("SELECT * FROM Win32_CodecFile")); if (FAILED(hRes)) goto END; // enumerate the instances from win32_CodecFile while (WBEM_S_NO_ERROR == pEnumInst->Next(WBEM_INFINITE, 1, &pObj, &ulRetVal)) { // Create a new instance based on the passed-in MethodContext CInstancePtr pInstance(CreateNewInstance(pMethodContext), false); CComVariant varValue; if (!pInstance->SetDateTime(pTimeStamp, WBEMTime(stUTCTime))) ErrorTrace(TRACE_ID, "SetDateTime on Timestamp Field failed."); if (!pInstance->SetCHString(pChange, L"Snapshot")) ErrorTrace(TRACE_ID, "SetCHString on Change Field failed."); (void)CopyProperty(pObj, L"group", pInstance, pCategory); (void)CopyProperty(pObj, L"name", pInstance, pCodecDriver); (void)CopyProperty(pObj, L"description", pInstance, pDescription); (void)CopyProperty(pObj, L"filesize", pInstance, pSize); (void)CopyProperty(pObj, L"version", pInstance, pVersion); // BUGBUG: WMI does not seem to be populating this field correctly. // Even though Win32_CODECFile is derived from CIM_DataFile, it doesn't // seem to be inheriting CreationDate. This is what we'd like to do: // // (void)CopyProperty(pObj, "CreationDate", pInstance, pDate); // Get the data which is missing from the Win32_CODECClass. Use the // instance of CCODECInfo we declared - we need to pass in just the // driver name (without the complete path). CComBSTR bstrDriver("name"); if (FAILED(pObj->Get(bstrDriver, 0, &varValue, NULL, NULL))) ErrorTrace(TRACE_ID, "GetVariant on pCodecDriver field failed."); else { CComBSTR ccombstrValue(V_BSTR(&varValue)); // Because Win32_CODECFile doesn't seem to be inheriting // CreationDate, we need to get the actual creation date // by calling API functions. LPTSTR szName = W2T(ccombstrValue); HANDLE hFile = CreateFile(szName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (INVALID_HANDLE_VALUE == hFile) ErrorTrace(TRACE_ID, "Couldn't open codec file to get date."); else { SYSTEMTIME stFileTime; FILETIME ftFileTime; if (GetFileTime(hFile, NULL, NULL, &ftFileTime)) if (FileTimeToSystemTime(&ftFileTime, &stFileTime)) if (!pInstance->SetDateTime(pDate, WBEMTime(stFileTime))) ErrorTrace(TRACE_ID, "SetDateTime on date field failed."); CloseHandle(hFile); } // We need to convert the string from a BSTR to a LPCTSTR, // and to only include the file part (without the path). UINT uLen = SysStringLen(ccombstrValue); // Scan backwards through the string until we've either reached // the start (shouldn't happen) or a '\'. UINT iChar = uLen - 1; while (iChar && ccombstrValue[iChar] != L'\\') iChar -= 1; // Then scan to the end of the string, copying the filename. if (ccombstrValue[iChar] == L'\\') iChar += 1; TCHAR szDriver[MAX_DRIVER_LEN + 1] = _T(""); int i = 0; while (iChar < uLen && i < MAX_DRIVER_LEN) szDriver[i++] = (TCHAR) ccombstrValue[iChar++]; szDriver[i] = _T('\0'); LPCSTR szKey = NULL; LPCSTR szGroup = NULL; if (codecinfo.QueryCODECInfo(szDriver, &szKey, &szGroup)) { if (!pInstance->SetCHString(pkey, szKey)) ErrorTrace(TRACE_ID, "SetCHString on key field failed."); if (!pInstance->SetCHString(pGroupName, szGroup)) ErrorTrace(TRACE_ID, "SetCHString on group field failed."); } else if (codecinfo.QueryCODECInfo(szName, &szKey, &szGroup)) { // Sometimes the CODEC is stored in the registry with a complete // path. If we can't find the CODEC based on just the filename, // we might find it with the path. if (!pInstance->SetCHString(pkey, szKey)) ErrorTrace(TRACE_ID, "SetCHString on key field failed."); if (!pInstance->SetCHString(pGroupName, szGroup)) ErrorTrace(TRACE_ID, "SetCHString on group field failed."); } } hRes = pInstance->Commit(); if (FAILED(hRes)) ErrorTrace(TRACE_ID, "Commit on Instance failed."); } END: TraceFunctLeave(); return hRes; }