Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

509 lines
18 KiB

  1. /********************************************************************
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. PCH_Module.CPP
  5. Abstract:
  6. WBEM provider class implementation for PCH_Module class
  7. Revision History:
  8. Ghim-Sim Chua (gschua) 04/27/99
  9. - Created
  10. Jim Martin (a-jammar) 05/20/99
  11. - Populated data fields.
  12. ********************************************************************/
  13. #include "pchealth.h"
  14. #include "PCH_Module.h"
  15. #include <tlhelp32.h>
  16. /////////////////////////////////////////////////////////////////////////////
  17. // tracing stuff
  18. #ifdef THIS_FILE
  19. #undef THIS_FILE
  20. #endif
  21. static char __szTraceSourceFile[] = __FILE__;
  22. #define THIS_FILE __szTraceSourceFile
  23. #define TRACE_ID DCID_MODULE
  24. CPCH_Module MyPCH_ModuleSet (PROVIDER_NAME_PCH_MODULE, PCH_NAMESPACE) ;
  25. // Property names
  26. //===============
  27. const static WCHAR * pAddress = L"Address" ;
  28. const static WCHAR * pTimeStamp = L"TimeStamp" ;
  29. const static WCHAR * pChange = L"Change" ;
  30. const static WCHAR * pDate = L"Date" ;
  31. const static WCHAR * pDescription = L"Description" ;
  32. const static WCHAR * pManufacturer = L"Manufacturer" ;
  33. const static WCHAR * pName = L"Name" ;
  34. const static WCHAR * pPartOf = L"PartOf" ;
  35. const static WCHAR * pPath = L"Path" ;
  36. const static WCHAR * pSize = L"Size" ;
  37. const static WCHAR * pType = L"Type" ;
  38. const static WCHAR * pVersion = L"Version" ;
  39. //-----------------------------------------------------------------------------
  40. // The CModuleCollection class is used to gather all of the running modules.
  41. // They can be found from the CIM_ProcessExecutable class, as the Antecedent
  42. // property, with the following caveat: this enumeration will include
  43. // duplicate entries of the same file (it will have a copy of a DLL for each
  44. // time it's been loaded). This class will remove the duplicates, and save
  45. // a list of filenames which can then be queried.
  46. //-----------------------------------------------------------------------------
  47. class CModuleCollection
  48. {
  49. public:
  50. CModuleCollection();
  51. ~CModuleCollection();
  52. HRESULT Create(IEnumWbemClassObject * pEnum);
  53. BOOL GetInstance(DWORD dwIndex, LPWSTR * pszFile);
  54. private:
  55. struct SModule
  56. {
  57. LPWSTR m_szFilename;
  58. SModule * m_pNext;
  59. SModule(LPWSTR szFilename, SModule * pNext) : m_pNext(pNext) { m_szFilename = szFilename; }
  60. ~SModule() { delete m_szFilename; }
  61. };
  62. SModule * m_pList;
  63. SModule * m_pLastQueriedItem;
  64. DWORD m_dwLastQueriedIndex;
  65. };
  66. //-----------------------------------------------------------------------------
  67. // The constructor and destructor are simple.
  68. //-----------------------------------------------------------------------------
  69. CModuleCollection::CModuleCollection()
  70. : m_pList(NULL),
  71. m_pLastQueriedItem(NULL),
  72. m_dwLastQueriedIndex(0)
  73. {}
  74. CModuleCollection::~CModuleCollection()
  75. {
  76. TraceFunctEnter("CModuleCollection::~CModuleCollection");
  77. while (m_pList)
  78. {
  79. SModule * pNext = m_pList->m_pNext;
  80. delete m_pList;
  81. m_pList = pNext;
  82. }
  83. TraceFunctLeave();
  84. }
  85. //-----------------------------------------------------------------------------
  86. // The Create method creates the list of module names based on the enumerator
  87. // passed in (which is assumed to enumerate Antecedents in
  88. // CIM_ProcessExecutable).
  89. //-----------------------------------------------------------------------------
  90. HRESULT CModuleCollection::Create(IEnumWbemClassObject * pEnum)
  91. {
  92. TraceFunctEnter("CModuleCollection::Create");
  93. HRESULT hRes = S_OK;
  94. IWbemClassObjectPtr pObj;
  95. ULONG ulRetVal;
  96. CComVariant varValue;
  97. CComBSTR bstrFile("Antecedent");
  98. while (WBEM_S_NO_ERROR == pEnum->Next(WBEM_INFINITE, 1, &pObj, &ulRetVal))
  99. {
  100. if (FAILED(pObj->Get(bstrFile, 0, &varValue, NULL, NULL)))
  101. ErrorTrace(TRACE_ID, "Get on Antecedent field failed.");
  102. else
  103. {
  104. // We need to convert the string from a BSTR to a LPCTSTR,
  105. // and to only include the file part (without the WMI part).
  106. // So we need to scan through the string until an '=' is
  107. // found, then use the rest (minus enclosing quote marks)
  108. // as the file path.
  109. CComBSTR ccombstrValue(V_BSTR(&varValue));
  110. UINT i = 0, uLen = SysStringLen(ccombstrValue);
  111. // Scan to the '='.
  112. while (i < uLen && ccombstrValue[i] != L'=')
  113. i++;
  114. // Skip over the '=' and any quotes.
  115. while (i < uLen && (ccombstrValue[i] == L'=' || ccombstrValue[i] == L'"'))
  116. i++;
  117. // Allocate a character buffer and copy the string, converting it to
  118. // lower case (to make comparisons faster later on).
  119. LPWSTR szFilename = new WCHAR[uLen - i + 1];
  120. if (!szFilename)
  121. {
  122. ErrorTrace(TRACE_ID, "CModuleCollection::Create out of memory");
  123. throw CHeap_Exception(CHeap_Exception::E_ALLOCATION_ERROR);
  124. }
  125. for (int j = 0; i < uLen; j++, i++)
  126. szFilename[j] = towlower(ccombstrValue[i]);
  127. // Terminate the string - if it ends with a quote, overwrite that with a
  128. // null character.
  129. if (j && szFilename[j - 1] == L'"')
  130. j -= 1;
  131. szFilename[j] = L'\0';
  132. // Check to see if this module is already in the list of strings.
  133. SModule * pScan = m_pList;
  134. while (pScan)
  135. {
  136. if (wcscmp(szFilename, pScan->m_szFilename) == 0)
  137. break;
  138. pScan = pScan->m_pNext;
  139. }
  140. if (pScan == NULL)
  141. {
  142. // We reached the end of the list without finding a duplicate.
  143. // Add the new string to the list of modules, which will be responsible for
  144. // deallocating the string.
  145. SModule * pNew = new SModule(szFilename, m_pList);
  146. if (!pNew)
  147. {
  148. delete [] szFilename;
  149. ErrorTrace(TRACE_ID, "CModuleCollection::Create out of memory");
  150. throw CHeap_Exception(CHeap_Exception::E_ALLOCATION_ERROR);
  151. }
  152. m_pList = pNew;
  153. }
  154. else
  155. delete [] szFilename;
  156. }
  157. }
  158. // Set the queried item pointer to the start of the list.
  159. m_pLastQueriedItem = m_pList;
  160. m_dwLastQueriedIndex = 0;
  161. TraceFunctLeave();
  162. return hRes;
  163. }
  164. //-----------------------------------------------------------------------------
  165. // Get the instance of module string referenced by the index. This is stored
  166. // internally as a linked list, but we'll cache a pointer for the last
  167. // referenced dwIndex to improve performance if the dwIndex is iterated
  168. // sequentially. Return TRUE and set pszFile to point to the string if
  169. // it exists, otherwise return FALSE.
  170. //-----------------------------------------------------------------------------
  171. BOOL CModuleCollection::GetInstance(DWORD dwIndex, LPWSTR * pszFile)
  172. {
  173. TraceFunctEnter("CModuleCollection::GetInstance");
  174. // If the call is for an index less than the last queried index (which
  175. // should be rare), we need to scan from the start of the list.
  176. if (dwIndex < m_dwLastQueriedIndex)
  177. {
  178. m_dwLastQueriedIndex = 0;
  179. m_pLastQueriedItem = m_pList;
  180. }
  181. // Scan through the list by (dwIndex - m_dwLastQueriedIndex) items.
  182. while (dwIndex > m_dwLastQueriedIndex && m_pLastQueriedItem)
  183. {
  184. m_pLastQueriedItem = m_pLastQueriedItem->m_pNext;
  185. m_dwLastQueriedIndex += 1;
  186. }
  187. BOOL fResult = FALSE;
  188. if (m_pLastQueriedItem)
  189. {
  190. *pszFile = m_pLastQueriedItem->m_szFilename;
  191. fResult = TRUE;
  192. }
  193. TraceFunctLeave();
  194. return fResult;
  195. }
  196. //-----------------------------------------------------------------------------
  197. // This function is designed to find the process ID and base address for a
  198. // given module. It will scan through the list of processes - if one of the
  199. // names matches the module name, it will return that information. If it's
  200. // not in the processes, then the modules for each process will be examined.
  201. //-----------------------------------------------------------------------------
  202. typedef HANDLE (*CTH32)(DWORD, DWORD);
  203. typedef BOOL (*MODENUM)(HANDLE, LPMODULEENTRY32);
  204. typedef BOOL (*PROCENUM)(HANDLE, LPPROCESSENTRY32);
  205. HRESULT GetModuleProcIDAndAddr(HINSTANCE hKernel32, HANDLE hToolhelp, LPCSTR szFile, DWORD * pdwProcessID, LPVOID * ppAddr, DWORD * pdwSize)
  206. {
  207. TraceFunctEnter("::GetModuleProcIDAndAddr");
  208. HRESULT hRes = E_FAIL;
  209. PROCESSENTRY32 pe;
  210. MODULEENTRY32 me;
  211. PROCENUM ProcFirst, ProcNext;
  212. MODENUM ModFirst, ModNext;
  213. HANDLE hModuleTH;
  214. CTH32 CrtToolhelp32;
  215. ProcFirst = (PROCENUM) ::GetProcAddress(hKernel32, "Process32First");
  216. ProcNext = (PROCENUM) ::GetProcAddress(hKernel32, "Process32Next");
  217. ModFirst = (MODENUM) ::GetProcAddress(hKernel32, "Module32First");
  218. ModNext = (MODENUM) ::GetProcAddress(hKernel32, "Module32Next");
  219. CrtToolhelp32 = (CTH32) ::GetProcAddress(hKernel32, "CreateToolhelp32Snapshot");
  220. pe.dwSize = sizeof(PROCESSENTRY32);
  221. if (CrtToolhelp32 && ProcFirst && ProcNext && ModFirst && ModNext && (ProcFirst)(hToolhelp, &pe))
  222. do
  223. {
  224. if (0 == _stricmp(szFile, pe.szExeFile))
  225. {
  226. hRes = S_OK;
  227. *pdwProcessID = pe.th32ProcessID;
  228. *ppAddr = (LPVOID) NULL;
  229. *pdwSize = 0;
  230. }
  231. // Scan through the modules - either for the process's module if the name
  232. // matched for the process, or for a module with a matching name.
  233. hModuleTH = (CrtToolhelp32)(TH32CS_SNAPMODULE, pe.th32ProcessID);
  234. if (hModuleTH != (HANDLE) -1)
  235. {
  236. me.dwSize = sizeof(MODULEENTRY32);
  237. if ((ModFirst)(hModuleTH, &me))
  238. {
  239. do
  240. {
  241. if (hRes == S_OK)
  242. {
  243. // The file matched a process. We should look for the module
  244. // which matches the process's module ID.
  245. if (me.th32ModuleID == pe.th32ModuleID)
  246. {
  247. *ppAddr = (LPVOID) me.modBaseAddr;
  248. *pdwSize = me.modBaseSize;
  249. break;
  250. }
  251. }
  252. else
  253. {
  254. // The file didn't match the process. We should look for a
  255. // module which matches the name.
  256. if (0 == _stricmp(szFile, me.szExePath))
  257. {
  258. *ppAddr = (LPVOID) me.modBaseAddr;
  259. *pdwSize = me.modBaseSize;
  260. *pdwProcessID = me.th32ProcessID;
  261. hRes = S_OK;
  262. break;
  263. }
  264. }
  265. } while ((ModNext)(hModuleTH, &me));
  266. }
  267. CloseHandle(hModuleTH);
  268. }
  269. } while ((ProcNext)(hToolhelp, &pe) && FAILED(hRes));
  270. TraceFunctLeave();
  271. return hRes;
  272. }
  273. /*****************************************************************************
  274. *
  275. * FUNCTION : CPCH_Module::EnumerateInstances
  276. *
  277. * DESCRIPTION : Returns all the instances of this class.
  278. *
  279. * INPUTS : A pointer to the MethodContext for communication with WinMgmt.
  280. * A long that contains the flags described in
  281. * IWbemServices::CreateInstanceEnumAsync. Note that the following
  282. * flags are handled by (and filtered out by) WinMgmt:
  283. * WBEM_FLAG_DEEP
  284. * WBEM_FLAG_SHALLOW
  285. * WBEM_FLAG_RETURN_IMMEDIATELY
  286. * WBEM_FLAG_FORWARD_ONLY
  287. * WBEM_FLAG_BIDIRECTIONAL
  288. *
  289. * RETURNS : WBEM_S_NO_ERROR if successful
  290. *
  291. * COMMENTS : TO DO: All instances on the machine should be returned here.
  292. * If there are no instances, return WBEM_S_NO_ERROR.
  293. * It is not an error to have no instances.
  294. *
  295. *****************************************************************************/
  296. typedef HANDLE (*CTH32)(DWORD, DWORD);
  297. HRESULT CPCH_Module::EnumerateInstances(MethodContext * pMethodContext, long lFlags)
  298. {
  299. TraceFunctEnter("CPCH_Module::EnumerateInstances");
  300. HRESULT hRes = WBEM_S_NO_ERROR;
  301. // Get the date and time
  302. SYSTEMTIME stUTCTime;
  303. GetSystemTime(&stUTCTime);
  304. // Create a toolhelp snapshot to get process information. We need to dynamically
  305. // link to the function, because it might not be present on all platforms.
  306. HANDLE hToolhelp = (HANDLE) -1;
  307. HINSTANCE hKernel32 = ::LoadLibrary("kernel32");
  308. if (hKernel32)
  309. {
  310. CTH32 CrtToolhelp32 = (CTH32) ::GetProcAddress(hKernel32, "CreateToolhelp32Snapshot");
  311. if (CrtToolhelp32)
  312. hToolhelp = (*CrtToolhelp32)(TH32CS_SNAPPROCESS, 0);
  313. }
  314. try
  315. {
  316. // The CModuleCollection class gathers a list of module names (which can then
  317. // be used to retrieve information about each file).
  318. CFileVersionInfo fileversioninfo;
  319. CModuleCollection moduleinfo;
  320. LPWSTR szFile;
  321. DWORD dwIndex;
  322. CComPtr<IEnumWbemClassObject> pEnum;
  323. hRes = ExecWQLQuery(&pEnum, CComBSTR("SELECT Antecedent FROM CIM_ProcessExecutable"));
  324. if (FAILED(hRes))
  325. goto END;
  326. hRes = moduleinfo.Create(pEnum);
  327. if (FAILED(hRes))
  328. goto END;
  329. // Iterate through all of the module instances.
  330. for (dwIndex = 0; moduleinfo.GetInstance(dwIndex, &szFile); dwIndex++)
  331. {
  332. if (!szFile)
  333. continue;
  334. CInstancePtr pInstance(CreateNewInstance(pMethodContext), false);
  335. // Set the change and timestamp fields to "Snapshot" and the current time.
  336. if (!pInstance->SetDateTime(pTimeStamp, WBEMTime(stUTCTime)))
  337. ErrorTrace(TRACE_ID, "SetDateTime on Timestamp field failed.");
  338. if (!pInstance->SetCHString(pChange, L"Snapshot"))
  339. ErrorTrace(TRACE_ID, "SetCHString on Change field failed.");
  340. // Using the filename, get the CIM_DataFile object.
  341. CComPtr<IWbemClassObject> pFileObj;
  342. CComBSTR ccombstrValue(szFile);
  343. if (SUCCEEDED(GetCIMDataFile(ccombstrValue, &pFileObj, TRUE)))
  344. {
  345. // Using the CIM_DataFile object, copy over the appropriate properties.
  346. CopyProperty(pFileObj, L"Version", pInstance, pVersion);
  347. CopyProperty(pFileObj, L"FileSize", pInstance, pSize);
  348. CopyProperty(pFileObj, L"CreationDate", pInstance, pDate);
  349. CopyProperty(pFileObj, L"Name", pInstance, pPath);
  350. CopyProperty(pFileObj, L"EightDotThreeFileName", pInstance, pName);
  351. CopyProperty(pFileObj, L"Manufacturer", pInstance, pManufacturer);
  352. }
  353. if (SUCCEEDED(fileversioninfo.QueryFile(szFile, TRUE)))
  354. {
  355. if (!pInstance->SetCHString(pDescription, fileversioninfo.GetDescription()))
  356. ErrorTrace(TRACE_ID, "SetCHString on description field failed.");
  357. if (!pInstance->SetCHString(pPartOf, fileversioninfo.GetProduct()))
  358. ErrorTrace(TRACE_ID, "SetCHString on partof field failed.");
  359. }
  360. if (hToolhelp != (HANDLE) -1)
  361. {
  362. DWORD dwProcessID;
  363. LPVOID lpAddr;
  364. DWORD dwSize;
  365. char szWorking[MAX_PATH];
  366. int i = 0, j = 0;
  367. // We need to get the file into an Ansi string, with the double backslashes removed.
  368. // Scan through the string, converting double backslashes to single backslashes.
  369. while (szFile[i])
  370. {
  371. if (szFile[i] == L'\\' && szFile[i + 1] == L'\\')
  372. i += 1;
  373. szWorking[j++] = (char) szFile[i++];
  374. }
  375. szWorking[j] = '\0';
  376. if (SUCCEEDED(GetModuleProcIDAndAddr(hKernel32, hToolhelp, szWorking, &dwProcessID, &lpAddr, &dwSize)))
  377. {
  378. TCHAR szBuffer[30];
  379. DWORD dwVersion;
  380. dwVersion = GetProcessVersion(dwProcessID);
  381. wsprintf(szBuffer, _T("%d.%d"), HIWORD(dwVersion), LOWORD(dwVersion));
  382. if (!pInstance->SetCHString(pType, szBuffer))
  383. ErrorTrace(TRACE_ID, "SetCHString on type field failed.");
  384. wsprintf(szBuffer, _T("%08X-%08X"), lpAddr, (DWORD)lpAddr + dwSize);
  385. if (!pInstance->SetCHString(pAddress, szBuffer))
  386. ErrorTrace(TRACE_ID, "SetCHString on address field failed.");
  387. }
  388. }
  389. hRes = pInstance->Commit();
  390. if (FAILED(hRes))
  391. ErrorTrace(TRACE_ID, "Commit on Instance failed.");
  392. }
  393. }
  394. catch (...)
  395. {
  396. if ((HANDLE)-1 != hToolhelp)
  397. CloseHandle(hToolhelp);
  398. if (hKernel32)
  399. FreeLibrary(hKernel32);
  400. throw;
  401. }
  402. END:
  403. if ((HANDLE)-1 != hToolhelp)
  404. CloseHandle(hToolhelp);
  405. if (hKernel32)
  406. FreeLibrary(hKernel32);
  407. TraceFunctLeave();
  408. return hRes;
  409. }