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.

519 lines
13 KiB

  1. #include "shellprv.h"
  2. #pragma hdrstop
  3. #include <shguidp.h>
  4. #include "uevttmr.h"
  5. // 1. Use dpa callback system to delete the entire hdpa array
  6. EXTERN_C const TCHAR c_szUserEventWindow[] = TEXT("UserEventWindow");
  7. // *** IUnknown methods ***
  8. STDMETHODIMP CUserEventTimer::QueryInterface (REFIID riid, LPVOID * ppvObj)
  9. {
  10. static const QITAB qit[] =
  11. {
  12. QITABENT(CUserEventTimer, IUserEventTimer),
  13. { 0 },
  14. };
  15. return QISearch(this, qit, riid, ppvObj);
  16. }
  17. STDMETHODIMP_(ULONG) CUserEventTimer::AddRef()
  18. {
  19. return InterlockedIncrement(&m_cRef);
  20. }
  21. STDMETHODIMP_(ULONG) CUserEventTimer::Release()
  22. {
  23. if (InterlockedDecrement(&m_cRef))
  24. return m_cRef;
  25. delete this;
  26. // _Destroy();
  27. return 0;
  28. }
  29. // *** Constructor and Destructor ***
  30. CUserEventTimer::CUserEventTimer() : m_cRef(1)
  31. {
  32. }
  33. CUserEventTimer::~CUserEventTimer()
  34. {
  35. _Destroy();
  36. }
  37. // *** IUserEventTimer methods ***
  38. HRESULT CUserEventTimer::SetUserEventTimer(
  39. HWND hWnd,
  40. UINT uCallbackMessage,
  41. UINT uTimerElapse,
  42. IUserEventTimerCallback * pUserEventTimerCallback,
  43. ULONG * puUserEventTimerID
  44. )
  45. {
  46. RIP(puUserEventTimerID != NULL);
  47. HRESULT hr;
  48. // Argument Validation
  49. if (!m_hWnd)
  50. hr = E_FAIL;
  51. else if (!_dpaUserEventInfo)
  52. hr = E_OUTOFMEMORY;
  53. else if (!hWnd && !pUserEventTimerCallback)
  54. hr = E_INVALIDARG;
  55. else if (!puUserEventTimerID || uTimerElapse <= 0)
  56. hr = E_INVALIDARG;
  57. else if (hWnd)
  58. {
  59. int nIndex = _GetTimerDetailsIndex(hWnd, *puUserEventTimerID);
  60. if (nIndex >= 0)
  61. hr = _ResetUserEventTimer(hWnd, uCallbackMessage, uTimerElapse, nIndex);
  62. else
  63. hr = _SetUserEventTimer(hWnd, uCallbackMessage, uTimerElapse, pUserEventTimerCallback, puUserEventTimerID);
  64. }
  65. else
  66. {
  67. ASSERT(pUserEventTimerCallback != NULL);
  68. hr = _SetUserEventTimer(hWnd, uCallbackMessage, uTimerElapse, pUserEventTimerCallback, puUserEventTimerID);
  69. }
  70. return hr;
  71. }
  72. HRESULT CUserEventTimer::InitTimerTickInterval(UINT uTimerTickIntervalMs)
  73. {
  74. // If there is more than one registered client to the user event timer,
  75. // then we cannot change the timer tick interval
  76. if (_dpaUserEventInfo.GetPtrCount() > 0)
  77. return E_FAIL;
  78. if (uTimerTickIntervalMs > 0)
  79. m_uTimerTickInterval = uTimerTickIntervalMs;
  80. else
  81. m_uTimerTickInterval = TIMER_ELAPSE;
  82. return S_OK;
  83. }
  84. HRESULT CUserEventTimer::_SetUserEventTimer(
  85. HWND hWnd,
  86. UINT uCallbackMessage,
  87. UINT uTimerElapse,
  88. IUserEventTimerCallback * pUserEventTimerCallback,
  89. ULONG * puUserEventTimerID
  90. )
  91. {
  92. ASSERT(puUserEventTimerID);
  93. ASSERT(m_uTimerTickInterval > 0);
  94. HRESULT hr = E_OUTOFMEMORY;
  95. USEREVENTINFO * pUserEventInfo = new USEREVENTINFO;
  96. if (pUserEventInfo)
  97. {
  98. pUserEventInfo->hWnd = hWnd;
  99. if (hWnd)
  100. {
  101. pUserEventInfo->uCallbackMessage = uCallbackMessage;
  102. pUserEventInfo->uUserEventTimerID = *puUserEventTimerID;
  103. }
  104. else
  105. {
  106. pUserEventInfo->pUserEventTimerCallback = pUserEventTimerCallback;
  107. }
  108. // Timer ID cannot be zero..
  109. if (!pUserEventInfo->uUserEventTimerID)
  110. {
  111. ULONG uTimerID = _GetNextInternalTimerID(hWnd);
  112. if (uTimerID != -1)
  113. pUserEventInfo->uUserEventTimerID = uTimerID;
  114. }
  115. int nRetInsert = -1;
  116. if (pUserEventInfo->uUserEventTimerID)
  117. {
  118. pUserEventInfo->uTimerElapse = uTimerElapse;
  119. pUserEventInfo->uIntervalCountdown = _CalcNumIntervals(uTimerElapse);
  120. pUserEventInfo->bFirstTime = TRUE;
  121. nRetInsert = _dpaUserEventInfo.AppendPtr(pUserEventInfo);
  122. if (nRetInsert != -1)
  123. {
  124. *puUserEventTimerID = pUserEventInfo->uUserEventTimerID;
  125. if (!_uUserTimerID)
  126. {
  127. _uUserTimerID = SetTimer(m_hWnd, TIMER_ID, m_uTimerTickInterval, NULL);
  128. }
  129. if (!_uUserTimerID)
  130. {
  131. _dpaUserEventInfo.DeletePtr(_dpaUserEventInfo.GetPtrCount()-1);
  132. }
  133. }
  134. }
  135. hr = S_OK;
  136. if (nRetInsert == -1 || _uUserTimerID == 0)
  137. {
  138. *puUserEventTimerID = 0;
  139. delete (pUserEventInfo);
  140. hr = E_FAIL;
  141. }
  142. else if (NULL == hWnd)
  143. {
  144. IUnknown_SetSite(pUserEventTimerCallback, this);
  145. pUserEventTimerCallback->AddRef();
  146. }
  147. }
  148. if (SUCCEEDED(hr))
  149. {
  150. if (!m_dwUserStartTime && _dpaUserEventInfo.GetPtrCount() == 1)
  151. m_dwUserStartTime = GetTickCount();
  152. }
  153. return hr;
  154. }
  155. HRESULT CUserEventTimer::_ResetUserEventTimer(
  156. HWND hWnd,
  157. UINT uCallbackMessage,
  158. UINT uTimerElapse,
  159. int nIndex
  160. )
  161. {
  162. ASSERT(m_hWnd != NULL);
  163. ASSERT(_dpaUserEventInfo != NULL);
  164. ASSERT(hWnd != NULL);
  165. ASSERT(nIndex >= 0);
  166. USEREVENTINFO * pUserEventInfo = _dpaUserEventInfo.GetPtr(nIndex);
  167. ASSERT(pUserEventInfo);
  168. ASSERT(pUserEventInfo->hWnd == hWnd);
  169. pUserEventInfo->uTimerElapse = uTimerElapse;
  170. pUserEventInfo->uIntervalCountdown = _CalcNumIntervals(uTimerElapse);
  171. pUserEventInfo->bFirstTime = TRUE;
  172. return S_OK;
  173. }
  174. int DeleteCB(USEREVENTINFO * lpData1, LPVOID lpData2)
  175. {
  176. USEREVENTINFO * pUserEventInfo = lpData1;
  177. ASSERT(pUserEventInfo);
  178. #ifdef DEBUG
  179. BOOL bErrorCheck = (lpData2 ? (*(BOOL *)lpData2) : FALSE);
  180. if (bErrorCheck)
  181. TraceMsg(TF_WARNING, "CUserEventTimer::s_DeleteCB App hasnt killed timer hwnd = %u, timerID = %u",
  182. pUserEventInfo->hWnd, pUserEventInfo->uUserEventTimerID);
  183. #endif
  184. IUserEventTimerCallback * pUserEventTimerCallback =
  185. (pUserEventInfo->hWnd == NULL) ? pUserEventInfo->pUserEventTimerCallback : NULL;
  186. ASSERT(pUserEventInfo->hWnd || pUserEventTimerCallback);
  187. // _dpaUserEventInfo.DeletePtr(nIndex);
  188. delete pUserEventInfo;
  189. if (pUserEventTimerCallback)
  190. {
  191. IUnknown_SetSite(pUserEventTimerCallback, NULL);
  192. pUserEventTimerCallback->Release();
  193. }
  194. return TRUE;
  195. }
  196. HRESULT CUserEventTimer::GetUserEventTimerElapsed(
  197. HWND hWnd,
  198. ULONG uUserEventTimerID,
  199. UINT * puTimerElapsed)
  200. {
  201. HRESULT hr = E_FAIL;
  202. if (!puTimerElapsed || !hWnd || !m_hWnd || !_dpaUserEventInfo)
  203. return hr;
  204. int nIndex = _GetTimerDetailsIndex(hWnd, uUserEventTimerID);
  205. if (nIndex >= 0)
  206. {
  207. USEREVENTINFO * pUserEventInfo = _dpaUserEventInfo.GetPtr(nIndex);
  208. *puTimerElapsed = _CalcMilliSeconds(
  209. _CalcNumIntervals(pUserEventInfo->uTimerElapse) - pUserEventInfo->uIntervalCountdown
  210. );
  211. hr = S_OK;
  212. }
  213. else
  214. *puTimerElapsed = 0;
  215. return hr;
  216. }
  217. HRESULT CUserEventTimer::KillUserEventTimer(HWND hWnd, ULONG uUserEventTimerID)
  218. {
  219. HRESULT hr = E_FAIL;
  220. if (!m_hWnd || !_dpaUserEventInfo)
  221. return hr;
  222. int nIndex = _GetTimerDetailsIndex(hWnd, uUserEventTimerID);
  223. if (nIndex >= 0)
  224. {
  225. DeleteCB(_dpaUserEventInfo.GetPtr(nIndex), NULL);
  226. _dpaUserEventInfo.DeletePtr(nIndex);
  227. _KillIntervalTimer();
  228. hr = S_OK;
  229. }
  230. else
  231. {
  232. hr = E_FAIL;
  233. }
  234. if (0 == _dpaUserEventInfo.GetPtrCount())
  235. m_dwUserStartTime = 0;
  236. return hr;
  237. }
  238. // Private helpers
  239. HRESULT CUserEventTimer::Init()
  240. {
  241. if (!_CreateWindow())
  242. return E_FAIL;
  243. if (!_dpaUserEventInfo.Create(4))
  244. return E_OUTOFMEMORY;
  245. m_uTimerTickInterval = TIMER_ELAPSE;
  246. ASSERT(!_uUserTimerID);
  247. ASSERT(!m_dwUserStartTime);
  248. return S_OK;
  249. }
  250. BOOL CUserEventTimer::_CreateWindow()
  251. {
  252. if (!m_hWnd)
  253. {
  254. WNDCLASSEX wc;
  255. DWORD dwExStyle = WS_EX_STATICEDGE;
  256. ZeroMemory(&wc, sizeof(wc));
  257. wc.cbSize = sizeof(WNDCLASSEX);
  258. if (!GetClassInfoEx(HINST_THISDLL, c_szUserEventWindow, &wc))
  259. {
  260. wc.lpszClassName = c_szUserEventWindow;
  261. wc.style = CS_DBLCLKS;
  262. wc.lpfnWndProc = s_WndProc;
  263. wc.hInstance = HINST_THISDLL;
  264. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  265. wc.hbrBackground = (HBRUSH)(COLOR_3DFACE+1);
  266. wc.cbWndExtra = sizeof(CUserEventTimer *);
  267. if (!RegisterClassEx(&wc))
  268. {
  269. return FALSE;
  270. }
  271. }
  272. m_hWnd = CreateWindowEx(dwExStyle, c_szUserEventWindow,
  273. NULL, WS_POPUP, 0, 0, 0, 0,
  274. HWND_MESSAGE, NULL, HINST_THISDLL, (void *)this);
  275. if (!m_hWnd)
  276. return FALSE;
  277. }
  278. return TRUE;
  279. }
  280. void CUserEventTimer::_Destroy()
  281. {
  282. if (_dpaUserEventInfo)
  283. {
  284. BOOL bErrorCheck = TRUE;
  285. _dpaUserEventInfo.DestroyCallback(DeleteCB, &bErrorCheck);
  286. }
  287. _KillIntervalTimer();
  288. DestroyWindow(m_hWnd);
  289. }
  290. void CUserEventTimer::_KillIntervalTimer()
  291. {
  292. if (_uUserTimerID)
  293. {
  294. if (_dpaUserEventInfo && _dpaUserEventInfo.GetPtrCount() == 0)
  295. {
  296. KillTimer(m_hWnd, _uUserTimerID);
  297. _uUserTimerID = 0;
  298. }
  299. }
  300. }
  301. ULONG CUserEventTimer::_GetNextInternalTimerID(HWND hWnd)
  302. {
  303. ULONG uStartTimerID = MIN_TIMER_ID;
  304. for (; uStartTimerID <= MAX_TIMER_ID; uStartTimerID++)
  305. {
  306. if (!_IsAssignedTimerID(hWnd, uStartTimerID))
  307. break;
  308. }
  309. if (uStartTimerID > MAX_TIMER_ID)
  310. uStartTimerID = -1;
  311. return uStartTimerID;
  312. }
  313. int CUserEventTimer::_GetTimerDetailsIndex(HWND hWnd, ULONG uUserEventTimerID)
  314. {
  315. if (!_dpaUserEventInfo || !uUserEventTimerID)
  316. return -1;
  317. for (int i = _dpaUserEventInfo.GetPtrCount()-1; i >= 0; i--)
  318. {
  319. USEREVENTINFO * pUserEventInfo = _dpaUserEventInfo.GetPtr(i);
  320. ASSERT(pUserEventInfo);
  321. if (pUserEventInfo->hWnd == hWnd && pUserEventInfo->uUserEventTimerID == uUserEventTimerID)
  322. {
  323. return i;
  324. }
  325. }
  326. return -1;
  327. }
  328. LRESULT CALLBACK CUserEventTimer::s_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  329. {
  330. CUserEventTimer *puet = (CUserEventTimer *)GetWindowLongPtr(hwnd, 0);
  331. if (WM_CREATE == uMsg)
  332. {
  333. CREATESTRUCT *pcs = (CREATESTRUCT *)lParam;
  334. puet = (CUserEventTimer *)pcs->lpCreateParams;
  335. puet->m_hWnd = hwnd;
  336. SetWindowLongPtr(hwnd, 0, (LONG_PTR)puet);
  337. }
  338. return puet ? puet->v_WndProc(uMsg, wParam, lParam) : DefWindowProc(hwnd, uMsg, wParam, lParam);
  339. }
  340. LRESULT CUserEventTimer::v_WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
  341. {
  342. switch (uMsg)
  343. {
  344. case WM_TIMER:
  345. ASSERT(wParam == TIMER_ID);
  346. _OnTimer();
  347. return 0;
  348. default:
  349. return (DefWindowProc(m_hWnd, uMsg, wParam, lParam));
  350. }
  351. }
  352. void CUserEventTimer::_OnTimer()
  353. {
  354. ASSERT(m_hWnd);
  355. if (_dpaUserEventInfo)
  356. {
  357. LONG uTimerDifference = -1;
  358. LASTINPUTINFO lii = {0};
  359. lii.cbSize = sizeof(LASTINPUTINFO);
  360. if (GetLastInputInfo(&lii))
  361. {
  362. if (lii.dwTime < m_dwUserStartTime)
  363. uTimerDifference = 0;
  364. else
  365. uTimerDifference = lii.dwTime - m_dwUserStartTime;
  366. }
  367. LONG uMinTimerDifferenceThreshold = (LONG) (m_uTimerTickInterval * (float)(1-MIN_TIMER_THRESHOLD));
  368. LONG uMaxTimerDifferenceThreshold = (LONG) (m_uTimerTickInterval * (float)(1+MAX_TIMER_THRESHOLD));
  369. for (int i = _dpaUserEventInfo.GetPtrCount()-1; i >= 0; i--)
  370. {
  371. USEREVENTINFO * pUserEventInfo = _dpaUserEventInfo.GetPtr(i);
  372. ASSERT(pUserEventInfo);
  373. if (uTimerDifference != 0)
  374. {
  375. if ( (uTimerDifference == -1) ||
  376. (pUserEventInfo->bFirstTime && uTimerDifference > uMinTimerDifferenceThreshold &&
  377. uTimerDifference <= uMaxTimerDifferenceThreshold) ||
  378. (!pUserEventInfo->bFirstTime && uTimerDifference <= uMaxTimerDifferenceThreshold)
  379. )
  380. {
  381. pUserEventInfo->uIntervalCountdown --;
  382. if (pUserEventInfo->uIntervalCountdown == 0)
  383. {
  384. // Reset the countdown
  385. pUserEventInfo->uIntervalCountdown = _CalcNumIntervals(pUserEventInfo->uTimerElapse);
  386. if (pUserEventInfo->hWnd)
  387. {
  388. PostMessage(pUserEventInfo->hWnd,
  389. pUserEventInfo->uCallbackMessage,
  390. (WPARAM) pUserEventInfo->uTimerElapse,
  391. (LPARAM) pUserEventInfo->uUserEventTimerID);
  392. }
  393. else
  394. {
  395. pUserEventInfo->pUserEventTimerCallback->UserEventTimerProc(
  396. pUserEventInfo->uUserEventTimerID,
  397. pUserEventInfo->uTimerElapse);
  398. }
  399. }
  400. }
  401. }
  402. pUserEventInfo->bFirstTime = FALSE;
  403. }
  404. m_dwUserStartTime = GetTickCount();
  405. }
  406. }
  407. STDAPI CUserEventTimer_CreateInstance(IUnknown* punkOuter, REFIID riid, void **ppv)
  408. {
  409. HRESULT hr = E_OUTOFMEMORY;
  410. CUserEventTimer * pUserEventTimer = new CUserEventTimer();
  411. if (!pUserEventTimer || FAILED(hr = pUserEventTimer->Init()))
  412. {
  413. *ppv = NULL;
  414. }
  415. else
  416. {
  417. hr = pUserEventTimer->QueryInterface(riid, ppv);
  418. pUserEventTimer->Release(); // Already have a ref count from new
  419. }
  420. return hr;
  421. }