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.

1568 lines
41 KiB

  1. //*** uemapp.cpp -- application side of event monitor
  2. // DESCRIPTION
  3. // event generators, actions, helpers, etc.
  4. #include "priv.h"
  5. #include <trayp.h>
  6. #include "sccls.h"
  7. #include "uemapp.h"
  8. #include "uacount.h"
  9. #include "regdb.h"
  10. #include "uareg.h"
  11. #include "resource.h"
  12. #define MAX(a, b) (((a) > (b)) ? (a) : (b))
  13. #define BIT_ASSIGN(dwBits, dwMasks, dwVals) \
  14. (((dwBits) & ~(dwMasks)) | (dwVals))
  15. #define FEATURE_EMDLL 0 // turn this *off* until msoem.dll in setup!
  16. #if FEATURE_EMDLL
  17. #include "mso.h"
  18. #include "msoem.h"
  19. #include "emrule.h"
  20. #include "libem.h" // configure genem.c and other client-side stuff
  21. #include "emdef.h" // rulc-generated
  22. #endif
  23. #if FEATURE_EMDLL
  24. /* UEMEvalIE... */
  25. extern "C" int FInitEm(void);
  26. #else
  27. #define UEMEvalIE(irul, val, ecmd) /*NOTHING*/
  28. #define FInitEm() /*NOTHING*/
  29. #endif
  30. #define DM_UEMTRACE 0
  31. #define DM_UEMTRACE2 0 // verbose
  32. #define DM_IDLEDETECT 0 // TF_CUSTOM2
  33. #define DM_EVTMON TF_UEM
  34. int SHSearchInt(int *psrc, int cnt, int val);
  35. int UEMIIDToInd(const GUID *pguidGrp);
  36. void UEMEnableTimer(UINT uTimeout);
  37. //*** event firers {
  38. //CASSERT(UEMIND_SHELL == 0 && UEMIND_BROWSER == 1);
  39. HRESULT GetUEMLogger(int iCmd, CEMDBLog **p);
  40. CEMDBLog *g_uempDbLog[UEMIND_NSTANDARD + UEMIND_NINSTR];
  41. DWORD g_uemdwFlags /*=0*/; // UAF_* and UAAF_*
  42. // Turning this so that it's a Flat 12hours. You have to explicitly set the SessionTime=0
  43. // in the registry to debug.
  44. #ifdef DEBUG_UEM_TIMEOUTS
  45. #define UAS_SESSTIME UAT_MINUTE1
  46. #else
  47. #define UAS_SESSTIME UAT_HOUR12
  48. #endif
  49. #define UAS_SESSMIN 0
  50. #define UAS_SESSMAX ... none for now ...
  51. DWORD g_dSessTime = UAS_SESSTIME; // session time threshhold
  52. #define UAS_IDLETIME UAT_HOUR12
  53. #define UAS_IDLEMIN 0
  54. #define UAS_IDLEMAX ... none for now ...
  55. DWORD g_dIdleTime = UAS_IDLETIME; // idle time threshhold
  56. #define UAS_CLEANSESS 16
  57. DWORD g_dCleanSess = UAS_CLEANSESS; // cleanup session count threshhold
  58. #if FEATURE_EMDLL // {
  59. #if YY_DELAYED
  60. extern "C" MSOACTTBL *_pacttbl;
  61. extern "C" void DoPendingActions(void);
  62. #endif
  63. //*** UEMEvalIE -- sched and eval event (and generic)
  64. //
  65. void UEMEvalIE(IRUL irul, long val, int eCmd)
  66. {
  67. #ifdef DEBUG
  68. static long iDepth = 0;
  69. #endif
  70. ASSERT(iDepth == 0);
  71. ASSERT(InterlockedIncrement(&iDepth) > 0);
  72. TraceMsg(DM_EVTMON, "uemeie: sched/eval irul=%d val=0x%x eCmd=%d", irul, val, eCmd);
  73. MsoScheduleIrul(irul, val); // e.g. irulIE_UIMENU
  74. MsoScheduleIrul(irulIE_GENERIC, eCmd); // e.g. UEME_UIMENU
  75. MsoEvaluateEvents(rulevtEmIE);
  76. #if YY_DELAYED
  77. if (_pacttbl->pactPending)
  78. DoPendingActions();
  79. #endif
  80. ASSERT(InterlockedDecrement(&iDepth) == 0);
  81. }
  82. #endif // }
  83. void UEMSpecial(int iTab, int iGrp, int eCmd, WPARAM wParam, LPARAM lParam)
  84. {
  85. CEMDBLog *pDbLog;
  86. pDbLog = g_uempDbLog[iGrp];
  87. if (!pDbLog)
  88. {
  89. ASSERT(0);
  90. TraceMsg(TF_ERROR, "uemt: pDbLog not initialized iTab=%d iGrp=%d eCmd=%d wParam=0x%x lParam=0x%x", iTab, iGrp, eCmd, wParam, lParam);
  91. return;
  92. }
  93. switch (eCmd) {
  94. case UEME_DBTRACEA:
  95. TraceMsg(DM_UEMTRACE, "uemt: e=runtrace s=%hs(0x%x)", (int)lParam, (int)lParam);
  96. break;
  97. case UEME_DBTRACEW:
  98. TraceMsg(DM_UEMTRACE, "uemt: e=runtrace s=%ls(0x%x)", (int)lParam, (int)lParam);
  99. break;
  100. #ifdef DEBUG
  101. case UEME_DBSLEEP:
  102. Sleep((DWORD)lParam);
  103. break;
  104. #endif
  105. // UEME_DONE*
  106. case UEME_DONECANCEL:
  107. TraceMsg(DM_UEMTRACE, "uemt: e=donecancel lP=%x", (int)lParam);
  108. break;
  109. // UEME_ERROR*
  110. case UEME_ERRORA:
  111. TraceMsg(DM_UEMTRACE, "uemt: e=errora id=%hs(0x%x)", (LPSTR)lParam, (int)lParam);
  112. break;
  113. case UEME_ERRORW:
  114. TraceMsg(DM_UEMTRACE, "uemt: e=errorw id=%ls(0x%x)", (LPWSTR)lParam, (int)lParam);
  115. break;
  116. case UEME_CTLSESSION:
  117. ASSERT(lParam == -1); // eventually, UAQ_*
  118. pDbLog->SetSession(UAQ_SESSION, (BOOL)wParam);
  119. #ifdef UAAF_INSTR
  120. // might be safer to copy UA.sess rather than inc UA2.sess in parallel?
  121. if (g_uemdwFlags & UAAF_INSTR)
  122. {
  123. if (EVAL(g_uempDbLog[iGrp + UEMIND_NINSTR]))
  124. g_uempDbLog[iGrp + UEMIND_NINSTR]->SetSession(UAQ_SESSION, (BOOL)wParam);
  125. }
  126. #endif
  127. break;
  128. default:
  129. TraceMsg(DM_UEMTRACE, "uemt: e=0x%x(%d) lP=0x%x(%d)", eCmd, eCmd, (int)lParam, (int)lParam);
  130. break;
  131. }
  132. return;
  133. }
  134. #ifdef DEBUG // {
  135. int DBShellMenuValTab[] =
  136. {
  137. 0x8, // UEMC_FILERUN
  138. 401, // IDM_FILERUN
  139. };
  140. TCHAR * DBShellMenuStrTab[] =
  141. {
  142. TEXT("run"),
  143. TEXT("run"),
  144. };
  145. int DBBrowserMenuValTab[] = {
  146. 0x106,
  147. };
  148. TCHAR * DBBrowserMenuStrTab[] = {
  149. TEXT("properties"),
  150. };
  151. int DBBrowserTbarValTab[] = {
  152. 0x124, 0x122,
  153. };
  154. TCHAR * DBBrowserTbarStrTab[] = {
  155. TEXT("stop"),
  156. TEXT("home"),
  157. };
  158. // Function used only in this file, and only in debug,
  159. // so no point in adding to shlwapi
  160. LPTSTR SHSearchMapIntStr(const int *src, const LPTSTR *dst, int cnt, int val)
  161. {
  162. for (; cnt > 0; cnt--, src++, dst++) {
  163. if (*src == val)
  164. return *dst;
  165. }
  166. return (LPTSTR)-1;
  167. }
  168. #endif // }
  169. #define TABDAT(ueme, dope, u1, u2, u3, u4) ueme,
  170. int UemeValTab[] = {
  171. #include "uemedat.h"
  172. };
  173. #undef TABDAT
  174. #define TABDAT(ueme, dope, u1, u2, u3, u4) TEXT(# ueme),
  175. TCHAR *UemeStrTab[] = {
  176. #include "uemedat.h"
  177. };
  178. #undef TABDAT
  179. #define TABDAT(ueme, dope, u1, u2, u3, u4) dope,
  180. char *UemeDopeTab[] = {
  181. #include "uemedat.h"
  182. };
  183. #undef TABDAT
  184. BOOL UEMEncodePidl(IShellFolder *psf, LPITEMIDLIST pidlItem,
  185. LPTSTR pszBuf, DWORD cchBuf, int* piIndexStart, int* pcsidl);
  186. #define MAX_EVENT_NAME 32
  187. //***
  188. // NOTES
  189. // todo: could put more encoding instrs in dope vector (e.g. %pidl, %tstr)
  190. // for now there are only a couple so we hard-code them
  191. void UEMEncode(int iTab, TCHAR *pszEvent, TCHAR *pszEncoded, DWORD cchEncoded, int iGrp, int eCmd, WPARAM wParam, LPARAM lParam)
  192. {
  193. #ifdef DEBUG
  194. TCHAR *pdb2;
  195. #endif
  196. int i, csIdl;
  197. TCHAR szBufTmp[MAX_URL_STRING];
  198. TCHAR szEvent[MAX_PATH];
  199. ASSERT(pszEvent[0] == 0);
  200. ASSERT(pszEncoded == 0 || pszEncoded[0] == 0);
  201. if (iTab == -1) {
  202. lstrcpy(pszEvent, TEXT("UEM?_?"));
  203. //pszEncoded[0] = 0;
  204. }
  205. else {
  206. lstrcpy(pszEvent, UemeStrTab[iTab]);
  207. ASSERT(lstrlen(pszEvent) < MAX_EVENT_NAME);
  208. if (pszEncoded) {
  209. switch (eCmd) {
  210. case UEME_RUNPIDL:
  211. if (UEMEncodePidl((IShellFolder *)wParam, (LPITEMIDLIST)lParam, szBufTmp, SIZECHARS(szBufTmp), &i, &csIdl)) {
  212. wnsprintf(pszEncoded, cchEncoded, TEXT("%s:%%csidl%d%%%s"), pszEvent, csIdl, szBufTmp + i);
  213. }
  214. else {
  215. wnsprintf(pszEncoded, cchEncoded, TEXT("%s:%s"), pszEvent, szBufTmp);
  216. }
  217. break;
  218. case UEME_RUNPATHA:
  219. ASSERT(lstrcmp(pszEvent, TEXT("UEME_RUNPATHA")) == 0);
  220. ASSERT(pszEvent[12] == TEXT('A'));
  221. pszEvent[12] = 0; // nuke the 'A'/'W'
  222. SHAnsiToTChar((LPSTR)lParam, szEvent, MAX_PATH);
  223. if (wParam != -1) {
  224. wnsprintf(pszEncoded, cchEncoded, TEXT("%s:%%csidl%d%%%s"), pszEvent, wParam, szEvent);
  225. }
  226. else {
  227. wnsprintf(pszEncoded, cchEncoded, TEXT("%s:%s"), pszEvent, szEvent);
  228. }
  229. break;
  230. case UEME_RUNPATHW:
  231. ASSERT(lstrcmp(pszEvent, TEXT("UEME_RUNPATHW")) == 0);
  232. ASSERT(pszEvent[12] == TEXT('W'));
  233. pszEvent[12] = 0; // nuke the 'A'/'W'
  234. if (wParam != -1) {
  235. wnsprintf(pszEncoded, cchEncoded, TEXT("%s:%%csidl%d%%%ls"), pszEvent, wParam, (WCHAR*)lParam);
  236. }
  237. else {
  238. wnsprintf(pszEncoded, cchEncoded, TEXT("%s:%ls"), pszEvent, (WCHAR *)lParam);
  239. }
  240. break;
  241. case UEME_RUNCPLA:
  242. ASSERT(lstrcmp(pszEvent, TEXT("UEME_RUNCPLA")) == 0);
  243. ASSERT(pszEvent[11] == TEXT('A'));
  244. pszEvent[11] = 0; // nuke the 'A'/'W'
  245. SHAnsiToTChar((LPSTR)lParam, szEvent, MAX_PATH);
  246. wnsprintf(pszEncoded, cchEncoded, TEXT("%s:%s"), pszEvent, szEvent);
  247. break;
  248. case UEME_RUNCPLW:
  249. ASSERT(lstrcmp(pszEvent, TEXT("UEME_RUNCPLW")) == 0);
  250. ASSERT(pszEvent[11] == TEXT('W'));
  251. pszEvent[11] = 0; // nuke the 'A'/'W'
  252. wnsprintf(pszEncoded, cchEncoded, TEXT("%s:%ls"), pszEvent, (WCHAR *)lParam);
  253. break;
  254. default:
  255. wnsprintf(pszEncoded, cchEncoded, TEXT("%s:0x%x,%x"), pszEvent, (DWORD)wParam, (DWORD)lParam);
  256. break;
  257. }
  258. }
  259. }
  260. #ifdef DEBUG
  261. pdb2 = (TCHAR *)-1;
  262. switch (eCmd) {
  263. case UEME_UIMENU:
  264. switch (iGrp) {
  265. case UEMIND_SHELL:
  266. pdb2 = SHSearchMapIntStr(DBShellMenuValTab, DBShellMenuStrTab, ARRAYSIZE(DBShellMenuValTab), (int)lParam);
  267. break;
  268. case UEMIND_BROWSER:
  269. pdb2 = SHSearchMapIntStr(DBBrowserMenuValTab, DBBrowserMenuStrTab, ARRAYSIZE(DBBrowserMenuValTab), (int)lParam);
  270. break;
  271. default:
  272. break;
  273. }
  274. break;
  275. case UEME_UITOOLBAR:
  276. ASSERT(iGrp == UEMIND_BROWSER);
  277. pdb2 = SHSearchMapIntStr(DBBrowserTbarValTab, DBBrowserTbarStrTab, ARRAYSIZE(DBBrowserTbarValTab), (int)lParam);
  278. break;
  279. default:
  280. break;
  281. }
  282. if (pdb2 != (TCHAR *)-1) {
  283. if (pszEncoded)
  284. wnsprintf(pszEncoded, cchEncoded, TEXT("%s:%s"), pszEvent, pdb2);
  285. }
  286. #endif
  287. return;
  288. }
  289. STDAPI _UEMGetDisplayName(IShellFolder *psf, LPCITEMIDLIST pidl, UINT shgdnf, LPTSTR pszOut, DWORD cchOut)
  290. {
  291. HRESULT hr;
  292. if (psf)
  293. {
  294. ASSERT(pidl == ILFindLastID(pidl));
  295. STRRET str;
  296. hr = psf->GetDisplayNameOf(pidl, shgdnf, &str);
  297. if (SUCCEEDED(hr))
  298. hr = StrRetToBuf(&str, pidl, pszOut, cchOut);
  299. }
  300. else
  301. hr = SHGetNameAndFlags(pidl, shgdnf, pszOut, cchOut, NULL);
  302. return hr;
  303. }
  304. //*** FoldCSIDL -- folder special CSIDLs to keep start menu happy
  305. //
  306. #define FoldCSIDL(csidl) \
  307. ((csidl) == CSIDL_COMMON_PROGRAMS ? CSIDL_PROGRAMS : (csidl))
  308. //*** UemEncodePidl -- encode pidl into csidl and relative path
  309. //
  310. BOOL UEMEncodePidl(IShellFolder *psf, LPITEMIDLIST pidlItem,
  311. LPTSTR pszBuf, DWORD cchBuf, int* piIndexStart, int* pcsidl)
  312. {
  313. static UINT csidlTab[] = { CSIDL_PROGRAMS, CSIDL_COMMON_PROGRAMS, CSIDL_FAVORITES, -1 };
  314. UINT *pcsidlCur;
  315. int i;
  316. TCHAR szFolderPath[MAX_PATH];
  317. _UEMGetDisplayName(psf, pidlItem, SHGDN_FORPARSING, pszBuf, cchBuf);
  318. for (pcsidlCur = csidlTab; *pcsidlCur != (UINT)-1; pcsidlCur++)
  319. {
  320. // perf: assume shell32 caches this (it does)
  321. if (SHGetSpecialFolderPath(NULL, szFolderPath, *pcsidlCur, FALSE))
  322. {
  323. i = PathCommonPrefix(szFolderPath, pszBuf, NULL);
  324. if (i != 0 && i == lstrlen(szFolderPath))
  325. {
  326. *pcsidl = FoldCSIDL(*pcsidlCur);
  327. *piIndexStart = i;
  328. return TRUE;
  329. }
  330. }
  331. }
  332. return FALSE;
  333. }
  334. //*** UEMEvalMsg -- fire event
  335. // ENTRY/EXIT
  336. // pguidGrp 'owner' of event. e.g. shell, browser, joe-app, etc.
  337. // eCmd command. one of UEME_* (standard) or UEME_USER+xxx (custom).
  338. // wP, lP args.
  339. // NOTES
  340. // - pri=1 gotta filter events for privacy issues (esp. Ger). not sure if
  341. // we should add a param saying 'usage' of event or just infer it from the
  342. // event.
  343. // - pri=? gotta encrypt the data we log
  344. // - pri=? change to UemEvalMsg(eCmd, wParam, lParam)
  345. //
  346. void UEMEvalMsg(const GUID *pguidGrp, int eCmd, WPARAM wParam, LPARAM lParam)
  347. {
  348. HRESULT hr;
  349. hr = UEMFireEvent(pguidGrp, eCmd, UEMF_XEVENT, wParam, lParam);
  350. return;
  351. }
  352. STDAPI_(BOOL) UEMGetInfo(const GUID *pguidGrp, int eCmd, WPARAM wParam, LPARAM lParam, LPUEMINFO pui)
  353. {
  354. HRESULT hr;
  355. hr = UEMQueryEvent(pguidGrp, eCmd, wParam, lParam, pui);
  356. return SUCCEEDED(hr);
  357. }
  358. class CUserAssist : public IUserAssist
  359. {
  360. public:
  361. //*** IUnknown
  362. virtual STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppv);
  363. virtual STDMETHODIMP_(ULONG) AddRef(void);
  364. virtual STDMETHODIMP_(ULONG) Release(void);
  365. //*** IUserAssist
  366. virtual STDMETHODIMP FireEvent(const GUID *pguidGrp, int eCmd, DWORD dwFlags, WPARAM wParam, LPARAM lParam);
  367. virtual STDMETHODIMP QueryEvent(const GUID *pguidGrp, int eCmd, WPARAM wParam, LPARAM lParam, LPUEMINFO pui);
  368. virtual STDMETHODIMP SetEvent(const GUID *pguidGrp, int eCmd, WPARAM wParam, LPARAM lParam, LPUEMINFO pui);
  369. protected:
  370. CUserAssist();
  371. HRESULT Initialize();
  372. virtual ~CUserAssist();
  373. friend HRESULT CUserAssist_CI2(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi);
  374. friend void CUserAssist_CleanUp(DWORD dwReason, void *lpvReserved);
  375. friend HRESULT UEMRegisterNotify(UEMCallback pfnUEMCB, void *param);
  376. HRESULT _InitLock();
  377. HRESULT _Lock();
  378. HRESULT _Unlock();
  379. void FireNotify(const GUID *pguidGrp, int eCmd)
  380. {
  381. // Assume that we have the lock
  382. if (_pfnNotifyCB)
  383. _pfnNotifyCB(_param, pguidGrp, eCmd);
  384. }
  385. HRESULT RegisterNotify(UEMCallback pfnUEMCB, void *param)
  386. {
  387. HRESULT hr;
  388. int cTries = 0;
  389. do
  390. {
  391. cTries++;
  392. hr = _Lock();
  393. if (SUCCEEDED(hr))
  394. {
  395. _pfnNotifyCB = pfnUEMCB;
  396. _param = param;
  397. _Unlock();
  398. }
  399. else
  400. {
  401. ::Sleep(100); // wait some for the lock to get freed up
  402. }
  403. }
  404. while (FAILED(hr) && cTries < 20);
  405. return hr;
  406. }
  407. private:
  408. LONG _cRef;
  409. HANDLE _hLock;
  410. UEMCallback _pfnNotifyCB;
  411. void *_param;
  412. };
  413. #define SZ_UALOCK TEXT("_SHuassist.mtx")
  414. void DoLog(CEMDBLog *pDbLog, TCHAR *pszBuf1, TCHAR *pszBuf2)
  415. {
  416. if (pDbLog && *pszBuf1) {
  417. pDbLog->IncCount(pszBuf1);
  418. if (*pszBuf2) {
  419. //ASSERT(iGrp == UEMIND_BROWSER); // not req'd but currently true
  420. pDbLog->IncCount(pszBuf2);
  421. }
  422. }
  423. return;
  424. }
  425. HRESULT CUserAssist::FireEvent(const GUID *pguidGrp, int eCmd, DWORD dwFlags, WPARAM wParam, LPARAM lParam)
  426. {
  427. TCHAR szBuf1[32]; // "UEME_xxx"
  428. TCHAR szBuf2[MAX_URL_STRING]; // "UEME_xxx:0x%x,%x"
  429. int iGrp;
  430. CEMDBLog *pDbLog;
  431. int i, iTab;
  432. char ch;
  433. char *pszDope;
  434. ASSERT(this != 0);
  435. // If called for instrumentation (NOT event monitor) and instrumentation not enabled
  436. // we should exit!
  437. if ((UEMF_INSTRUMENT == (dwFlags & UEMF_MASK)) && (!(g_uemdwFlags & UAAF_INSTR)))
  438. return E_FAIL;
  439. if (g_uemdwFlags & UAAF_NOLOG)
  440. return E_FAIL;
  441. if (eCmd & UEME_FBROWSER) {
  442. ASSERT(0);
  443. ASSERT(IsEqualIID(*pguidGrp, UEMIID_NIL));
  444. pguidGrp = &UEMIID_BROWSER;
  445. eCmd &= ~UEME_FBROWSER;
  446. }
  447. iGrp = UEMIIDToInd(pguidGrp);
  448. pDbLog = g_uempDbLog[iGrp];
  449. TraceMsg(DM_UEMTRACE2, "uemt: eCmd=0x%x wP=0x%x lP=0x%x(%d)", eCmd, wParam, (int)lParam, (int)lParam);
  450. szBuf1[0] = szBuf2[0] = 0;
  451. iTab = SHSearchInt(UemeValTab, ARRAYSIZE(UemeValTab), eCmd);
  452. if (iTab == -1) {
  453. ASSERT(0);
  454. return E_FAIL;
  455. }
  456. pszDope = UemeDopeTab[iTab];
  457. while (ch = *pszDope++) {
  458. switch (ch) {
  459. case 'e':
  460. i = *pszDope++ - '0';
  461. UEMEncode(iTab, szBuf1, i >= 2 ? szBuf2 : NULL, SIZECHARS(szBuf2), iGrp, eCmd, wParam, lParam);
  462. TraceMsg(DM_UEMTRACE, "uemt: %s %s (0x%x %x %x)", szBuf1, szBuf2, eCmd, wParam, lParam);
  463. break;
  464. case 'f':
  465. // make sure we don't cause ourselves trouble in future
  466. // EM only gives us a couple of DWORDs, so we need s.t. like:
  467. // bits(UEMIND_*)+bits(wParam)+bits(lParam) <= bits(DWORD)
  468. // for now we allow 0/-1 in hiword, if/when we use EM we'll
  469. // need to clean that up.
  470. #if 0
  471. // fails for pidls, sigh...
  472. ASSERT((int)(unsigned short)lParam == lParam ||
  473. ((int)(short)lParam == lParam));
  474. UEMEvalIE(irulXxx, fTrue, eCmd);
  475. #endif
  476. break;
  477. case 'l':
  478. if (SUCCEEDED(_Lock())) {
  479. if (dwFlags & UEMF_EVENTMON)
  480. DoLog(pDbLog, szBuf1, szBuf2);
  481. #ifdef UAAF_INSTR
  482. if ((g_uemdwFlags & UAAF_INSTR) && (dwFlags & UEMF_INSTRUMENT))
  483. DoLog(g_uempDbLog[iGrp + UEMIND_NINSTR], szBuf1, szBuf2);
  484. #endif
  485. FireNotify(pguidGrp, eCmd);
  486. _Unlock();
  487. }
  488. break;
  489. case 'x':
  490. TraceMsg(DM_UEMTRACE, "uemt: NYI");
  491. goto Lnodope;
  492. #ifdef DEBUG
  493. case '!':
  494. ASSERT(0);
  495. break;
  496. #endif
  497. case '@':
  498. if (SUCCEEDED(_Lock())) {
  499. UEMSpecial(iTab, iGrp, eCmd, wParam, lParam);
  500. FireNotify(pguidGrp, eCmd);
  501. _Unlock();
  502. }
  503. break;
  504. }
  505. }
  506. Lnodope:
  507. return S_OK;
  508. }
  509. HRESULT CUserAssist::QueryEvent(const GUID *pguidGrp, int eCmd, WPARAM wParam, LPARAM lParam, LPUEMINFO pui)
  510. {
  511. int iGrp;
  512. CEMDBLog *pDbLog;
  513. TCHAR szBuf1[32]; // "UEME_xxx"
  514. TCHAR szBuf2[MAX_URL_STRING]; // "UEME_xxx:0x%x,%x"
  515. ASSERT(this != 0);
  516. if (g_uemdwFlags & UAAF_NOLOG)
  517. return E_FAIL;
  518. ASSERT(eCmd == UEME_RUNPIDL
  519. || eCmd == UEME_RUNPATH || eCmd == UEME_RUNWMCMD); // others NYI
  520. ASSERT(pui->cbSize == SIZEOF(*pui));
  521. // pui->dwVersion?
  522. iGrp = UEMIIDToInd(pguidGrp);
  523. pDbLog = g_uempDbLog[iGrp];
  524. TraceMsg(DM_UEMTRACE2, "uemgi: eCmd=0x%x wP=0x%x lP=0x%x(%d)", eCmd, wParam, (int)lParam, (int)lParam);
  525. szBuf1[0] = szBuf2[0] = 0;
  526. int iTab = SHSearchInt(UemeValTab, ARRAYSIZE(UemeValTab), eCmd);
  527. UEMEncode(iTab, szBuf1, szBuf2, SIZECHARS(szBuf2), iGrp, eCmd, wParam, lParam);
  528. int cHit;
  529. //if (SUCCEEDED(_Lock()))
  530. cHit = pDbLog->GetCount(szBuf2);
  531. //_Unlock();
  532. TraceMsg(DM_UEMTRACE, "uemgi: cHit=%d psz=%s", cHit, szBuf2);
  533. if (pui->dwMask & UEIM_HIT)
  534. {
  535. pui->cHit = cHit;
  536. }
  537. if (pui->dwMask & UEIM_FILETIME)
  538. {
  539. pui->ftExecute = pDbLog->GetFileTime(szBuf2);
  540. }
  541. return S_OK;
  542. }
  543. HRESULT CUserAssist::SetEvent(const GUID *pguidGrp, int eCmd, WPARAM wParam, LPARAM lParam, LPUEMINFO pui)
  544. {
  545. int iGrp;
  546. CEMDBLog *pDbLog;
  547. TCHAR szBuf1[32]; // "UEME_xxx"
  548. TCHAR szBuf2[MAX_URL_STRING]; // "UEME_xxx:0x%x,%x"
  549. ASSERT(this != 0);
  550. if (g_uemdwFlags & UAAF_NOLOG)
  551. return E_FAIL;
  552. ASSERT(pui->cbSize == SIZEOF(*pui));
  553. // pui->dwVersion?
  554. iGrp = UEMIIDToInd(pguidGrp);
  555. pDbLog = g_uempDbLog[iGrp];
  556. TraceMsg(DM_UEMTRACE2, "uemgi: eCmd=0x%x wP=0x%x lP=0x%x(%d)", eCmd, wParam, (int)lParam, (int)lParam);
  557. szBuf1[0] = szBuf2[0] = 0;
  558. int iTab = SHSearchInt(UemeValTab, ARRAYSIZE(UemeValTab), eCmd);
  559. UEMEncode(iTab, szBuf1, szBuf2, SIZECHARS(szBuf2), iGrp, eCmd, wParam, lParam);
  560. pui->dwMask &= UEIM_HIT | UEIM_FILETIME; // what we support
  561. if (pui->dwMask && SUCCEEDED(_Lock())) {
  562. if (pui->dwMask & UEIM_HIT) {
  563. pDbLog->SetCount(szBuf2, pui->cHit);
  564. }
  565. if (pui->dwMask & UEIM_FILETIME) {
  566. pDbLog->SetFileTime(szBuf2, &pui->ftExecute);
  567. }
  568. _Unlock();
  569. }
  570. return S_OK;
  571. }
  572. //*** CUserAssist::CCI,ctor/dtor/init {
  573. IUnknown *g_uempUaSingleton;
  574. //*** CUserAssist_CreateInstance -- manage *singleton* instance
  575. //
  576. HRESULT CUserAssist_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
  577. {
  578. HRESULT hr = E_FAIL;
  579. if (g_uempUaSingleton == 0) {
  580. IUnknown *pua;
  581. hr = CUserAssist_CI2(pUnkOuter, &pua, poi);
  582. if (pua)
  583. {
  584. ENTERCRITICAL;
  585. if (g_uempUaSingleton == 0)
  586. {
  587. // Now the global owns the ref.
  588. g_uempUaSingleton = pua; // xfer refcnt
  589. pua = NULL;
  590. }
  591. LEAVECRITICAL;
  592. if (pua)
  593. {
  594. // somebody beat us.
  595. // free up the 2nd one we just created, and use new one
  596. TraceMsg(DM_UEMTRACE, "sl.cua_ci: undo race");
  597. pua->Release();
  598. }
  599. // Now, the caller gets it's own ref.
  600. g_uempUaSingleton->AddRef();
  601. TraceMsg(DM_UEMTRACE, "sl.cua_ci: create pua=0x%x g_uempUaSingleton=%x", pua, g_uempUaSingleton);
  602. }
  603. }
  604. else {
  605. g_uempUaSingleton->AddRef();
  606. }
  607. TraceMsg(DM_UEMTRACE, "sl.cua_ci: ret g_uempUaSingleton=0x%x", g_uempUaSingleton);
  608. *ppunk = g_uempUaSingleton;
  609. return *ppunk ? S_OK : hr;
  610. }
  611. //*** CUserAssist_CI2 -- *always* create instance
  612. //
  613. HRESULT CUserAssist_CI2(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
  614. {
  615. CUserAssist * p = new CUserAssist();
  616. if (p && FAILED(p->Initialize())) {
  617. delete p;
  618. p = NULL;
  619. }
  620. if (p) {
  621. *ppunk = SAFECAST(p, IUserAssist*);
  622. return S_OK;
  623. }
  624. *ppunk = NULL;
  625. return E_OUTOFMEMORY;
  626. }
  627. extern "C"
  628. HRESULT UEMRegisterNotify(UEMCallback pfnUEMCB, void *param)
  629. {
  630. HRESULT hr = E_UNEXPECTED;
  631. if (g_uempUaSingleton)
  632. {
  633. CUserAssist *pua = reinterpret_cast<CUserAssist *>(g_uempUaSingleton);
  634. hr = pua->RegisterNotify(pfnUEMCB, param);
  635. }
  636. return hr;
  637. }
  638. extern void GetUEMSettings();
  639. DWORD g_dwSessionStart; // When did this session start?
  640. #if defined(_M_IX86) && (_MSC_VER < 1200)
  641. #pragma optimize("", off)
  642. #define BUG_OPTIMIZE // restore, see below
  643. #endif
  644. //***
  645. HRESULT CUserAssist::Initialize()
  646. {
  647. HRESULT hr = S_OK;
  648. ASSERT(UEMIND_SHELL == 0 && UEMIND_BROWSER == 1);
  649. hr = _InitLock();
  650. // get standard loggers
  651. if (SUCCEEDED(hr))
  652. hr = GetUEMLogger(UEMIND_SHELL, &g_uempDbLog[UEMIND_SHELL]);
  653. if (SUCCEEDED(hr))
  654. hr = GetUEMLogger(UEMIND_BROWSER, &g_uempDbLog[UEMIND_BROWSER]);
  655. if (SUCCEEDED(hr)) {
  656. FInitEm();
  657. }
  658. GetUEMSettings();
  659. #define UAXF_XSETTINGS (UAXF_NOPURGE|UAXF_BACKUP|UAXF_NOENCRYPT)
  660. if (g_uempDbLog[UEMIND_SHELL])
  661. {
  662. g_uempDbLog[UEMIND_SHELL]->_SetFlags(UAXF_XSETTINGS, g_uemdwFlags & UAXF_XSETTINGS);
  663. // n.b. just for shell (browser no need, instr no decay)
  664. g_uempDbLog[UEMIND_SHELL]->GarbageCollect(FALSE);
  665. }
  666. if (g_uempDbLog[UEMIND_BROWSER])
  667. {
  668. g_uempDbLog[UEMIND_BROWSER]->_SetFlags(UAXF_XSETTINGS, g_uemdwFlags & UAXF_XSETTINGS);
  669. g_uempDbLog[UEMIND_BROWSER]->GarbageCollect(FALSE);
  670. }
  671. #ifdef UAAF_INSTR
  672. if (g_uemdwFlags & UAAF_INSTR) {
  673. if (SUCCEEDED(hr))
  674. hr = GetUEMLogger(UEMIND_SHELL2, &g_uempDbLog[UEMIND_SHELL2]);
  675. if (SUCCEEDED(hr))
  676. hr = GetUEMLogger(UEMIND_BROWSER2, &g_uempDbLog[UEMIND_BROWSER2]);
  677. if (g_uempDbLog[UEMIND_SHELL2]) {
  678. g_uempDbLog[UEMIND_SHELL2]->_SetFlags(UAXF_XSETTINGS, g_uemdwFlags & UAXF_XSETTINGS);
  679. g_uempDbLog[UEMIND_SHELL2]->_SetFlags(UAXF_NODECAY, UAXF_NODECAY);
  680. }
  681. if (g_uempDbLog[UEMIND_BROWSER2]) {
  682. g_uempDbLog[UEMIND_BROWSER2]->_SetFlags(UAXF_XSETTINGS, g_uemdwFlags & UAXF_XSETTINGS);
  683. g_uempDbLog[UEMIND_BROWSER2]->_SetFlags(UAXF_NODECAY, UAXF_NODECAY);
  684. }
  685. }
  686. #endif
  687. g_dwSessionStart = GetTickCount();
  688. UEMEnableTimer(UATTOMSEC(g_dIdleTime));
  689. return hr;
  690. }
  691. #ifdef BUG_OPTIMIZE
  692. #pragma optimize("", on)
  693. #undef BUG_OPTIMIZE
  694. #endif
  695. void CEMDBLog_CleanUp();
  696. //*** CUserAssist_CleanUp -- free up the world (on DLL_PROCESS_DETACH)
  697. // NOTES
  698. // a bit hoaky right now since our UEMLog object isn't really refcnt'ed
  699. void CUserAssist_CleanUp(DWORD dwReason, void *lpvReserved)
  700. {
  701. int i;
  702. IUnknown *pUa;
  703. ASSERT(dwReason == DLL_PROCESS_DETACH);
  704. if (lpvReserved != 0) {
  705. // on process termination, *don't* nuke us since:
  706. // - safety: other DLLs in our process may still be using us, and
  707. // they'll blow up when they reference us if we're freed
  708. // - leaks: process termination will free us up when all is done,
  709. // so there's no worry about a leak
  710. TraceMsg(DM_UEMTRACE, "bui.cua_cu: skip cleanup (end process/non-FreeLibrary)");
  711. return;
  712. }
  713. // otherwise, on FreeLibrary, *do* nuke us since:
  714. // - safety: our refcnt is 0, so nobody is using us any more
  715. // - leaks: multiple Load/FreeLibrary calls will cause a leak if we
  716. // don't free ourselves here
  717. //ENTERCRITICAL;
  718. TraceMsg(DM_UEMTRACE, "bui.cua_cu: cleaning up");
  719. UEMEnableTimer(0);
  720. // free cache (and make sure we'll GPF if we party on it further)
  721. for (i = 0; i < UEMIND_NSTANDARD + UEMIND_NINSTR; i++) {
  722. // UEMIND_SHELL, UEMIND_BROWSER, UEMIND_SHELL2, UEMIND_BROWSER2
  723. InterlockedExchangePointer((void**) &g_uempDbLog[i], (LPVOID) -1);
  724. }
  725. // free 'real' guy
  726. CEMDBLog_CleanUp();
  727. // free THIS
  728. if (pUa = (IUnknown *)InterlockedExchangePointer((void**) &g_uempUaSingleton, (LPVOID) -1)) {
  729. delete SAFECAST(pUa, CUserAssist *);
  730. }
  731. //LEAVECRITICAL;
  732. }
  733. DWORD Reg_GetFlags(DWORD dwInit, HKEY hk, LPCTSTR pszSubkey, LPCTSTR const pszNameTab[], DWORD *dwMaskTab, int cTab)
  734. {
  735. int i;
  736. DWORD dwMasks, dwVals;
  737. dwMasks = dwVals = 0;
  738. for (i = 0; i < cTab; i++) {
  739. DWORD dwData, cbSize = SIZEOF(dwData);
  740. if (SHGetValue(hk, pszSubkey, pszNameTab[i], NULL, &dwData, &cbSize) == ERROR_SUCCESS) {
  741. TraceMsg(DM_UEMTRACE, "ua: regkey %s\\%s=0x%x", pszSubkey, pszNameTab[i], dwData);
  742. dwMasks |= dwMaskTab[i];
  743. if (dwData)
  744. dwVals |= dwMaskTab[i];
  745. }
  746. }
  747. dwInit = BIT_ASSIGN(dwInit, dwMasks, dwVals);
  748. TraceMsg(DM_UEMTRACE, "ua.grs: ret 0x%x", dwInit);
  749. return dwInit;
  750. }
  751. void Reg_GetVals(HKEY hk, LPCTSTR pszSubkey, LPCTSTR const pszNameTab[], DWORD **dwValTab, int cTab)
  752. {
  753. for (int i = 0; i < cTab; i++) {
  754. DWORD dwData, cbSize = SIZEOF(dwData);
  755. if (SHGetValue(hk, pszSubkey, pszNameTab[i], NULL, &dwData, &cbSize) == ERROR_SUCCESS) {
  756. TraceMsg(DM_UEMTRACE, "ua: regkey %s/%s=0x%x", pszSubkey, pszNameTab[i], dwData);
  757. *dwValTab[i] = dwData;
  758. }
  759. }
  760. }
  761. void GetUEMSettings()
  762. {
  763. HKEY hk = SHGetShellKey(SHELLKEY_HKCU_EXPLORER, NULL, FALSE);
  764. if (hk)
  765. {
  766. static const LPCTSTR pszName1Tab[] = {
  767. SZ_NOPURGE , SZ_BACKUP , SZ_NOLOG , SZ_INSTRUMENT, SZ_NOENCRYPT,
  768. };
  769. static DWORD dwMask1Tab[] = {
  770. UAXF_NOPURGE, UAXF_BACKUP, UAAF_NOLOG, UAAF_INSTR , UAXF_NOENCRYPT,
  771. };
  772. static const LPCTSTR pszName2Tab[] = { SZ_SESSTIME, SZ_IDLETIME , SZ_CLEANTIME, };
  773. static DWORD *dwVal2Tab[] = { &g_dSessTime, &g_dIdleTime, &g_dCleanSess,};
  774. g_uemdwFlags = Reg_GetFlags(g_uemdwFlags, hk, SZ_UASSIST TEXT("\\") SZ_SETTINGS, pszName1Tab, dwMask1Tab, ARRAYSIZE(pszName1Tab));
  775. TraceMsg(DM_UEMTRACE, "ua: g_uemdwFlags=0x%x", g_uemdwFlags);
  776. Reg_GetVals(hk, SZ_UASSIST TEXT("\\") SZ_SETTINGS, pszName2Tab, dwVal2Tab, ARRAYSIZE(pszName2Tab));
  777. if (!((int)UAS_SESSMIN <= (int)g_dSessTime /*&& g_dSessTime<=UAS_SESSMAX*/))
  778. g_dSessTime = UAS_SESSTIME;
  779. if (!((int)UAS_IDLEMIN <= (int)g_dIdleTime /*&& g_dIdleTime<=UAS_IDLEMAX*/))
  780. g_dIdleTime = UAS_IDLETIME;
  781. RegCloseKey(hk);
  782. }
  783. if (SHRestricted2(REST_NoUserAssist, NULL, 0)) {
  784. TraceMsg(DM_WARNING, "ua: restrict off!");
  785. g_uemdwFlags |= UAAF_NOLOG;
  786. g_uemdwFlags &= ~UAAF_INSTR; // paranoia (UAAF_NOLOG should be enuf)
  787. }
  788. #ifdef DEBUG
  789. if (g_uemdwFlags & UAAF_NOLOG)
  790. TraceMsg(DM_WARNING, "ua: logging off!");
  791. #endif
  792. return;
  793. }
  794. CUserAssist::CUserAssist() : _cRef(1)
  795. {
  796. return;
  797. }
  798. //***
  799. // NOTES
  800. // n.b. we're only called on DLL_PROCESS_DETACH (refcnt never really
  801. // goes to 0).
  802. CUserAssist::~CUserAssist()
  803. {
  804. if (_hLock)
  805. CloseHandle(_hLock);
  806. #if 1 // 981022 breadcrumbs for stress failure (see if we're double freed)
  807. //memcpy((BYTE *)_hLock, "CUAd", 4);
  808. _hLock = (void *)0x77777777;
  809. #endif
  810. return;
  811. }
  812. // }
  813. //*** CUserAssist::IUnknown::* {
  814. ULONG CUserAssist::AddRef()
  815. {
  816. TraceMsg(DM_UEMTRACE2, "cua.ar: _cRef=%d++", _cRef);
  817. return InterlockedIncrement(&_cRef);
  818. }
  819. ULONG CUserAssist::Release()
  820. {
  821. ASSERT(_cRef > 0);
  822. TraceMsg(DM_UEMTRACE2, "cua.r: _cRef=%d--", _cRef);
  823. // n.b. returns <0,=0,>0 (not actual dec result)
  824. if (InterlockedDecrement(&_cRef))
  825. return _cRef;
  826. delete this;
  827. return 0;
  828. }
  829. HRESULT CUserAssist::QueryInterface(REFIID riid, void **ppvObj)
  830. {
  831. static const QITAB qit[] = {
  832. QITABENT(CUserAssist, IUserAssist), // IID_IUserAssist
  833. { 0 },
  834. };
  835. return QISearch(this, qit, riid, ppvObj);
  836. }
  837. // }
  838. //*** locking stuff {
  839. HRESULT CUserAssist::_InitLock()
  840. {
  841. HRESULT hr = S_OK;
  842. if ((_hLock = CreateMutex(NULL, FALSE, SZ_UALOCK)) == NULL) {
  843. TraceMsg(TF_ERROR, "cua.i: no mutex");
  844. hr = E_FAIL;
  845. }
  846. return hr;
  847. }
  848. #define LOCK_TIMEOUT 0 // immediate timeout, should be rare
  849. HRESULT CUserAssist::_Lock()
  850. {
  851. DWORD dwRes;
  852. dwRes = WaitForSingleObject(_hLock, LOCK_TIMEOUT);
  853. switch (dwRes) {
  854. case WAIT_ABANDONED:
  855. return S_FALSE;
  856. case WAIT_OBJECT_0:
  857. return S_OK;
  858. case WAIT_TIMEOUT:
  859. TraceMsg(DM_UEMTRACE, "cua.l: locked (timeout)");
  860. return E_FAIL;
  861. }
  862. /*NOTREACHED*/
  863. return E_FAIL;
  864. }
  865. HRESULT CUserAssist::_Unlock()
  866. {
  867. ReleaseMutex(_hLock);
  868. return S_OK;
  869. }
  870. // }
  871. //*** timer stuff {
  872. DWORD_PTR g_idTimer;
  873. BOOL g_fIdle /*=FALSE*/;
  874. #if !(_WIN32_WINNT >= 0x0500) // {
  875. #define GetLastInputInfo UEMGetLastInputInfo
  876. typedef struct {
  877. UINT cbSize;
  878. DWORD dwTime;
  879. } LASTINPUTINFO;
  880. DWORD g_dwTime; // prev GetTickCount
  881. int g_csKeys; // prev GetKeyboardState
  882. int g_csCursor; // prev GetCursorPos
  883. BOOL (*g_pfnGLII)(LASTINPUTINFO *plii); // 'real' version
  884. //*** memsum -- checksum bytes
  885. //
  886. int memsum(void *pv, int n)
  887. {
  888. unsigned char *pb = (unsigned char *)pv;
  889. int sum = 0;
  890. while (n-- > 0)
  891. sum += *pb++;
  892. return sum;
  893. }
  894. //*** UEMGetLastInputInfo -- simulate (sort of...) GetLastInputInfo
  895. // DESCRIPTION
  896. // we fake it big time. our detection of 'currently non-idle' is pretty
  897. // good, but the the actual *time* we were idle is pretty iffy. each time
  898. // we're called defines a checkpoint. any time the new checkpoint differs
  899. // from the old one, we update our (approx) idle start point.
  900. BOOL UEMGetLastInputInfo(LASTINPUTINFO *plii)
  901. {
  902. int csCursor, csKeys;
  903. POINT ptCursor;
  904. BYTE ksKeys[256]; // per GetKeyboardState spec
  905. if (g_dwTime == 0) {
  906. // 1st time here...
  907. g_dwTime = GetTickCount();
  908. g_csCursor = g_csKeys = -1;
  909. // GetProcAddress only accepts ANSI.
  910. *(FARPROC *)&g_pfnGLII = GetProcAddress(GetModuleHandle(TEXT("user32.dll")),
  911. "GetLastInputInfo");
  912. TraceMsg(DM_UEMTRACE, "bui.glii: init g_dwTime=%d pfn=0x%x", g_dwTime, g_pfnGLII);
  913. }
  914. #if 1 // 980313 adp: off until we can test it!
  915. // 1st try the easy (and exact) way...
  916. if (g_pfnGLII)
  917. return (*g_pfnGLII)(plii);
  918. #endif
  919. // now the hard (and approximate) way...
  920. csCursor = csKeys = -1;
  921. if (GetCursorPos(&ptCursor))
  922. csCursor = memsum(&ptCursor, SIZEOF(ptCursor));
  923. if (GetKeyboardState(ksKeys))
  924. csKeys = memsum(ksKeys, SIZEOF(ksKeys));
  925. if (csCursor != g_csCursor || csKeys != g_csKeys
  926. || (csCursor == -1 && csKeys == -1)) {
  927. TraceMsg(DM_UEMTRACE, "bui.glli: !idle cur=0x%x cur'=%x keys=%x keys'=%x gtc(old)=%x",
  928. g_csCursor, csCursor, g_csKeys, csKeys, g_dwTime);
  929. g_dwTime = GetTickCount();
  930. g_csCursor = csCursor;
  931. g_csKeys = csKeys;
  932. }
  933. plii->dwTime = g_dwTime;
  934. TraceMsg(DM_UEMTRACE, "bui.uastp: !nt5, simulate GLII()=%d", plii->dwTime);
  935. return TRUE;
  936. }
  937. #endif // }
  938. LRESULT UEMSendTrayMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
  939. {
  940. //
  941. // We may be sending a function pointer, so make sure the target window
  942. // is in our address space.
  943. //
  944. // We need to revalidate that g_hwndTray really is the
  945. // tray window, because Explorer may have crashed, and then the
  946. // window handle got recycled into our process, and so we send
  947. // a random message to a window that isn't what we think it is.
  948. // (raymondc)
  949. //
  950. HWND hwndTray;
  951. DWORD dwPid;
  952. LRESULT lres;
  953. hwndTray = GetTrayWindow();
  954. if (IsWindow(hwndTray) &&
  955. GetWindowThreadProcessId(hwndTray, &dwPid) &&
  956. dwPid == GetCurrentProcessId()) {
  957. lres = SendMessage(hwndTray, uMsg, wParam, lParam);
  958. } else {
  959. lres = 0;
  960. }
  961. return lres;
  962. }
  963. //***
  964. //
  965. // UEMTimerProc
  966. //
  967. // Periodically checks if the user has gone idle.
  968. //
  969. // Rules for session increment:
  970. //
  971. // No session lasts longer than g_dIdleTime units.
  972. //
  973. // If the user remains idle for a long time, keep bumping the
  974. // "start of session" timer so time spent idle does not count towards
  975. // the new session.
  976. //
  977. void CALLBACK UEMTimerProc(HWND hwnd, UINT uMsg, UINT idEvt, DWORD dwNow)
  978. {
  979. #ifdef DEBUG
  980. static long iDepth; // make sure we don't get 2 ticks
  981. #endif
  982. UINT dwIdleTime; // mSec
  983. LASTINPUTINFO lii;
  984. ASSERT(iDepth == 0);
  985. ASSERT(InterlockedIncrement(&iDepth) > 0);
  986. UEMEnableTimer(0);
  987. dwIdleTime = UATTOMSEC(g_dIdleTime); // convert to mSec's (again...)
  988. lii.cbSize = SIZEOF(lii);
  989. if (GetLastInputInfo(&lii)) {
  990. DWORD dwNow = GetTickCount();
  991. TraceMsg(DM_IDLEDETECT, "UEM.tp: now-start=%d, now-last=%d",
  992. dwNow - g_dwSessionStart, dwNow - lii.dwTime);
  993. if (!g_fIdle && dwNow - g_dwSessionStart >= dwIdleTime)
  994. {
  995. g_fIdle = TRUE;
  996. g_dwSessionStart = dwNow;
  997. TraceMsg(DM_IDLEDETECT, "UEM.tp: IncrementSession");
  998. UEMFireEvent(&UEMIID_SHELL, UEME_CTLSESSION, UEMF_XEVENT, TRUE, -1);
  999. UEMFireEvent(&UEMIID_BROWSER, UEME_CTLSESSION, UEMF_XEVENT, TRUE, -1);
  1000. UEMSendTrayMessage(TM_REFRESH, 0, 0); // RefreshStartMenu
  1001. }
  1002. //
  1003. // Get out of idle mode if the user has done anything since
  1004. // the session started. And mark the session as having started
  1005. // at the point the user did something.
  1006. //
  1007. if (dwNow - lii.dwTime < dwNow - g_dwSessionStart) {
  1008. TraceMsg(DM_IDLEDETECT, "UEM.tp: not idle; starting new session");
  1009. g_dwSessionStart = lii.dwTime;
  1010. g_fIdle = FALSE;
  1011. }
  1012. //
  1013. // Now decide how much longer before the next interesting event.
  1014. //
  1015. DWORD dwWait = g_fIdle ? dwIdleTime : dwIdleTime - (dwNow - g_dwSessionStart);
  1016. TraceMsg(DM_UEMTRACE, "UEM.tp: sleep=%d", dwWait);
  1017. UEMEnableTimer(dwWait);
  1018. }
  1019. //else timer left disabled
  1020. ASSERT(InterlockedDecrement(&iDepth) == 0);
  1021. return;
  1022. }
  1023. //*** UEMEnableTimer -- turn timer on/off
  1024. // ENTRY
  1025. // uTimeout delay in mSec; 0 means disable
  1026. void UEMEnableTimer(UINT uTimeout)
  1027. {
  1028. #if !(_WIN32_WINNT >= 0x0500)
  1029. static BOOL fVirg = TRUE; // 1st time thru?
  1030. if (fVirg) {
  1031. LASTINPUTINFO lii;
  1032. fVirg = FALSE;
  1033. lii.cbSize = SIZEOF(lii);
  1034. GetLastInputInfo(&lii); // prime it in case it's simulated
  1035. }
  1036. #endif
  1037. if (uTimeout) {
  1038. // ASSERT(!g_idTimer); // race window can hit this assert spuriously
  1039. g_idTimer = UEMSendTrayMessage(TM_SETTIMER, uTimeout, (LPARAM)UEMTimerProc);
  1040. }
  1041. else if (g_idTimer) {
  1042. UEMSendTrayMessage(TM_KILLTIMER, 0, g_idTimer);
  1043. g_idTimer = 0;
  1044. }
  1045. return;
  1046. }
  1047. // }
  1048. // }
  1049. //*** utils {
  1050. //*** FAST_IsEqualIID -- fast compare
  1051. // (cast to 'int' so don't get overloaded ==)
  1052. #define FAST_IsEqualIID(piid1, piid2) ((int) (piid1) == (int) (piid2))
  1053. //***
  1054. // ENTRY/EXIT
  1055. // iGuid (return) index of GUID in table, o.w. -1 if not found
  1056. // NOTES
  1057. // move to shlwapi if this is needed some place other than here.
  1058. int SHSearchIID(IID **pguidTab, int cnt, IID *pguidVal)
  1059. {
  1060. IID **pguid;
  1061. BOOL fInt;
  1062. pguid = pguidTab;
  1063. fInt = (pguidVal == 0 || pguidVal == (IID *)-1);
  1064. for (; cnt > 0; cnt--, pguid++) {
  1065. if (fInt) {
  1066. if (*pguid == pguidVal)
  1067. goto Lfound;
  1068. }
  1069. else if (IsEqualIID(**pguid, *pguidVal)) {
  1070. Lfound:
  1071. return (int)(pguid - pguidTab);
  1072. }
  1073. }
  1074. return -1;
  1075. }
  1076. int SHSearchInt(int *psrc, int cnt, int val)
  1077. {
  1078. int *pcur;
  1079. pcur = psrc;
  1080. for (; cnt > 0; cnt--, pcur++) {
  1081. if (*pcur == val)
  1082. return (int)(pcur - psrc);
  1083. }
  1084. return -1;
  1085. }
  1086. int UEMIIDToInd(const GUID *pguidGrp)
  1087. {
  1088. int iGrp;
  1089. if (IsEqualIID(*pguidGrp, UEMIID_BROWSER))
  1090. iGrp = UEMIND_BROWSER;
  1091. else if (IsEqualIID(*pguidGrp, UEMIID_SHELL))
  1092. iGrp = UEMIND_SHELL;
  1093. else
  1094. {
  1095. ASSERT(IsEqualIID(*pguidGrp, UEMIID_NIL));
  1096. iGrp = UEMIND_SHELL;
  1097. }
  1098. return iGrp;
  1099. }
  1100. // }
  1101. // {
  1102. #if 0 // currently unused
  1103. // glue for standalone test {
  1104. //#define XXX_TEST
  1105. //#define XXX_DEBUG
  1106. #ifdef XXX_TEST // {
  1107. #include "stdlib.h"
  1108. #include "string.h"
  1109. #include "stdio.h"
  1110. #define lstrlen(s1) strlen(s1)
  1111. #define lstrcmp(s1, s2) strcmp(s1, s2)
  1112. #define lstrncmp(s1, s2, n) strncmp(s1, s2, n)
  1113. #define TRUE 1
  1114. #define FALSE 0
  1115. #endif // }
  1116. // }
  1117. //*** rule: smart rename
  1118. // DESCRIPTION
  1119. // we recognize renames that remove prefixes (e.g. "Shortcut to ..."),
  1120. // and apply automatically to future renames. adding back the prefix
  1121. // resets to the original state.
  1122. // NOTES
  1123. // assumes only care about prefix, which is bogus for non-US
  1124. struct renpre {
  1125. char *pszPrefix; // prefix we care about
  1126. int fChange; // recent change: 0=nil +1=add -1=del
  1127. };
  1128. struct renpre SmartPrefixTab[] = {
  1129. { "Shortcut to ", 0 },
  1130. 0
  1131. };
  1132. //*** IsSuffix -- return index of (possible) suffix in string
  1133. // ENTRY/EXIT
  1134. // i (return) index of suffix in string, o.w. -1
  1135. int IsSuffix(char *pszStr, char *pszSuf)
  1136. {
  1137. int cchStr, cchSuf;
  1138. int i;
  1139. cchStr = lstrlen(pszStr);
  1140. cchSuf = lstrlen(pszSuf);
  1141. if (cchSuf > cchStr)
  1142. return -1;
  1143. i = cchStr - cchSuf;
  1144. if (lstrcmp(pszStr + i, pszSuf) == 0)
  1145. return i;
  1146. return -1;
  1147. }
  1148. int UpdateSmartPrefix(char *pszOld, char *pszNew, int fChange);
  1149. //*** UEMOnRename -- track renames for interesting patterns
  1150. // ENTRY/EXIT
  1151. // (SE) updates smart prefix table if rename effects it
  1152. void UEMOnRename(char *pszOld, char *pszNew)
  1153. {
  1154. if (UpdateSmartPrefix(pszOld, pszNew, -1)) {
  1155. #ifdef XXX_DEBUG
  1156. // "Shortcut to foo" to "foo"
  1157. printf("or: -1\n");
  1158. #endif
  1159. }
  1160. else if (UpdateSmartPrefix(pszNew, pszOld, +1)) {
  1161. #ifdef XXX_DEBUG
  1162. // "foo" to "Shortcut to foo"
  1163. printf("or: +1\n");
  1164. #endif
  1165. }
  1166. return;
  1167. }
  1168. //*** UpdateSmartPrefix -- if op effects prefix, mark change
  1169. //
  1170. int UpdateSmartPrefix(char *pszOld, char *pszNew, int fChange)
  1171. {
  1172. int i;
  1173. struct renpre *prp;
  1174. // (note that if we rename to the same thing, i==0, so we don't
  1175. // do anything. this is as it should be).
  1176. if ((i = IsSuffix(pszOld, pszNew)) > 0) {
  1177. prp = &SmartPrefixTab[0]; // TODO: for each ...
  1178. // iSuf==cchPre, so pszOld[0..iSuf-1] is prefix
  1179. if (i == lstrlen(prp->pszPrefix) && lstrncmp(pszOld, prp->pszPrefix, i) == 0) {
  1180. #ifdef XXX_DEBUG
  1181. printf("usp: o=%s n=%s p=%s f=%d\n", pszOld, pszNew, SmartPrefixTab[0].pszPrefix, fChange);
  1182. #endif
  1183. prp->fChange = fChange;
  1184. return 1;
  1185. }
  1186. }
  1187. return 0;
  1188. }
  1189. //*** GetSmartRename --
  1190. // ENTRY/EXIT
  1191. // pszDef proposed default name (in 'original' form, e.g. 'Shortcut to')
  1192. // i (ret) index of 'smart' default name
  1193. int GetSmartRename(char *pszDef)
  1194. {
  1195. char *pszPre;
  1196. int cchPre;
  1197. // for each prefix in smartPrefixList ...
  1198. pszPre = SmartPrefixTab[0].pszPrefix;
  1199. cchPre = lstrlen(pszPre);
  1200. if (strncmp(pszDef, pszPre, cchPre) == 0) {
  1201. if (SmartPrefixTab[0].fChange == -1)
  1202. return cchPre;
  1203. }
  1204. return 0;
  1205. }
  1206. #endif
  1207. // }
  1208. #ifdef XXX_TEST // {
  1209. char c_szScToFoo[] = "Shortcut to foo";
  1210. char c_szScToFred[] = "Shortcut to fred";
  1211. char *TestTab[] = {
  1212. c_szScToFoo,
  1213. c_szScToFred,
  1214. "bar",
  1215. 0
  1216. };
  1217. pr(char *p)
  1218. {
  1219. int i;
  1220. i = GetSmartRename(p);
  1221. if (i >= 0)
  1222. printf("\t<%s>", p + i);
  1223. else
  1224. printf("\t<%s>", p);
  1225. return;
  1226. }
  1227. prtab()
  1228. {
  1229. pr("foo");
  1230. pr(c_szScToFoo);
  1231. pr(c_szScToFred);
  1232. printf("\n");
  1233. }
  1234. TstRename()
  1235. {
  1236. int i;
  1237. // original
  1238. prtab();
  1239. // delete
  1240. printf("del\n");
  1241. UEMOnRename(c_szScToFoo, "foo");
  1242. prtab();
  1243. // ... again
  1244. printf("del\n");
  1245. UEMOnRename(c_szScToFoo, "foo");
  1246. prtab();
  1247. // add (restore)
  1248. printf("add\n");
  1249. UEMOnRename("foo", c_szScToFoo);
  1250. prtab();
  1251. // ... again
  1252. printf("add\n");
  1253. UEMOnRename("foo", c_szScToFoo);
  1254. prtab();
  1255. // delete partial (shouldn't look like prefix)
  1256. printf("del partial\n");
  1257. UEMOnRename(c_szScToFoo, "to foo");
  1258. prtab();
  1259. // rename to same (shouldn't look like prefix)
  1260. printf("ren same\n");
  1261. UEMOnRename(c_szScToFoo, "c_szScToFoo");
  1262. prtab();
  1263. }
  1264. main()
  1265. {
  1266. TstRename();
  1267. }
  1268. #endif // }