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.

407 lines
11 KiB

  1. //
  2. // MODULE: RegistryMonitor.cpp
  3. //
  4. // PURPOSE: Monitor changes to the registry.
  5. //
  6. // COMPANY: Saltmine Creative, Inc. (206)-284-7511 [email protected]
  7. //
  8. // AUTHOR: Joe Mabel
  9. //
  10. // ORIGINAL DATE: 9-16-98
  11. //
  12. // NOTES:
  13. //
  14. // Version Date By Comments
  15. //--------------------------------------------------------------------
  16. // V3.0 09-16-98 JM
  17. //
  18. #pragma warning(disable:4786)
  19. #include "stdafx.h"
  20. #include "RegistryMonitor.h"
  21. #include "apgts.h"//Added 20010302 for RUNNING_LOCAL_TS macro
  22. #include "event.h"
  23. #include "apiwraps.h"
  24. #include "ThreadPool.h"
  25. #include "apgtslog.h"
  26. //////////////////////////////////////////////////////////////////////
  27. // CRegistryMonitor::ThreadStatus
  28. //////////////////////////////////////////////////////////////////////
  29. /* static */ CString CRegistryMonitor::ThreadStatusText(ThreadStatus ts)
  30. {
  31. switch(ts)
  32. {
  33. case eBeforeInit: return _T("Before Init");
  34. case eInit: return _T("Init");
  35. case eFail: return _T("Fail");
  36. case eDefaulting: return _T("Defaulting");
  37. case eWait: return _T("Wait");
  38. case eRun: return _T("Run");
  39. case eExiting: return _T("Exiting");
  40. default: return _T("");
  41. }
  42. }
  43. //////////////////////////////////////////////////////////////////////
  44. // CRegistryMonitor
  45. // This class does the bulk of its work on a separate thread.
  46. // The thread is created in the constructor by starting static function
  47. // CDirectoryMonitor::RegistryMonitorTask
  48. // That function, in turn does its work by calling private members of this class that
  49. // are specific to use on the RegistryMonitorTask thread.
  50. // When this goes out of scope, its own destructor calls ShutDown to stop the thread,
  51. // waits for the thread to shut.
  52. // Inherited methods from CRegistryMonitor are available to other threads.
  53. //////////////////////////////////////////////////////////////////////
  54. CRegistryMonitor::CRegistryMonitor( CDirectoryMonitor & DirectoryMonitor,
  55. CThreadPool * pThreadPool,
  56. const CString& strTopicName,
  57. CHTMLLog *pLog )
  58. : CAPGTSRegConnector( strTopicName ),
  59. m_DirectoryMonitor(DirectoryMonitor),
  60. m_bMustStartDirMonitor(true),
  61. m_bMustStartThreadPool(true),
  62. m_bShuttingDown(false),
  63. m_dwErr(0),
  64. m_ThreadStatus(eBeforeInit),
  65. m_time(0),
  66. m_pThreadPool(pThreadPool),
  67. m_pLog( pLog )
  68. {
  69. enum {eHevMon, eHevInit, eHevShut, eThread, eOK} Progress = eHevMon;
  70. SetThreadStatus(eBeforeInit);
  71. m_hevMonitorRequested = ::CreateEvent(
  72. NULL,
  73. FALSE, // release one thread (the RegistryMonitorTask) on signal
  74. FALSE, // initially non-signalled
  75. NULL);
  76. if (m_hevMonitorRequested)
  77. {
  78. Progress = eHevInit;
  79. m_hevInitialized = ::CreateEvent(
  80. NULL,
  81. FALSE, // release one thread (this one) on signal
  82. FALSE, // initially non-signalled
  83. NULL);
  84. if (m_hevInitialized)
  85. {
  86. Progress = eHevShut;
  87. m_hevThreadIsShut = ::CreateEvent(
  88. NULL,
  89. FALSE, // release one thread (this one) on signal
  90. FALSE, // initially non-signalled
  91. NULL);
  92. if (m_hevThreadIsShut)
  93. {
  94. Progress = eThread;
  95. DWORD dwThreadID; // No need to hold onto dwThreadID in member variable.
  96. // All Win32 functions take the handle m_hThread instead.
  97. // The one reason you'd ever want to know this ID is for
  98. // debugging
  99. // Note that there is no corresponding ::CloseHandle(m_hThread).
  100. // That is because the thread goes out of existence on the implicit
  101. // ::ExitThread() when RegistryMonitorTask returns. See documentation of
  102. // ::CreateThread for further details JM 10/22/98
  103. m_hThread = ::CreateThread( NULL,
  104. 0,
  105. (LPTHREAD_START_ROUTINE)RegistryMonitorTask,
  106. this,
  107. 0,
  108. &dwThreadID);
  109. if (m_hThread)
  110. Progress = eOK;
  111. }
  112. }
  113. }
  114. if (m_hThread)
  115. {
  116. // Wait for a set period, if failure then log error msg and wait infinite.
  117. WAIT_INFINITE( m_hevInitialized );
  118. }
  119. else
  120. {
  121. m_dwErr = GetLastError();
  122. CString str;
  123. str.Format(_T("%d"), m_dwErr);
  124. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  125. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  126. SrcLoc.GetSrcFileLineStr(),
  127. (Progress == eHevMon) ? _T("Can't create monitor event")
  128. : (Progress == eHevInit) ? _T("Can't create \"init\" event")
  129. : (Progress == eHevShut) ? _T("Can't create \"shut\" event")
  130. : _T("Can't create thread"),
  131. str,
  132. EV_GTS_ERROR_REGMONITORTHREAD );
  133. SetThreadStatus(eFail);
  134. if (m_hevMonitorRequested)
  135. ::CloseHandle(m_hevMonitorRequested);
  136. if (m_hevInitialized)
  137. ::CloseHandle(m_hevInitialized);
  138. if (m_hevThreadIsShut)
  139. ::CloseHandle(m_hevThreadIsShut);
  140. }
  141. }
  142. CRegistryMonitor::~CRegistryMonitor()
  143. {
  144. ShutDown();
  145. if (m_hevMonitorRequested)
  146. ::CloseHandle(m_hevMonitorRequested);
  147. if (m_hevInitialized)
  148. ::CloseHandle(m_hevInitialized);
  149. if (m_hevThreadIsShut)
  150. ::CloseHandle(m_hevThreadIsShut);
  151. }
  152. void CRegistryMonitor::SetThreadStatus(ThreadStatus ts)
  153. {
  154. Lock();
  155. m_ThreadStatus = ts;
  156. time(&m_time);
  157. Unlock();
  158. }
  159. DWORD CRegistryMonitor::GetStatus(ThreadStatus &ts, DWORD & seconds)
  160. {
  161. time_t timeNow;
  162. Lock();
  163. ts = m_ThreadStatus;
  164. time(&timeNow);
  165. seconds = timeNow - m_time;
  166. Unlock();
  167. return m_dwErr;
  168. }
  169. // Only for use by this class's own destructor.
  170. void CRegistryMonitor::ShutDown()
  171. {
  172. Lock();
  173. m_bShuttingDown = true;
  174. if (m_hThread)
  175. {
  176. ::SetEvent(m_hevMonitorRequested);
  177. Unlock();
  178. // Wait for a set period, if failure then log error msg and wait infinite.
  179. WAIT_INFINITE( m_hevThreadIsShut );
  180. }
  181. else
  182. Unlock();
  183. }
  184. // Must be called on RegistryMonitorTask thread. Handles all work of monitoring the directory.
  185. void CRegistryMonitor::Monitor()
  186. {
  187. enum {eRegChange, eHev /*shutdown*/, eNumHandles};
  188. HANDLE hList[eNumHandles]= { NULL }; // array of handles we can use when waiting for multiple events
  189. HKEY hk= NULL; // handle to key in registry
  190. DWORD dwNErr = 0;
  191. LONG lResult = ERROR_SUCCESS + 1; // scratch for returns of any of several
  192. // calls to Win32 Registry fns. Initialize to arbitrary value
  193. // != ERROR_SUCCESS so we don't close what we haven't opened.
  194. SetThreadStatus(eInit);
  195. try
  196. {
  197. // create an event for registry notification
  198. hList[eRegChange] = ::CreateEvent(NULL, FALSE, FALSE, NULL);
  199. if (hList[eRegChange] == NULL)
  200. {
  201. throw CGenSysException( __FILE__, __LINE__,
  202. _T("Registry notification event"),
  203. EV_GTS_ERROR_REG_NFT_CEVT );
  204. }
  205. CString str = ThisProgramFullKey();
  206. // Technically, KEY_ALL_ACCESS is overkill, but this program should always
  207. // run in an environment where this shoudl succeed, so we haven't bothered
  208. // trying to limit to only the access we need. At the very least, we need
  209. // KEY_QUERY_VALUE | KEY_NOTIFY.
  210. // [BC - 20010302] - Registry access needs to be restricted to run local TShoot
  211. // for certain user accts, such as WinXP built in guest acct. To minimize change
  212. // access only restricted for local TShoot, not online.
  213. REGSAM samRegistryAccess= KEY_ALL_ACCESS;
  214. if(RUNNING_LOCAL_TS())
  215. samRegistryAccess= KEY_QUERY_VALUE | KEY_NOTIFY;
  216. lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE, str, 0, samRegistryAccess, &hk );
  217. if (lResult != ERROR_SUCCESS)
  218. {
  219. CString strError;
  220. strError.Format(_T("%ld"),lResult);
  221. ::SetEvent(m_hevInitialized); // OK to ask this object for registry values;
  222. // of course, you'll just get defaults.
  223. SetThreadStatus(eDefaulting);
  224. throw CGeneralException( __FILE__, __LINE__, strError,
  225. EV_GTS_ERROR_REG_NFT_OPKEY );
  226. }
  227. // ...and we also wait for an explicit wakeup
  228. hList[eHev] = m_hevMonitorRequested;
  229. while (true)
  230. {
  231. if (m_bShuttingDown)
  232. break;
  233. LoadChangedRegistryValues();
  234. ::SetEvent(m_hevInitialized); // OK to ask this object for registry values
  235. // set up to be informed of change
  236. lResult = ::RegNotifyChangeKeyValue( hk,
  237. FALSE,
  238. REG_NOTIFY_CHANGE_LAST_SET,
  239. hList[eRegChange],
  240. TRUE);
  241. if (lResult != ERROR_SUCCESS)
  242. {
  243. CString strError;
  244. strError.Format(_T("%ld"),lResult);
  245. throw CGeneralException( __FILE__, __LINE__, strError,
  246. EV_GTS_ERROR_REG_NFT_SETNTF );
  247. }
  248. ::ResetEvent(m_hevMonitorRequested); // maybe we don't need to do this. JM 9/16/98
  249. SetThreadStatus(eWait);
  250. DWORD dwNotifyObj = WaitForMultipleObjects (
  251. eNumHandles,
  252. hList,
  253. FALSE, // only need one object, not all
  254. INFINITE);
  255. SetThreadStatus(eRun);
  256. }
  257. }
  258. catch (CGenSysException& x)
  259. {
  260. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  261. CEvent::ReportWFEvent( x.GetSrcFileLineStr(),
  262. SrcLoc.GetSrcFileLineStr(),
  263. x.GetErrorMsg(), x.GetSystemErrStr(),
  264. x.GetErrorCode() );
  265. }
  266. catch (CGeneralException& x)
  267. {
  268. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  269. CEvent::ReportWFEvent( x.GetSrcFileLineStr(),
  270. SrcLoc.GetSrcFileLineStr(),
  271. x.GetErrorMsg(), _T("General exception"),
  272. x.GetErrorCode() );
  273. }
  274. catch (...)
  275. {
  276. // Catch any other exception thrown.
  277. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  278. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  279. SrcLoc.GetSrcFileLineStr(),
  280. _T(""), _T(""),
  281. EV_GTS_GEN_EXCEPTION );
  282. }
  283. if (hk != NULL)
  284. ::RegCloseKey(hk);
  285. if (hList[eRegChange] != NULL)
  286. ::CloseHandle(hList[eRegChange]);
  287. SetThreadStatus(eExiting);
  288. }
  289. // Must be called on RegistryMonitorTask thread.
  290. void CRegistryMonitor::AckShutDown()
  291. {
  292. Lock();
  293. ::SetEvent(m_hevThreadIsShut);
  294. Unlock();
  295. }
  296. // get new registry values into our internal data structure.
  297. void CRegistryMonitor::LoadChangedRegistryValues()
  298. {
  299. int maskChanged;
  300. int maskCreated;
  301. Read(maskChanged, maskCreated);
  302. // It actually matters that we set reload delay before we set directory monitor path.
  303. // The first time through here, the call to m_DirectoryMonitor.SetResourceDirectory
  304. // actually sets loose the DirectoryMonitorTask
  305. if ( (maskChanged & eReloadDelay) == eReloadDelay)
  306. {
  307. DWORD dwReloadDelay;
  308. GetNumericInfo(eReloadDelay, dwReloadDelay);
  309. m_DirectoryMonitor.SetReloadDelay(dwReloadDelay);
  310. }
  311. if ( m_bMustStartDirMonitor || (maskChanged & eResourcePath) == eResourcePath)
  312. {
  313. CString strResourcePath;
  314. GetStringInfo(eResourcePath, strResourcePath);
  315. m_DirectoryMonitor.SetResourceDirectory(strResourcePath); // side effect: if the
  316. // directory monitor is not yet started, this tells it what
  317. // directory to monitor so it can start.
  318. m_bMustStartDirMonitor = false;
  319. }
  320. if ( (maskChanged & eDetailedEventLogging) == eDetailedEventLogging)
  321. {
  322. DWORD dw;
  323. GetNumericInfo(eDetailedEventLogging, dw);
  324. CEvent::s_bLogAll = dw ? true : false;
  325. }
  326. if ((maskChanged & eLogFilePath) == eLogFilePath)
  327. {
  328. // Notify the logging object about the new logging file path.
  329. CString strLogFilePath;
  330. GetStringInfo( eLogFilePath, strLogFilePath);
  331. m_pLog->SetLogDirectory( strLogFilePath );
  332. }
  333. if ( m_bMustStartThreadPool
  334. || (maskChanged & eMaxThreads) == eMaxThreads
  335. || (maskChanged & eThreadsPP) == eThreadsPP )
  336. {
  337. m_pThreadPool->ExpandPool(GetDesiredThreadCount());
  338. m_bMustStartThreadPool = false;
  339. }
  340. return;
  341. }
  342. // Main routine of a thread responsible for monitoring the registry.
  343. // INPUT lpParams
  344. // Always returns 0.
  345. /* static */ UINT WINAPI CRegistryMonitor::RegistryMonitorTask(LPVOID lpParams)
  346. {
  347. reinterpret_cast<CRegistryMonitor*>(lpParams)->Monitor();
  348. reinterpret_cast<CRegistryMonitor*>(lpParams)->AckShutDown();
  349. return 0;
  350. }