browserThread.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. #include "main.h"
  2. #include "./browserThread.h"
  3. #include "../nu/threadname.h"
  4. #include <exdisp.h>
  5. typedef struct __BROWSERTHREADCRAEATEPARAM
  6. {
  7. BTCREATEWNDPROC fnCreateWnd;
  8. BTKEYFILTERPROC fnKeyFilter;
  9. ULONG_PTR user;
  10. HANDLE readyEvent;
  11. HWND hHost;
  12. HWND hWinamp;
  13. } BROWSERTHREADCREATEPARAM;
  14. typedef struct __BROWSERTHREAD
  15. {
  16. HHOOK messageHook;
  17. HANDLE wakeupEvent;
  18. UINT flags;
  19. } BROWSERTHREAD;
  20. #define NAVIGATE_WAITTIMEOUT 30
  21. static size_t tlsIndex = TLS_OUT_OF_INDEXES;
  22. static UINT BHTM_DESTROY = 0xFEFE;
  23. static DWORD CALLBACK BrowserThread_MainLoop(LPVOID param);
  24. #define GetThreadInstance() ((TLS_OUT_OF_INDEXES != tlsIndex) ? (BROWSERTHREAD*)Plugin_TlsGetValue(tlsIndex) : NULL)
  25. BOOL BrowserThread_IsQuiting()
  26. {
  27. BROWSERTHREAD *thread = GetThreadInstance();
  28. return (NULL == thread || 0 != ((BHTF_BEGINDESTROY | BHTF_QUITLOOP) & thread->flags));
  29. }
  30. BOOL BrowserThread_SetFlags(UINT flags, UINT flagsMask, BOOL fAlarm)
  31. {
  32. BROWSERTHREAD *thread = GetThreadInstance();
  33. if (NULL == thread) return FALSE;
  34. thread->flags = ((thread->flags & flagsMask) | flags);
  35. if (FALSE == fAlarm)
  36. return TRUE;
  37. return (NULL != thread->wakeupEvent && SetEvent(thread->wakeupEvent));
  38. }
  39. HANDLE BrowserThread_Create(HWND hWinamp, BTCREATEWNDPROC fnCreateWnd, ULONG_PTR user, BTKEYFILTERPROC fnKeyFilter, HWND *pWnd, DWORD *pThreadId)
  40. {
  41. if (NULL == fnCreateWnd)
  42. return NULL;
  43. if (TLS_OUT_OF_INDEXES == tlsIndex)
  44. {
  45. tlsIndex = Plugin_TlsAlloc();
  46. if (TLS_OUT_OF_INDEXES == tlsIndex)
  47. return NULL;
  48. }
  49. DWORD threadId;
  50. BROWSERTHREADCREATEPARAM param;
  51. ZeroMemory(&param, sizeof(BROWSERTHREADCREATEPARAM));
  52. param.fnCreateWnd = fnCreateWnd;
  53. param.fnKeyFilter = fnKeyFilter;
  54. param.user = user;
  55. param.readyEvent = CreateEvent(0, TRUE, FALSE, 0);
  56. param.hWinamp = hWinamp;
  57. HANDLE hThread = CreateThread(NULL, 0, BrowserThread_MainLoop, (LPVOID)&param, 0, &threadId);
  58. if (NULL != hThread)
  59. {
  60. if (NULL != param.readyEvent)
  61. WaitForSingleObject(param.readyEvent, INFINITE);
  62. }
  63. else
  64. {
  65. if (NULL != param.hHost)
  66. {
  67. DestroyWindow(param.hHost);
  68. param.hHost = NULL;
  69. }
  70. threadId = 0;
  71. }
  72. if (NULL != param.readyEvent)
  73. CloseHandle(param.readyEvent);
  74. if (NULL != pThreadId)
  75. *pThreadId = threadId;
  76. if (NULL != pWnd)
  77. *pWnd = param.hHost;
  78. return hThread;
  79. }
  80. BOOL BrowserThread_PostDestroyEx(DWORD threadId, HWND hHost)
  81. {
  82. if (0 == BHTM_DESTROY)
  83. BHTM_DESTROY = RegisterWindowMessage(L"omBrowserDestroyMsg");
  84. if (0 == BHTM_DESTROY ||
  85. FALSE == PostThreadMessage(threadId, BHTM_DESTROY, 0, (LPARAM)hHost))
  86. {
  87. return FALSE;
  88. }
  89. BrowserThread_SetFlags(BHTF_BEGINDESTROY, BHTF_BEGINDESTROY, FALSE);
  90. return TRUE;
  91. }
  92. BOOL BrowserThread_PostDestroy(HWND hHost)
  93. {
  94. return BrowserThread_PostDestroyEx(GetCurrentThreadId(), hHost);
  95. }
  96. BOOL BrowserThread_WaitNavigateComplete(IWebBrowser2 *pWeb2, UINT waitMax)
  97. {
  98. MSG msg;
  99. READYSTATE state;
  100. if (NULL == pWeb2)
  101. return FALSE;
  102. BOOL resultOk = FALSE;
  103. DWORD tickStart = GetTickCount();
  104. for(;;)
  105. {
  106. if (FAILED(pWeb2->get_ReadyState(&state)))
  107. break;
  108. if (READYSTATE_INTERACTIVE <= state)
  109. {
  110. resultOk = TRUE;
  111. break;
  112. }
  113. else
  114. {
  115. DWORD tickNow = GetTickCount();
  116. if (tickNow < tickStart || (tickNow - tickStart) >= waitMax)
  117. {
  118. break; // time out
  119. }
  120. }
  121. DWORD status = MsgWaitForMultipleObjectsEx(0, NULL, NAVIGATE_WAITTIMEOUT, QS_POSTMESSAGE | QS_TIMER | QS_SENDMESSAGE, MWMO_ALERTABLE);
  122. switch(status)
  123. {
  124. case (WAIT_OBJECT_0 + 0):
  125. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  126. {
  127. if (!CallMsgFilter(&msg, MSGF_BROWSERLOOP))
  128. {
  129. DispatchMessageW(&msg);
  130. }
  131. }
  132. break;
  133. }
  134. }
  135. return resultOk;
  136. }
  137. static BOOL BrowserThread_HandleMessage(MSG *pMsg)
  138. {
  139. switch(pMsg->message)
  140. {
  141. case WM_QUIT:
  142. BrowserThread_SetFlags(BHTF_QUITLOOP, BHTF_QUITLOOP, TRUE);
  143. return TRUE;
  144. }
  145. if (0 != BHTM_DESTROY && BHTM_DESTROY == pMsg->message)
  146. {
  147. HWND hHost = (HWND)pMsg->lParam;
  148. if (NULL != hHost)
  149. {
  150. BrowserThread_SetFlags(BHTF_BEGINDESTROY, BHTF_BEGINDESTROY, FALSE);
  151. SendMessage(hHost, BTM_RELEASECONTAINER, 0, 0L);
  152. DestroyWindow(hHost);
  153. }
  154. return TRUE;
  155. }
  156. return FALSE;
  157. }
  158. static LRESULT CALLBACK BrowserThread_MessageFilterProc(INT code, WPARAM wParam, LPARAM lParam)
  159. {
  160. BROWSERTHREAD *thread = GetThreadInstance();
  161. if (code >= 0)
  162. {
  163. if (BrowserThread_HandleMessage((MSG*)lParam))
  164. {
  165. return TRUE;
  166. }
  167. }
  168. return (NULL != thread && NULL != thread->messageHook) ?
  169. CallNextHookEx(thread->messageHook, code, wParam, lParam) :
  170. FALSE;
  171. }
  172. static BOOL CALLBACK BrowserThread_DefaultKeyFilter(HWND hwnd, MSG *pMsg)
  173. {
  174. return FALSE;
  175. }
  176. inline static BOOL BrowserThread_ProcessMessage(HWND hHost, HWND hWinamp, MSG *pMsg, BTKEYFILTERPROC IsHostMessage)
  177. {
  178. if (hHost != pMsg->hwnd && FALSE == IsChild(hHost, pMsg->hwnd))
  179. return FALSE;
  180. if (pMsg->message >= WM_KEYFIRST && pMsg->message <= WM_KEYLAST)
  181. {
  182. if (FALSE != IsHostMessage(hHost, pMsg))
  183. return TRUE;
  184. switch(pMsg->wParam)
  185. {
  186. case VK_TAB:
  187. {
  188. HWND hOwner = (HWND)(LONG_PTR)GetWindowLongPtr(hHost, GWLP_HWNDPARENT);
  189. if (NULL == hOwner || hWinamp == hOwner)
  190. hOwner = hHost;
  191. return IsDialogMessageW(hOwner, pMsg);
  192. }
  193. break;
  194. }
  195. }
  196. if (pMsg->message == WM_MOUSEWHEEL)
  197. {
  198. POINT cursor;
  199. HWND targetWindow;
  200. POINTSTOPOINT(cursor, pMsg->lParam);
  201. targetWindow = WindowFromPoint(cursor);
  202. if (NULL != targetWindow &&
  203. FALSE == IsChild(hHost, targetWindow ) &&
  204. GetWindowThreadProcessId(targetWindow, NULL) != GetWindowThreadProcessId(hHost, NULL))
  205. {
  206. PostMessage(hWinamp, pMsg->message, pMsg->wParam, pMsg->lParam);
  207. return TRUE;
  208. }
  209. }
  210. return FALSE;
  211. }
  212. static void BrowserThread_FinishThread(BROWSERTHREAD *thread)
  213. {
  214. if (NULL != thread)
  215. {
  216. if (NULL != thread->messageHook)
  217. {
  218. UnhookWindowsHookEx(thread->messageHook);
  219. thread->messageHook = NULL;
  220. }
  221. if (NULL != thread->wakeupEvent)
  222. {
  223. CloseHandle(thread->wakeupEvent);
  224. thread->wakeupEvent = NULL;
  225. }
  226. }
  227. if (TLS_OUT_OF_INDEXES != tlsIndex)
  228. Plugin_TlsSetValue(tlsIndex, NULL);
  229. OleUninitialize();
  230. #ifdef _DEBUG
  231. aTRACE_FMT("[%d] %S: thread exit\r\n", GetCurrentThreadId(), OMBROWSER_NAME);
  232. #endif // _DEBUG
  233. }
  234. static DWORD CALLBACK BrowserThread_MainLoop(LPVOID param)
  235. {
  236. #ifdef _DEBUG
  237. SetThreadName(GetCurrentThreadId(), "omBrowserThread");
  238. aTRACE_FMT("[%d] %S: thread created\r\n", GetCurrentThreadId(), OMBROWSER_NAME);
  239. #endif //_DEBUG
  240. BROWSERTHREADCREATEPARAM *createParam = (BROWSERTHREADCREATEPARAM*)param;
  241. HWND hWinamp = createParam->hWinamp;
  242. BROWSERTHREAD thread;
  243. ZeroMemory(&thread, sizeof(BROWSERTHREAD));
  244. if (TLS_OUT_OF_INDEXES != tlsIndex)
  245. Plugin_TlsSetValue(tlsIndex, &thread);
  246. MSG msg;
  247. PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
  248. BTKEYFILTERPROC IsHostMessage = (NULL != createParam->fnKeyFilter) ? createParam->fnKeyFilter : BrowserThread_DefaultKeyFilter;
  249. thread.messageHook = SetWindowsHookEx(WH_MSGFILTER, BrowserThread_MessageFilterProc, NULL, GetCurrentThreadId());
  250. thread.wakeupEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  251. HWND hHost = createParam->fnCreateWnd(createParam->user);
  252. createParam->hHost = hHost;
  253. #ifdef _DEBUG
  254. if (NULL != hHost)
  255. aTRACE_FMT("[%d] %S: host created\r\n", GetCurrentThreadId(), OMBROWSER_NAME);
  256. else
  257. aTRACE_FMT("[%d] %S: host creation fialed\r\n", GetCurrentThreadId(), OMBROWSER_NAME);
  258. #endif //_DEBUG
  259. if (NULL != createParam->readyEvent)
  260. SetEvent(createParam->readyEvent);
  261. if (NULL != hHost && FAILED(OleInitialize(0)))
  262. {
  263. DestroyWindow(hHost);
  264. hHost = NULL;
  265. }
  266. if (NULL == hHost)
  267. {
  268. BrowserThread_FinishThread(&thread);
  269. return -1;
  270. }
  271. SendMessage(hHost, BTM_INITCONTAINER, (WPARAM)hWinamp, 0L);
  272. SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
  273. while (0 == (BHTF_QUITLOOP & thread.flags))
  274. {
  275. DWORD status = MsgWaitForMultipleObjectsEx(1, &thread.wakeupEvent, INFINITE,
  276. QS_ALLPOSTMESSAGE | QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
  277. switch(status)
  278. {
  279. case (WAIT_OBJECT_0 + 0):
  280. // wake up!!!
  281. break;
  282. case (WAIT_OBJECT_0 + 1):
  283. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  284. {
  285. if (!CallMsgFilter(&msg, MSGF_BROWSERLOOP) && NULL != msg.hwnd)
  286. {
  287. if (0 == (BHTF_BEGINDESTROY & thread.flags))
  288. {
  289. if (FALSE == BrowserThread_ProcessMessage(hHost, hWinamp, &msg, IsHostMessage))
  290. {
  291. TranslateMessage(&msg);
  292. DispatchMessageW(&msg);
  293. }
  294. }
  295. else
  296. {
  297. DispatchMessageW(&msg);
  298. }
  299. }
  300. }
  301. break;
  302. }
  303. }
  304. BrowserThread_FinishThread(&thread);
  305. return 0;
  306. }
  307. INT BrowserThread_ModalLoop(HWND hwnd, HANDLE hCancel, DWORD timeout)
  308. {
  309. MSG msg;
  310. for (;;)
  311. {
  312. DWORD status = MsgWaitForMultipleObjectsEx(1, &hCancel, timeout, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
  313. if (WAIT_OBJECT_0 == status)
  314. {
  315. return 0;
  316. }
  317. else if ((WAIT_OBJECT_0 + 1) == status)
  318. {
  319. while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
  320. {
  321. if (msg.message == WM_QUIT)
  322. {
  323. PostQuitMessage((INT)msg.wParam);
  324. return (INT)msg.wParam;
  325. }
  326. if (!IsDialogMessageW(hwnd, &msg))
  327. {
  328. TranslateMessage(&msg);
  329. DispatchMessageW(&msg);
  330. }
  331. }
  332. }
  333. }
  334. return 0;
  335. }