1
0

skinnedmenuthreadinfo.cpp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. #include "main.h"
  2. #include "./skinnedMenuThreadInfo.h"
  3. #include "./skinnedMenu.h"
  4. #include "./skinnedMenuWnd.h"
  5. #ifndef _DEBUG
  6. #include <new>
  7. #endif
  8. static DWORD tlsIndex = TLS_OUT_OF_INDEXES;
  9. size_t ref;
  10. HHOOK attachHook;
  11. SkinnedMenu *attachMenu;
  12. HHOOK validationHook;
  13. SkinnedMenuWnd *validationWindow;
  14. khash_t(intptr_map) *windowMap;
  15. khash_t(int_set) *claimedIdSet;
  16. unsigned int lastAssignedId;
  17. HMENU activeMeasureMenu;
  18. SkinnedMenuThreadInfo::SkinnedMenuThreadInfo()
  19. : ref(1), attachHook(NULL), attachMenu(NULL), validationHook(NULL),
  20. validationWindow(NULL), lastAssignedId((unsigned int)-100),
  21. activeMeasureMenu(NULL)
  22. {
  23. windowMap = kh_init(intptr_map);
  24. claimedIdSet = kh_init(int_set);
  25. }
  26. SkinnedMenuThreadInfo::~SkinnedMenuThreadInfo()
  27. {
  28. if (TLS_OUT_OF_INDEXES != tlsIndex)
  29. TlsSetValue(tlsIndex, NULL);
  30. if (NULL != attachHook)
  31. {
  32. UnhookWindowsHookEx(attachHook);
  33. attachHook = NULL;
  34. }
  35. if (NULL != validationHook)
  36. {
  37. UnhookWindowsHookEx(validationHook);
  38. validationHook = NULL;
  39. }
  40. if (NULL != windowMap)
  41. kh_destroy(intptr_map, windowMap);
  42. if (NULL != claimedIdSet)
  43. kh_destroy(int_set, claimedIdSet);
  44. }
  45. HRESULT SkinnedMenuThreadInfo::GetInstance(BOOL allowCreate, SkinnedMenuThreadInfo **instance)
  46. {
  47. HRESULT hr;
  48. SkinnedMenuThreadInfo *self;
  49. if (NULL == instance)
  50. return E_POINTER;
  51. *instance = NULL;
  52. if (TLS_OUT_OF_INDEXES == tlsIndex)
  53. {
  54. tlsIndex = TlsAlloc();
  55. if (TLS_OUT_OF_INDEXES == tlsIndex)
  56. return E_OUTOFMEMORY;
  57. self = NULL;
  58. if (FALSE != TlsSetValue(tlsIndex, NULL))
  59. SetLastError(ERROR_SUCCESS);
  60. }
  61. else
  62. {
  63. self = (SkinnedMenuThreadInfo*)TlsGetValue(tlsIndex);
  64. }
  65. if (NULL == self)
  66. {
  67. unsigned long errorCode;
  68. errorCode = GetLastError();
  69. if (ERROR_SUCCESS != errorCode)
  70. {
  71. hr = HRESULT_FROM_WIN32(errorCode);
  72. }
  73. else
  74. {
  75. if (FALSE == allowCreate)
  76. {
  77. self = NULL;
  78. hr = S_FALSE;
  79. }
  80. else
  81. {
  82. #ifndef _DEBUG
  83. self = new (std::nothrow) SkinnedMenuThreadInfo();
  84. #else
  85. self = new SkinnedMenuThreadInfo();
  86. #endif
  87. if (NULL == self)
  88. hr = E_OUTOFMEMORY;
  89. else
  90. {
  91. if (FALSE == TlsSetValue(tlsIndex, self))
  92. {
  93. errorCode = GetLastError();
  94. hr = HRESULT_FROM_WIN32(errorCode);
  95. self->Release();
  96. self = NULL;
  97. }
  98. else
  99. {
  100. hr = S_OK;
  101. }
  102. }
  103. }
  104. }
  105. }
  106. else
  107. {
  108. self->AddRef();
  109. hr = S_OK;
  110. }
  111. *instance = self;
  112. return hr;
  113. }
  114. size_t SkinnedMenuThreadInfo::AddRef()
  115. {
  116. return InterlockedIncrement((LONG*)&ref);
  117. }
  118. size_t SkinnedMenuThreadInfo::Release()
  119. {
  120. if (0 == ref)
  121. return ref;
  122. LONG r = InterlockedDecrement((LONG*)&ref);
  123. if (0 == r)
  124. delete(this);
  125. return r;
  126. }
  127. BOOL SkinnedMenuThreadInfo::SetAttachHook(SkinnedMenu *menu)
  128. {
  129. if (NULL == menu)
  130. return FALSE;
  131. if (NULL != attachHook)
  132. return FALSE;
  133. attachMenu = menu;
  134. attachHook = SetWindowsHookEx(WH_CALLWNDPROC, SkinnedMenuThreadInfo_AttachHookCb, NULL, GetCurrentThreadId());
  135. if (NULL == attachHook)
  136. {
  137. attachMenu = FALSE;
  138. return FALSE;
  139. }
  140. return TRUE;
  141. }
  142. BOOL SkinnedMenuThreadInfo::RemoveAttachHook(SkinnedMenu *menu)
  143. {
  144. if (NULL == attachHook)
  145. return FALSE;
  146. if (menu != attachMenu)
  147. return FALSE;
  148. UnhookWindowsHookEx(attachHook);
  149. attachHook = NULL;
  150. attachMenu = NULL;
  151. return TRUE;
  152. }
  153. BOOL SkinnedMenuThreadInfo::IsAttachHookActive()
  154. {
  155. return (NULL != attachHook);
  156. }
  157. LRESULT SkinnedMenuThreadInfo::AttachHook(int nCode, WPARAM wParam, LPARAM lParam)
  158. {
  159. if (HC_ACTION == nCode)
  160. {
  161. CWPSTRUCT *pcwp = (CWPSTRUCT*)lParam;
  162. if (WM_NCCREATE == pcwp->message)
  163. {
  164. wchar_t szName[128] = {0};
  165. if (GetClassNameW(pcwp->hwnd, szName, ARRAYSIZE(szName)) &&
  166. CSTR_EQUAL == CompareStringW(CSTR_INVARIANT, NORM_IGNORECASE, szName, -1, L"#32768", -1))
  167. {
  168. LRESULT result;
  169. HHOOK hookCopy;
  170. SkinnedMenu *menuCopy;
  171. menuCopy = attachMenu;
  172. hookCopy = attachHook;
  173. attachHook = NULL;
  174. attachMenu = NULL;
  175. result = CallNextHookEx(hookCopy, nCode, wParam, lParam);
  176. UnhookWindowsHookEx(hookCopy);
  177. if (NULL != menuCopy)
  178. menuCopy->AttachToHwnd(pcwp->hwnd);
  179. return result;
  180. }
  181. }
  182. }
  183. return CallNextHookEx(attachHook, nCode, wParam, lParam);
  184. }
  185. BOOL SkinnedMenuThreadInfo::SetValidationHook(SkinnedMenuWnd *window)
  186. {
  187. HMENU prevMenu;
  188. if (NULL == window)
  189. return FALSE;
  190. if (NULL != validationHook)
  191. return FALSE;
  192. validationWindow = window;
  193. prevMenu = SetActiveMeasureMenu(window->GetMenuHandle());
  194. validationHook = SetWindowsHookEx(WH_CALLWNDPROC, SkinnedMenuThreadInfo_ValidationHookCb, NULL, GetCurrentThreadId());
  195. if (NULL == validationHook)
  196. {
  197. validationWindow = FALSE;
  198. SetActiveMeasureMenu(prevMenu);
  199. return FALSE;
  200. }
  201. return TRUE;
  202. }
  203. BOOL SkinnedMenuThreadInfo::RemoveValidationHook(SkinnedMenuWnd *window)
  204. {
  205. if (NULL == validationHook)
  206. return FALSE;
  207. if (window != validationWindow)
  208. return FALSE;
  209. UnhookWindowsHookEx(validationHook);
  210. validationWindow = NULL;
  211. validationHook = NULL;
  212. return TRUE;
  213. }
  214. BOOL SkinnedMenuThreadInfo::IsValidationHookActive()
  215. {
  216. return (NULL != validationHook);
  217. }
  218. LRESULT SkinnedMenuThreadInfo::ValidationHook(int nCode, WPARAM wParam, LPARAM lParam)
  219. {
  220. if (HC_ACTION == nCode)
  221. {
  222. CWPSTRUCT *pcwp = (CWPSTRUCT*)lParam;
  223. if (WM_MEASUREITEM == pcwp->message ||
  224. WM_DRAWITEM == pcwp->message)
  225. {
  226. if (NULL != validationWindow && NULL != pcwp->lParam)
  227. {
  228. BOOL validationCompleted(FALSE);
  229. if (WM_MEASUREITEM == pcwp->message)
  230. {
  231. MEASUREITEMSTRUCT *measureItem;
  232. measureItem = (MEASUREITEMSTRUCT*)pcwp->lParam;
  233. if (ODT_MENU == measureItem->CtlType &&
  234. validationWindow->GetMenuHandle() == GetActiveMeasureMenu())
  235. {
  236. validationCompleted = TRUE;
  237. }
  238. }
  239. else
  240. {
  241. DRAWITEMSTRUCT *drawItem;
  242. drawItem = (DRAWITEMSTRUCT*)pcwp->lParam;
  243. if (ODT_MENU == drawItem->CtlType &&
  244. validationWindow->GetMenuHandle() == (HMENU)drawItem->hwndItem)
  245. {
  246. validationCompleted = TRUE;
  247. }
  248. }
  249. if (FALSE != validationCompleted)
  250. {
  251. LRESULT result;
  252. HHOOK hookCopy;
  253. SkinnedMenuWnd *windowCopy;
  254. windowCopy = validationWindow;
  255. hookCopy = validationHook;
  256. validationHook = NULL;
  257. validationWindow = NULL;
  258. if (NULL != windowCopy && windowCopy->GetOwnerWindow() != pcwp->hwnd)
  259. {
  260. windowCopy->SetOwnerWindow(pcwp->hwnd);
  261. }
  262. result = CallNextHookEx(hookCopy, nCode, wParam, lParam);
  263. UnhookWindowsHookEx(hookCopy);
  264. return result;
  265. }
  266. }
  267. }
  268. }
  269. return CallNextHookEx(attachHook, nCode, wParam, lParam);
  270. }
  271. BOOL SkinnedMenuThreadInfo::RegisterMenu(HMENU menu, HWND window)
  272. {
  273. int code;
  274. khint_t key;
  275. if (NULL == menu)
  276. return FALSE;
  277. if (NULL == windowMap)
  278. return FALSE;
  279. key = kh_put(intptr_map, windowMap, (intptr_t)menu, &code);
  280. kh_val(windowMap, key) = window;
  281. return TRUE;
  282. }
  283. BOOL SkinnedMenuThreadInfo::UnregisterMenu(HMENU menu)
  284. {
  285. khint_t key;
  286. if (NULL == menu)
  287. return FALSE;
  288. if (NULL == windowMap)
  289. return FALSE;
  290. key = kh_get(intptr_map, windowMap, (intptr_t)menu);
  291. if (kh_end(windowMap) == key)
  292. return FALSE;
  293. kh_del(intptr_map, windowMap, key);
  294. return TRUE;
  295. }
  296. HWND SkinnedMenuThreadInfo::FindMenuWindow(HMENU menu)
  297. {
  298. khint_t key;
  299. if (NULL == menu)
  300. return NULL;
  301. if (NULL == windowMap)
  302. return NULL;
  303. key = kh_get(intptr_map, windowMap, (intptr_t)menu);
  304. if (kh_end(windowMap) == key)
  305. return NULL;
  306. return kh_val(windowMap, key);
  307. }
  308. void SkinnedMenuThreadInfo::ClaimId(unsigned int id)
  309. {
  310. int code;
  311. kh_put(int_set, claimedIdSet, id, &code);
  312. }
  313. void SkinnedMenuThreadInfo::ReleaseId(unsigned int id)
  314. {
  315. khint_t key;
  316. key = kh_get(int_set, claimedIdSet, id);
  317. if (kh_end(claimedIdSet) != key)
  318. kh_del(int_set, claimedIdSet, key);
  319. }
  320. unsigned int SkinnedMenuThreadInfo::GetAvailableId()
  321. {
  322. khint_t key;
  323. unsigned int originalId;
  324. lastAssignedId--;
  325. if ((unsigned int)-1 == lastAssignedId)
  326. lastAssignedId--;
  327. originalId = lastAssignedId;
  328. for(;;)
  329. {
  330. key = kh_get(int_set, claimedIdSet, lastAssignedId);
  331. if (kh_end(claimedIdSet) == key)
  332. return lastAssignedId;
  333. lastAssignedId--;
  334. if ((unsigned int)-1 == lastAssignedId)
  335. lastAssignedId--;
  336. if (lastAssignedId == originalId)
  337. break;
  338. }
  339. return (unsigned int)-1;
  340. }
  341. HMENU SkinnedMenuThreadInfo::SetActiveMeasureMenu(HMENU menu)
  342. {
  343. HMENU prevMenu;
  344. prevMenu = activeMeasureMenu;
  345. activeMeasureMenu = menu;
  346. return prevMenu;
  347. }
  348. HMENU SkinnedMenuThreadInfo::GetActiveMeasureMenu()
  349. {
  350. return activeMeasureMenu;
  351. }
  352. static LRESULT CALLBACK SkinnedMenuThreadInfo_AttachHookCb(int nCode, WPARAM wParam, LPARAM lParam)
  353. {
  354. LRESULT result;
  355. SkinnedMenuThreadInfo *self;
  356. if (S_OK != SkinnedMenuThreadInfo::GetInstance(FALSE, &self))
  357. return 0;
  358. result = self->AttachHook(nCode, wParam, lParam);
  359. self->Release();
  360. return result;
  361. }
  362. static LRESULT CALLBACK SkinnedMenuThreadInfo_ValidationHookCb(int nCode, WPARAM wParam, LPARAM lParam)
  363. {
  364. LRESULT result;
  365. SkinnedMenuThreadInfo *self;
  366. if (S_OK != SkinnedMenuThreadInfo::GetInstance(FALSE, &self))
  367. return 0;
  368. result = self->ValidationHook(nCode, wParam, lParam);
  369. self->Release();
  370. return result;
  371. }