Source code of Windows XP (NT5)
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.

402 lines
12 KiB

  1. // Copyright (c) 1998-1999 Microsoft Corporation
  2. #include "StdAfx.h"
  3. #include <windows.h>
  4. #include <process.h>
  5. #include <atlbase.h>
  6. #include "DataObj.h"
  7. #include "CompData.h"
  8. #include "DataSrc.h"
  9. #include "SysInfo.h"
  10. #include "gathint.h"
  11. #include "gather.h"
  12. #include "thread.h"
  13. //-----------------------------------------------------------------------------
  14. // Construction and destruction. Cancel any refresh in progress while
  15. // destructing the object.
  16. //-----------------------------------------------------------------------------
  17. CThreadingRefresh::CThreadingRefresh(CDataGatherer * pGatherer) : m_pGatherer(pGatherer)
  18. {
  19. InitializeCriticalSection(&m_csThreadRefresh);
  20. // Generate a system wide unique name for the events (in case there are multiple
  21. // instances of MSInfo running). If we can't generate a GUID for this, use the tick count.
  22. // Unique name generation fixes bug 394884.
  23. CString strEvent(_T(""));
  24. GUID guid;
  25. if (SUCCEEDED(::CoCreateGuid(&guid)))
  26. {
  27. LPOLESTR lpGUID;
  28. if (SUCCEEDED(StringFromCLSID(guid, &lpGUID)))
  29. {
  30. strEvent = lpGUID;
  31. CoTaskMemFree(lpGUID);
  32. }
  33. }
  34. if (strEvent.IsEmpty())
  35. strEvent.Format(_T("%08x"), ::GetTickCount());
  36. m_pThreadInfo = new SThreadInfo;
  37. m_pThreadInfo->m_eventDone = CreateEvent(NULL, TRUE, TRUE, CString(_T("MSInfoDone")) + strEvent);
  38. m_pThreadInfo->m_eventStart = CreateEvent(NULL, TRUE, FALSE, CString(_T("MSInfoStart")) + strEvent);
  39. m_pThreadInfo->m_pThreadRefresh = this;
  40. m_hThread = NULL;
  41. }
  42. CThreadingRefresh::~CThreadingRefresh()
  43. {
  44. KillRefreshThread();
  45. DeleteCriticalSection(&m_csThreadRefresh);
  46. CloseHandle(m_pThreadInfo->m_eventDone);
  47. CloseHandle(m_pThreadInfo->m_eventStart);
  48. delete m_pThreadInfo;
  49. m_pThreadInfo = NULL;
  50. }
  51. //-----------------------------------------------------------------------------
  52. // This function lets the part of the other thread which updates the results
  53. // pane whether or not it's safe to do so. If the refresh thread is currently
  54. // updating the display, the UI thread should leave it alone. This is used
  55. // by SetRefreshing().
  56. //
  57. // It's a global function, it's not pretty, but it's for functionality added
  58. // to the system after it was done.
  59. //-----------------------------------------------------------------------------
  60. BOOL CThreadingRefresh::ResultsPaneNotAvailable()
  61. {
  62. return ((WAIT_TIMEOUT != ::WaitForSingleObject(m_pThreadInfo->m_eventDone, 0)) && m_pThreadInfo->m_fShowData);
  63. }
  64. //-----------------------------------------------------------------------------
  65. // Is there currently a thread doing a refresh?
  66. //-----------------------------------------------------------------------------
  67. BOOL CThreadingRefresh::IsRefreshing()
  68. {
  69. return (WAIT_TIMEOUT == ::WaitForSingleObject(m_pThreadInfo->m_eventDone, 0));
  70. }
  71. //-----------------------------------------------------------------------------
  72. // Wait until the current refresh is done (or a long timeout period passes).
  73. //-----------------------------------------------------------------------------
  74. BOOL CThreadingRefresh::WaitForRefresh()
  75. {
  76. if (IsRefreshing())
  77. return (WAIT_TIMEOUT != ::WaitForSingleObject(m_pThreadInfo->m_eventDone, 600000));
  78. return TRUE;
  79. }
  80. //-----------------------------------------------------------------------------
  81. // What is the percentage complete? Not implemented at this time.
  82. //-----------------------------------------------------------------------------
  83. int CThreadingRefresh::PerchentageComplete()
  84. {
  85. return 0;
  86. }
  87. //-----------------------------------------------------------------------------
  88. // Refresh all of the data recursively from pFolder, not returning until the
  89. // refresh is complete.
  90. //-----------------------------------------------------------------------------
  91. void CThreadingRefresh::RefreshAll(CFolder * pFolder, volatile BOOL * pfCancel)
  92. {
  93. CWaitCursor waitcursor;
  94. if (pFolder && pFolder->GetType() == CDataSource::GATHERER)
  95. {
  96. RefreshFolderAsync(pFolder, NULL, TRUE, FALSE);
  97. while (IsRefreshing())
  98. {
  99. if (pfCancel && *pfCancel)
  100. {
  101. CancelRefresh();
  102. break;
  103. }
  104. Sleep(250);
  105. }
  106. }
  107. }
  108. //-----------------------------------------------------------------------------
  109. // A simple synchronous refresh on a folder. We don't return until it's done.
  110. // We'll cancel any existing refresh in progress, though.
  111. //-----------------------------------------------------------------------------
  112. void CThreadingRefresh::RefreshFolder(CFolder * pFolder, BOOL fRecursive, BOOL fSoftRefresh)
  113. {
  114. if (IsRefreshing() && pFolder == m_pThreadInfo->m_pFolder)
  115. return;
  116. RefreshFolderAsync(pFolder, NULL, fRecursive, fSoftRefresh);
  117. WaitForRefresh();
  118. }
  119. //-----------------------------------------------------------------------------
  120. // This is an asynchronous refresh for a specified folder. It starts a thread
  121. // to perform the refresh, and returns immediately.
  122. //-----------------------------------------------------------------------------
  123. DWORD WINAPI ThreadRefresh(void * pArg);
  124. void CThreadingRefresh::RefreshFolderAsync(CFolder * pFolder, CSystemInfo * pSysInfo, BOOL fRecursive, BOOL fSoftRefresh)
  125. {
  126. if (!m_pGatherer || !pFolder || pFolder->GetType() != CDataSource::GATHERER)
  127. return;
  128. // If we're currently refreshing this folder, don't bother.
  129. if (m_pThreadInfo->m_pFolder == pFolder && IsRefreshing())
  130. return;
  131. // Stop any refresh in progress.
  132. CancelRefresh();
  133. // If this is a soft refresh, if the folder has already been refreshed,
  134. // bounce out of here.
  135. CWBEMFolder * pWBEMFolder = reinterpret_cast<CWBEMFolder *>(pFolder);
  136. if (fSoftRefresh)
  137. {
  138. if (!pWBEMFolder || pWBEMFolder->m_fBeenRefreshed)
  139. return;
  140. if (pWBEMFolder->m_pCategory && pWBEMFolder->m_pCategory->HasBeenRefreshed())
  141. return;
  142. }
  143. // Give some visual indicators that we're refreshing.
  144. if (pSysInfo)
  145. {
  146. pSysInfo->ClearResultsPane();
  147. pSysInfo->SetStatusText(IDS_REFRESHING_MSG);
  148. if (pFolder && pFolder->GetChildNode() == NULL)
  149. pSysInfo->SetRefreshing(pSysInfo->lparamRefreshIndicator);
  150. }
  151. // Set the fields in the shared memory. We know the thread is in a paused
  152. // state (isn't using these fields) because of the CancelRefresh().
  153. m_pThreadInfo->m_fShowData = FALSE;
  154. m_pThreadInfo->m_fCancel = FALSE;
  155. m_pThreadInfo->m_fQuit = FALSE;
  156. m_pThreadInfo->m_pFolder = pFolder;
  157. m_pThreadInfo->m_pSysInfo = pSysInfo;
  158. m_pThreadInfo->m_pGatherer = m_pGatherer;
  159. m_pThreadInfo->m_fRecursive = fRecursive;
  160. m_pThreadInfo->m_fSoftRefresh = fSoftRefresh;
  161. // If the thread hasn't been created, create it now. It will do the refresh,
  162. // then pause waiting for m_eventStart to be triggered. Otherwise, just
  163. // do the trigger so the thread wakes up and refreshes.
  164. if (m_hThread == NULL)
  165. {
  166. m_pThreadInfo->m_fShowData = TRUE;
  167. ::ResetEvent(m_pThreadInfo->m_eventDone);
  168. ::ResetEvent(m_pThreadInfo->m_eventStart);
  169. m_hThread = ::CreateThread(NULL, 0, ThreadRefresh, (LPVOID) m_pThreadInfo, 0, &m_dwThreadID);
  170. }
  171. else
  172. {
  173. ::ResetEvent(m_pThreadInfo->m_eventDone);
  174. ::SetEvent(m_pThreadInfo->m_eventStart);
  175. }
  176. }
  177. //-----------------------------------------------------------------------------
  178. // Cancel a refresh in progress by setting the flag and waiting for thread to
  179. // signal that it's done.
  180. //-----------------------------------------------------------------------------
  181. void CThreadingRefresh::CancelRefresh(BOOL fUpdateUI)
  182. {
  183. if (IsRefreshing())
  184. {
  185. if (fUpdateUI)
  186. {
  187. CWaitCursor waitcursor;
  188. m_pThreadInfo->m_fCancel = TRUE;
  189. if (!WaitForRefresh())
  190. ; // Something bad has happened.
  191. }
  192. else
  193. {
  194. m_pThreadInfo->m_fCancel = TRUE;
  195. if (!WaitForRefresh())
  196. ; // Something bad has happened.
  197. }
  198. if (fUpdateUI && m_pThreadInfo->m_pSysInfo)
  199. m_pThreadInfo->m_pSysInfo->SetStatusText(_T(""));
  200. }
  201. }
  202. //-----------------------------------------------------------------------------
  203. // Terminates the thread which does the refresh, presumably before exiting.
  204. //-----------------------------------------------------------------------------
  205. void CThreadingRefresh::KillRefreshThread()
  206. {
  207. CancelRefresh();
  208. m_pThreadInfo->m_fQuit = TRUE;
  209. m_pThreadInfo->m_fCancel = TRUE;
  210. ::SetEvent(m_pThreadInfo->m_eventStart);
  211. if (WAIT_TIMEOUT == ::WaitForSingleObject(m_hThread, 60000))
  212. ::TerminateThread(m_hThread, 0);
  213. ::CloseHandle(m_hThread);
  214. m_hThread = NULL;
  215. }
  216. //-----------------------------------------------------------------------------
  217. // The thread function which actually performs the work.
  218. //-----------------------------------------------------------------------------
  219. DWORD WINAPI ThreadRefresh(void * pArg)
  220. {
  221. SThreadInfo * pThreadInfo = (SThreadInfo *) pArg;
  222. if (pThreadInfo == NULL)
  223. return 0;
  224. CoInitialize(NULL);
  225. CDataProvider * pOurProvider = NULL;
  226. CDataProvider * pLastExternalProvider = NULL;
  227. CString strLastExternalComputer(_T(""));
  228. while (!pThreadInfo->m_fQuit)
  229. {
  230. if (pThreadInfo->m_pFolder && pThreadInfo->m_pGatherer)
  231. {
  232. // If the provider pointer in the gatherer object has changed since
  233. // we last did a refresh, we should recreate our own provider.
  234. if (pLastExternalProvider != pThreadInfo->m_pGatherer->m_pProvider ||
  235. strLastExternalComputer.CompareNoCase(pThreadInfo->m_pGatherer->m_strDeferredProvider) != 0)
  236. {
  237. // Get the computer name to which we connect.
  238. strLastExternalComputer = pThreadInfo->m_pGatherer->m_strDeferredProvider;
  239. if (pThreadInfo->m_pGatherer->m_pProvider)
  240. strLastExternalComputer = pThreadInfo->m_pGatherer->m_pProvider->m_strComputer;
  241. // Create a new CDataProvider for that computer.
  242. if (pOurProvider)
  243. delete pOurProvider;
  244. pOurProvider = new CDataProvider;
  245. if (pOurProvider)
  246. pOurProvider->Create(strLastExternalComputer, pThreadInfo->m_pGatherer);
  247. }
  248. // Save the external provider pointer, and replace it with ours.
  249. // The main thread shouldn't be touching it while we are running
  250. // (i.e. when m_eventDone is not set).
  251. pLastExternalProvider = pThreadInfo->m_pGatherer->m_pProvider;
  252. pThreadInfo->m_pGatherer->m_pProvider = pOurProvider;
  253. // Do the refresh on the folder.
  254. if (pThreadInfo->m_pFolder->GetType() == CDataSource::GATHERER)
  255. {
  256. if (pThreadInfo->m_pGatherer && !pThreadInfo->m_fSoftRefresh)
  257. pThreadInfo->m_pGatherer->SetLastError(GATH_ERR_NOERROR);
  258. CWBEMFolder * pWBEMFolder = reinterpret_cast<CWBEMFolder *>(pThreadInfo->m_pFolder);
  259. if (pWBEMFolder && pWBEMFolder->m_pCategory != NULL)
  260. pWBEMFolder->m_pCategory->Refresh(pThreadInfo->m_fRecursive, &(pThreadInfo->m_fCancel), pThreadInfo->m_fSoftRefresh);
  261. if (!pThreadInfo->m_fCancel)
  262. pWBEMFolder->m_fBeenRefreshed = TRUE;
  263. }
  264. // Restore the external provider pointer.
  265. pOurProvider = pThreadInfo->m_pGatherer->m_pProvider;
  266. pThreadInfo->m_pGatherer->m_pProvider = pLastExternalProvider;
  267. // Signal that we're done, to release the UI thread (if it's waiting to cancel us and start
  268. // a new refresh).
  269. ::SetEvent(pThreadInfo->m_eventDone);
  270. // Make sure we've cleared the UI thread updating the results pane by entering and immediately
  271. // leaving the critical section.
  272. if (pThreadInfo->m_pThreadRefresh)
  273. {
  274. pThreadInfo->m_pThreadRefresh->EnterCriticalSection();
  275. pThreadInfo->m_pThreadRefresh->LeaveCriticalSection();
  276. }
  277. // If the refresh wasn't cancelled, and the user hasn't selected a
  278. // different category while we were refreshing, then update the
  279. // display. Note, we have to indicate that we're done before doing
  280. // this.
  281. if (!pThreadInfo->m_fCancel && pThreadInfo->m_pSysInfo && pThreadInfo->m_pSysInfo->m_pConsole2)
  282. {
  283. pThreadInfo->m_pSysInfo->SetStatusText(_T(""));
  284. if (pThreadInfo->m_fShowData)
  285. {
  286. if (pThreadInfo->m_pSysInfo->m_pLastRefreshedFolder == pThreadInfo->m_pFolder)
  287. {
  288. pThreadInfo->m_pSysInfo->ClearResultsPane();
  289. pThreadInfo->m_pSysInfo->SetResultHeaderColumns(pThreadInfo->m_pFolder);
  290. pThreadInfo->m_pSysInfo->EnumerateValues(pThreadInfo->m_pFolder);
  291. }
  292. }
  293. }
  294. else
  295. {
  296. // Invalidate the refresh (didn't get to display it).
  297. CWBEMFolder * pWBEMFolder = reinterpret_cast<CWBEMFolder *>(pThreadInfo->m_pFolder);
  298. if (pWBEMFolder)
  299. pWBEMFolder->m_fBeenRefreshed = FALSE;
  300. }
  301. }
  302. else
  303. ::SetEvent(pThreadInfo->m_eventDone);
  304. pThreadInfo->m_fShowData = FALSE;
  305. // Go to sleep until it's time to return to work.
  306. ::WaitForSingleObject(pThreadInfo->m_eventStart, INFINITE);
  307. ::ResetEvent(pThreadInfo->m_eventStart);
  308. ::ResetEvent(pThreadInfo->m_eventDone);
  309. pThreadInfo->m_fShowData = TRUE;
  310. if (!pThreadInfo->m_fCancel && !pThreadInfo->m_fQuit && pThreadInfo->m_pSysInfo)
  311. pThreadInfo->m_pSysInfo->SetStatusText(IDS_REFRESHING_MSG);
  312. }
  313. if (pOurProvider)
  314. delete pOurProvider;
  315. CoUninitialize();
  316. return 0;
  317. }