413 lines
9.0 KiB
C++
413 lines
9.0 KiB
C++
|
#include "main.h"
|
||
|
#include "./browserThread.h"
|
||
|
#include "../nu/threadname.h"
|
||
|
|
||
|
#include <exdisp.h>
|
||
|
|
||
|
typedef struct __BROWSERTHREADCRAEATEPARAM
|
||
|
{
|
||
|
BTCREATEWNDPROC fnCreateWnd;
|
||
|
BTKEYFILTERPROC fnKeyFilter;
|
||
|
ULONG_PTR user;
|
||
|
HANDLE readyEvent;
|
||
|
HWND hHost;
|
||
|
HWND hWinamp;
|
||
|
} BROWSERTHREADCREATEPARAM;
|
||
|
|
||
|
typedef struct __BROWSERTHREAD
|
||
|
{
|
||
|
HHOOK messageHook;
|
||
|
HANDLE wakeupEvent;
|
||
|
UINT flags;
|
||
|
} BROWSERTHREAD;
|
||
|
|
||
|
#define NAVIGATE_WAITTIMEOUT 30
|
||
|
|
||
|
static size_t tlsIndex = TLS_OUT_OF_INDEXES;
|
||
|
static UINT BHTM_DESTROY = 0xFEFE;
|
||
|
|
||
|
static DWORD CALLBACK BrowserThread_MainLoop(LPVOID param);
|
||
|
|
||
|
#define GetThreadInstance() ((TLS_OUT_OF_INDEXES != tlsIndex) ? (BROWSERTHREAD*)Plugin_TlsGetValue(tlsIndex) : NULL)
|
||
|
|
||
|
BOOL BrowserThread_IsQuiting()
|
||
|
{
|
||
|
BROWSERTHREAD *thread = GetThreadInstance();
|
||
|
return (NULL == thread || 0 != ((BHTF_BEGINDESTROY | BHTF_QUITLOOP) & thread->flags));
|
||
|
}
|
||
|
|
||
|
BOOL BrowserThread_SetFlags(UINT flags, UINT flagsMask, BOOL fAlarm)
|
||
|
{
|
||
|
BROWSERTHREAD *thread = GetThreadInstance();
|
||
|
if (NULL == thread) return FALSE;
|
||
|
|
||
|
thread->flags = ((thread->flags & flagsMask) | flags);
|
||
|
if (FALSE == fAlarm)
|
||
|
return TRUE;
|
||
|
|
||
|
return (NULL != thread->wakeupEvent && SetEvent(thread->wakeupEvent));
|
||
|
}
|
||
|
|
||
|
|
||
|
HANDLE BrowserThread_Create(HWND hWinamp, BTCREATEWNDPROC fnCreateWnd, ULONG_PTR user, BTKEYFILTERPROC fnKeyFilter, HWND *pWnd, DWORD *pThreadId)
|
||
|
{
|
||
|
if (NULL == fnCreateWnd)
|
||
|
return NULL;
|
||
|
|
||
|
if (TLS_OUT_OF_INDEXES == tlsIndex)
|
||
|
{
|
||
|
tlsIndex = Plugin_TlsAlloc();
|
||
|
if (TLS_OUT_OF_INDEXES == tlsIndex)
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
DWORD threadId;
|
||
|
|
||
|
BROWSERTHREADCREATEPARAM param;
|
||
|
ZeroMemory(¶m, sizeof(BROWSERTHREADCREATEPARAM));
|
||
|
|
||
|
param.fnCreateWnd = fnCreateWnd;
|
||
|
param.fnKeyFilter = fnKeyFilter;
|
||
|
param.user = user;
|
||
|
param.readyEvent = CreateEvent(0, TRUE, FALSE, 0);
|
||
|
param.hWinamp = hWinamp;
|
||
|
|
||
|
HANDLE hThread = CreateThread(NULL, 0, BrowserThread_MainLoop, (LPVOID)¶m, 0, &threadId);
|
||
|
|
||
|
if (NULL != hThread)
|
||
|
{
|
||
|
if (NULL != param.readyEvent)
|
||
|
WaitForSingleObject(param.readyEvent, INFINITE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (NULL != param.hHost)
|
||
|
{
|
||
|
DestroyWindow(param.hHost);
|
||
|
param.hHost = NULL;
|
||
|
}
|
||
|
threadId = 0;
|
||
|
}
|
||
|
|
||
|
if (NULL != param.readyEvent)
|
||
|
CloseHandle(param.readyEvent);
|
||
|
|
||
|
|
||
|
if (NULL != pThreadId)
|
||
|
*pThreadId = threadId;
|
||
|
|
||
|
if (NULL != pWnd)
|
||
|
*pWnd = param.hHost;
|
||
|
|
||
|
return hThread;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
BOOL BrowserThread_PostDestroyEx(DWORD threadId, HWND hHost)
|
||
|
{
|
||
|
if (0 == BHTM_DESTROY)
|
||
|
BHTM_DESTROY = RegisterWindowMessage(L"omBrowserDestroyMsg");
|
||
|
|
||
|
if (0 == BHTM_DESTROY ||
|
||
|
FALSE == PostThreadMessage(threadId, BHTM_DESTROY, 0, (LPARAM)hHost))
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
BrowserThread_SetFlags(BHTF_BEGINDESTROY, BHTF_BEGINDESTROY, FALSE);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
BOOL BrowserThread_PostDestroy(HWND hHost)
|
||
|
{
|
||
|
return BrowserThread_PostDestroyEx(GetCurrentThreadId(), hHost);
|
||
|
}
|
||
|
|
||
|
BOOL BrowserThread_WaitNavigateComplete(IWebBrowser2 *pWeb2, UINT waitMax)
|
||
|
{
|
||
|
MSG msg;
|
||
|
READYSTATE state;
|
||
|
if (NULL == pWeb2)
|
||
|
return FALSE;
|
||
|
|
||
|
BOOL resultOk = FALSE;
|
||
|
DWORD tickStart = GetTickCount();
|
||
|
|
||
|
for(;;)
|
||
|
{
|
||
|
if (FAILED(pWeb2->get_ReadyState(&state)))
|
||
|
break;
|
||
|
|
||
|
if (READYSTATE_INTERACTIVE <= state)
|
||
|
{
|
||
|
resultOk = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DWORD tickNow = GetTickCount();
|
||
|
if (tickNow < tickStart || (tickNow - tickStart) >= waitMax)
|
||
|
{
|
||
|
break; // time out
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DWORD status = MsgWaitForMultipleObjectsEx(0, NULL, NAVIGATE_WAITTIMEOUT, QS_POSTMESSAGE | QS_TIMER | QS_SENDMESSAGE, MWMO_ALERTABLE);
|
||
|
switch(status)
|
||
|
{
|
||
|
case (WAIT_OBJECT_0 + 0):
|
||
|
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
||
|
{
|
||
|
if (!CallMsgFilter(&msg, MSGF_BROWSERLOOP))
|
||
|
{
|
||
|
DispatchMessageW(&msg);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return resultOk;
|
||
|
}
|
||
|
|
||
|
static BOOL BrowserThread_HandleMessage(MSG *pMsg)
|
||
|
{
|
||
|
switch(pMsg->message)
|
||
|
{
|
||
|
case WM_QUIT:
|
||
|
BrowserThread_SetFlags(BHTF_QUITLOOP, BHTF_QUITLOOP, TRUE);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
if (0 != BHTM_DESTROY && BHTM_DESTROY == pMsg->message)
|
||
|
{
|
||
|
HWND hHost = (HWND)pMsg->lParam;
|
||
|
if (NULL != hHost)
|
||
|
{
|
||
|
BrowserThread_SetFlags(BHTF_BEGINDESTROY, BHTF_BEGINDESTROY, FALSE);
|
||
|
SendMessage(hHost, BTM_RELEASECONTAINER, 0, 0L);
|
||
|
DestroyWindow(hHost);
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
static LRESULT CALLBACK BrowserThread_MessageFilterProc(INT code, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
BROWSERTHREAD *thread = GetThreadInstance();
|
||
|
if (code >= 0)
|
||
|
{
|
||
|
if (BrowserThread_HandleMessage((MSG*)lParam))
|
||
|
{
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return (NULL != thread && NULL != thread->messageHook) ?
|
||
|
CallNextHookEx(thread->messageHook, code, wParam, lParam) :
|
||
|
FALSE;
|
||
|
}
|
||
|
|
||
|
static BOOL CALLBACK BrowserThread_DefaultKeyFilter(HWND hwnd, MSG *pMsg)
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
inline static BOOL BrowserThread_ProcessMessage(HWND hHost, HWND hWinamp, MSG *pMsg, BTKEYFILTERPROC IsHostMessage)
|
||
|
{
|
||
|
if (hHost != pMsg->hwnd && FALSE == IsChild(hHost, pMsg->hwnd))
|
||
|
return FALSE;
|
||
|
|
||
|
if (pMsg->message >= WM_KEYFIRST && pMsg->message <= WM_KEYLAST)
|
||
|
{
|
||
|
if (FALSE != IsHostMessage(hHost, pMsg))
|
||
|
return TRUE;
|
||
|
|
||
|
switch(pMsg->wParam)
|
||
|
{
|
||
|
case VK_TAB:
|
||
|
{
|
||
|
HWND hOwner = (HWND)(LONG_PTR)GetWindowLongPtr(hHost, GWLP_HWNDPARENT);
|
||
|
if (NULL == hOwner || hWinamp == hOwner)
|
||
|
hOwner = hHost;
|
||
|
return IsDialogMessageW(hOwner, pMsg);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pMsg->message == WM_MOUSEWHEEL)
|
||
|
{
|
||
|
POINT cursor;
|
||
|
HWND targetWindow;
|
||
|
|
||
|
POINTSTOPOINT(cursor, pMsg->lParam);
|
||
|
targetWindow = WindowFromPoint(cursor);
|
||
|
|
||
|
if (NULL != targetWindow &&
|
||
|
FALSE == IsChild(hHost, targetWindow ) &&
|
||
|
GetWindowThreadProcessId(targetWindow, NULL) != GetWindowThreadProcessId(hHost, NULL))
|
||
|
{
|
||
|
PostMessage(hWinamp, pMsg->message, pMsg->wParam, pMsg->lParam);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
static void BrowserThread_FinishThread(BROWSERTHREAD *thread)
|
||
|
{
|
||
|
if (NULL != thread)
|
||
|
{
|
||
|
if (NULL != thread->messageHook)
|
||
|
{
|
||
|
UnhookWindowsHookEx(thread->messageHook);
|
||
|
thread->messageHook = NULL;
|
||
|
}
|
||
|
|
||
|
if (NULL != thread->wakeupEvent)
|
||
|
{
|
||
|
CloseHandle(thread->wakeupEvent);
|
||
|
thread->wakeupEvent = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (TLS_OUT_OF_INDEXES != tlsIndex)
|
||
|
Plugin_TlsSetValue(tlsIndex, NULL);
|
||
|
|
||
|
OleUninitialize();
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
aTRACE_FMT("[%d] %S: thread exit\r\n", GetCurrentThreadId(), OMBROWSER_NAME);
|
||
|
#endif // _DEBUG
|
||
|
}
|
||
|
|
||
|
static DWORD CALLBACK BrowserThread_MainLoop(LPVOID param)
|
||
|
{
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
SetThreadName(GetCurrentThreadId(), "omBrowserThread");
|
||
|
aTRACE_FMT("[%d] %S: thread created\r\n", GetCurrentThreadId(), OMBROWSER_NAME);
|
||
|
#endif //_DEBUG
|
||
|
|
||
|
BROWSERTHREADCREATEPARAM *createParam = (BROWSERTHREADCREATEPARAM*)param;
|
||
|
|
||
|
HWND hWinamp = createParam->hWinamp;
|
||
|
|
||
|
BROWSERTHREAD thread;
|
||
|
ZeroMemory(&thread, sizeof(BROWSERTHREAD));
|
||
|
|
||
|
if (TLS_OUT_OF_INDEXES != tlsIndex)
|
||
|
Plugin_TlsSetValue(tlsIndex, &thread);
|
||
|
|
||
|
MSG msg;
|
||
|
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
|
||
|
|
||
|
BTKEYFILTERPROC IsHostMessage = (NULL != createParam->fnKeyFilter) ? createParam->fnKeyFilter : BrowserThread_DefaultKeyFilter;
|
||
|
|
||
|
thread.messageHook = SetWindowsHookEx(WH_MSGFILTER, BrowserThread_MessageFilterProc, NULL, GetCurrentThreadId());
|
||
|
thread.wakeupEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||
|
HWND hHost = createParam->fnCreateWnd(createParam->user);
|
||
|
|
||
|
createParam->hHost = hHost;
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
if (NULL != hHost)
|
||
|
aTRACE_FMT("[%d] %S: host created\r\n", GetCurrentThreadId(), OMBROWSER_NAME);
|
||
|
else
|
||
|
aTRACE_FMT("[%d] %S: host creation fialed\r\n", GetCurrentThreadId(), OMBROWSER_NAME);
|
||
|
#endif //_DEBUG
|
||
|
|
||
|
if (NULL != createParam->readyEvent)
|
||
|
SetEvent(createParam->readyEvent);
|
||
|
|
||
|
|
||
|
if (NULL != hHost && FAILED(OleInitialize(0)))
|
||
|
{
|
||
|
DestroyWindow(hHost);
|
||
|
hHost = NULL;
|
||
|
}
|
||
|
|
||
|
if (NULL == hHost)
|
||
|
{
|
||
|
BrowserThread_FinishThread(&thread);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
SendMessage(hHost, BTM_INITCONTAINER, (WPARAM)hWinamp, 0L);
|
||
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
|
||
|
|
||
|
while (0 == (BHTF_QUITLOOP & thread.flags))
|
||
|
{
|
||
|
DWORD status = MsgWaitForMultipleObjectsEx(1, &thread.wakeupEvent, INFINITE,
|
||
|
QS_ALLPOSTMESSAGE | QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
|
||
|
|
||
|
switch(status)
|
||
|
{
|
||
|
case (WAIT_OBJECT_0 + 0):
|
||
|
// wake up!!!
|
||
|
break;
|
||
|
|
||
|
case (WAIT_OBJECT_0 + 1):
|
||
|
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
||
|
{
|
||
|
if (!CallMsgFilter(&msg, MSGF_BROWSERLOOP) && NULL != msg.hwnd)
|
||
|
{
|
||
|
if (0 == (BHTF_BEGINDESTROY & thread.flags))
|
||
|
{
|
||
|
if (FALSE == BrowserThread_ProcessMessage(hHost, hWinamp, &msg, IsHostMessage))
|
||
|
{
|
||
|
TranslateMessage(&msg);
|
||
|
DispatchMessageW(&msg);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DispatchMessageW(&msg);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BrowserThread_FinishThread(&thread);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
INT BrowserThread_ModalLoop(HWND hwnd, HANDLE hCancel, DWORD timeout)
|
||
|
{
|
||
|
MSG msg;
|
||
|
for (;;)
|
||
|
{
|
||
|
DWORD status = MsgWaitForMultipleObjectsEx(1, &hCancel, timeout, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
|
||
|
if (WAIT_OBJECT_0 == status)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
else if ((WAIT_OBJECT_0 + 1) == status)
|
||
|
{
|
||
|
while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
|
||
|
{
|
||
|
if (msg.message == WM_QUIT)
|
||
|
{
|
||
|
PostQuitMessage((INT)msg.wParam);
|
||
|
return (INT)msg.wParam;
|
||
|
}
|
||
|
|
||
|
if (!IsDialogMessageW(hwnd, &msg))
|
||
|
{
|
||
|
TranslateMessage(&msg);
|
||
|
DispatchMessageW(&msg);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|