Сообщение Re[2]: Кликнуть в скрытое окно от 06.08.2019 4:48
Изменено 06.08.2019 5:06 Cannol
Re[2]: Кликнуть в скрытое окно
Бесполезно, не фурычит.
Если кому вдруг интересно, привожу ниже код.
Вот так создаю окно:
А вот 2 ключевые функции:
Как тут видно, перепробовал разные варианты (закомментированные, через static, через "else if (byCodeOption == ***)", и т.д.).
В том числе попробовал посоветованные выше IAccessible, SetWindowLong() и сильно отрицательные координаты. Что-то ничто не помогает.
Теперь уже буду пробовать другие упомянутые выше советы, типа Sciter.
Здравствуйте, CEMb, Вы писали:
CEM>Гипотеза: процесс окна проверяет у себя WS_VISIBLE.
Там стандартнейший и простейший IWebBrowser2 (этот проект), я несколько сомневаюсь, что там есть такое. Но таки да, подозрения эти обоснованы!
Тем не менее SetWindowLong() тоже не помог. Может, я где-то накосячил в другом месте.
Если кому вдруг интересно, привожу ниже код.
Вот так создаю окно:
//hWndMain = CreateWindowEx(WS_EX_TOPMOST, szWndClassMain, szWndTitleMain, WS_OVERLAPPEDWINDOW, dwWndX, dwWndY, wWndWidth, wWndHeight, NULL, NULL, hInst, NULL);
hWndMain = CreateWindowEx(WS_EX_TRANSPARENT, szWndClassMain, szWndTitleMain, WS_OVERLAPPEDWINDOW, dwWndX, dwWndY, wWndWidth, wWndHeight, NULL, NULL, hInst, NULL);
А вот 2 ключевые функции:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool GrabTheScreen()
{
RECT rc;
HWND hwnd = FindWindow(szWndClassMain, NULL); //the window can't be min
if (hwnd == NULL)
{
Write2Log(fpLogFile, "Error: window not found.\n");
return false;
}
GetClientRect(hwnd, &rc);
//create
HDC hdcScreen = GetDC(NULL);
HDC hdc = CreateCompatibleDC(hdcScreen);
HBITMAP hbmp = CreateCompatibleBitmap(hdcScreen, rc.right - rc.left, rc.bottom - rc.top);
SelectObject(hdc, hbmp);
// Code to debug:
if (byCodeOption <= 100)
ShowWindow(hwnd, SW_SHOWNOACTIVATE);
//SetForegroundWindow(hwnd);
else if (byCodeOption > 100)
{
DWORD dwStyle=GetWindowLong(hwnd, GWL_STYLE);
SetWindowLongPtr(hwnd, GWL_STYLE, dwStyle | WS_VISIBLE); // SetWindowLong(hwnd, GWL_STYLE, dwStyle | WS_VISIBLE);
}
//Print to memory hdc
PrintWindow(hwnd, hdc, PW_CLIENTONLY);
// Code to debug:
if (byCodeOption <= 100)
ShowWindow(hwnd, SW_HIDE);
pCockpit->setBitmap(hbmp); // m_pRT->CreateBitmap(...)
//release
DeleteDC(hdc);
DeleteObject(hbmp);
ReleaseDC(NULL, hdcScreen);
Write2Log(fpLogFile, "Image grabbed successfully!\n");
return true;
} // GrabTheScreen
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool EFB::ProcessTheMouseClick(PPIXPOINT relative_point, WebBrowser *pWebBrowser)
{
POINT ptOld; // previous cursor location
GetCursorPos(&ptOld);
IOleInPlaceFrame *pFrame;
IOleInPlaceUIWindow *pDoc;
RECT prcPosRect;
RECT prcClipRect;
tagOIFI pFrameInfo;
pFrameInfo.cb = sizeof(tagOIFI);
HRESULT hr = pWebBrowser->GetWindowContext(&pFrame, &pDoc, &prcPosRect, &prcClipRect, &pFrameInfo);
HWND hwnd = pFrameInfo.hwndFrame;
RECT rcClip; // new area for ClipCursor
RECT rcOldClip; // previous area for ClipCursor
GetClipCursor(&rcOldClip); // Record the area in which the cursor can move.
GetWindowRect(hwnd, &rcClip); // Get the dimensions of the application's window.
// Code to debug:
static bool bClipCursor = true;
if (bClipCursor)
ClipCursor(&rcClip); // Confine the cursor to the application's window.
//
// Process input from the confined cursor.
//
POINT pt;
const int nSizeX = rcClip.right - rcClip.left;
const int nSizeY = rcClip.bottom - rcClip.top;
int nRelX = Round(((double) relative_point->x / m_wGaugeWidth) * nSizeX) + rcClip.left;
int nRelY = Round(((double) relative_point->y / m_wGaugeHeight) * nSizeY) + rcClip.top;
Write2Log(fpLogFile, "PPIXPOINT: [%d*%d]=>[%d*%d].\n", relative_point->x, relative_point->y, nRelX, nRelY);
pt.x = nRelX;
pt.y = nRelY;
// Code to debug:
static BYTE byShowTheWnd = 0;
if (byCodeOption > 100)
{
//static int PosX = 30000;
//static int PosY = PosX;
//static UINT uFlags = SWP_NOSIZE;
//SetWindowPos(hwnd, HWND_TOP, PosX, PosY, nSizeX, nSizeY, uFlags);
DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE);
SetWindowLongPtr(hwnd, GWL_STYLE, dwStyle | WS_VISIBLE); // SetWindowLong(hwnd, GWL_STYLE, dwStyle | WS_VISIBLE);
}
else if (byShowTheWnd == 0)
ShowWindow(hwnd, SW_SHOWNORMAL);
else if (byShowTheWnd == 1)
ShowWindow(hwnd, SW_SHOWNOACTIVATE);
// Code to debug:
static BYTE byForegroundFuncAlt = 1;
if (byForegroundFuncAlt == 1)
//SetForegroundWindow(hwnd);
SetActiveWindow(hwnd);
else if (byForegroundFuncAlt == 2)
{
static int PosX = -3000;
static int PosY = PosX;
static UINT uFlags = 0;
//SetWindowPos(hwnd, HWND_NOTOPMOST, PosX, PosY, nSizeX, nSizeY, uFlags);
SetWindowPos(hwnd, HWND_TOP, PosX, PosY, nSizeX, nSizeY, uFlags);
}
SetCursorPos(pt.x, pt.y);
//
// Process mouse button press.
//
// Code to debug:
if (byCodeOption == 0 || byCodeOption == 101)
{
INPUT input={0};
// left down
input.type = INPUT_MOUSE;
input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN; // mouse button down
SendInput(1,&input,sizeof(INPUT)); //call SendInput, pass in input to click
Sleep(1);
// left up
ZeroMemory(&input,sizeof(INPUT)); //clear out input
input.type = INPUT_MOUSE;
input.mi.dwFlags = MOUSEEVENTF_LEFTUP; // mouse button up
SendInput(1,&input,sizeof(INPUT)); //call SendInput, pass in input to unclick
/*
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
*/
}
else if (byCodeOption == 2)
{
// Option B1
SendMessage(hwnd, WM_LBUTTONDOWN, pt.x, pt.y);
Sleep(10);
SendMessage(hwnd, WM_LBUTTONUP, pt.x, pt.y);
}
else if (byCodeOption == 4)
{
// Option B2
SendMessage(hwnd, WM_LBUTTONDOWN, MK_LBUTTON, MAKELPARAM(pt.x, pt.y));
Sleep(10);
SendMessage(hwnd, WM_LBUTTONUP, MK_LBUTTON, MAKELPARAM(pt.x, pt.y));
}
else if (byCodeOption == 6)
{
// Option C1
PostMessage(hwnd, WM_LBUTTONDOWN, 0, MAKELPARAM(pt.x, pt.y));
Sleep(5);
PostMessage(hwnd, WM_LBUTTONUP, 0, MAKELPARAM(pt.x, pt.y));
}
else if (byCodeOption == 8)
{
// Option C2
PostMessage(hwnd, WM_LBUTTONDOWN, MK_LBUTTON, MAKELPARAM(pt.x, pt.y));
Sleep(5);
PostMessage(hwnd, WM_LBUTTONUP, MK_LBUTTON, MAKELPARAM(pt.x, pt.y));
}
else if (byCodeOption == 10)
{
// Option C3
PostMessage(hwnd, WM_LBUTTONDOWN, pt.x, pt.y);
Sleep(5);
PostMessage(hwnd, WM_LBUTTONUP, pt.x, pt.y);
}
else if (byCodeOption == 100)
{
CString Name;
CPoint CursorPos;
GetCursorPos(&CursorPos);
IAccessiblePtr pIAcc;
_variant_t vt;
BSTR pName = NULL; BSTR pValue = NULL;
//assert((pt.x == CursorPos.x) && (pt.y == CursorPos.y));
if (!((pt.x == CursorPos.x) && (pt.y == CursorPos.y)))
{
Write2Log(fpLogFile, "Cursor out or range: [%d,%d] vs [%d,%d].\n", CursorPos.x, CursorPos.y, pt.x, pt.y);
static bool bUseAnotherCoords = false;
if (bUseAnotherCoords)
{
CursorPos.x = pt.x;
CursorPos.y = pt.y;
}
}
HRESULT hr;
static bool bAccessCursor = true;
if (bAccessCursor)
hr = AccessibleObjectFromPoint(CursorPos, &pIAcc, &vt);
else
hr = AccessibleObjectFromWindow(hwnd, OBJID_CURSOR, IID_IAccessible, (void **) &pIAcc);
if (SUCCEEDED(hr))
{
VARIANT varChild;
hr = pIAcc->accHitTest(pt.x, pt.y, &varChild);
//if ((hr == S_OK) && (varChild.lVal != CHILDID_SELF))
//{
// hr = pIAcc->accSelect((SELFLAG_TAKEFOCUS | SELFLAG_TAKESELECTION), varChild);
//}
{
//pIAcc->get_accChild(
BSTR pName = NULL;
hr = pIAcc->get_accName(vt, &pName);
if (SUCCEEDED(hr))
{
if (pName && ::SysStringLen(pName))
Name = pName;
VARIANT varId;
varId.vt = VT_I4;
varId.lVal = CHILDID_SELF;
hr = pIAcc->accDoDefaultAction(varId);
if (SUCCEEDED(hr))
Write2Log(fpLogFile, "Webbrowser DoAction OK: '%s'.\n", Name.Left(200));
else
//Write2Log(fpLogFile, "Webbrowser DoAction failed: %05s, 0x%X.\n", Name.GetBuffer(), hr);
Write2Log(fpLogFile, "Webbrowser DoAction failed: '%s', 0x%X.\n", Name.Left(200), hr);
::SysFreeString(pName);
}
}
}
}
ClipCursor(&rcOldClip); // Restore the cursor to its previous area.
SetCursorPos(ptOld.x, ptOld.y); // Restore previous coordinates of the cursor.
// Code to debug:
static bool bHideWnd = false;
if (bHideWnd)
ShowWindow(hwnd, SW_HIDE);
return true;
} // ProcessTheMouseClick
Как тут видно, перепробовал разные варианты (закомментированные, через static, через "else if (byCodeOption == ***)", и т.д.).
В том числе попробовал посоветованные выше IAccessible, SetWindowLong() и сильно отрицательные координаты. Что-то ничто не помогает.
Теперь уже буду пробовать другие упомянутые выше советы, типа Sciter.
Здравствуйте, CEMb, Вы писали:
CEM>Гипотеза: процесс окна проверяет у себя WS_VISIBLE.
Там стандартнейший и простейший IWebBrowser2 (этот проект), я несколько сомневаюсь, что там есть такое. Но таки да, подозрения эти обоснованы!
Тем не менее SetWindowLong() тоже не помог. Может, я где-то накосячил в другом месте.
Re[2]: Кликнуть в скрытое окно
Бесполезно, не фурычит.
Если кому вдруг интересно, привожу ниже код.
Вот так создаю окно:
А вот 2 ключевые функции:
Как тут видно, перепробовал разные варианты (закомментированные, через static, через "else if (byCodeOption == ***)", и т.д.).
В том числе попробовал посоветованные выше IAccessible, SetWindowLong() и сильно отрицательные координаты. Что-то ничто не помогает.
Теперь уже буду пробовать другие упомянутые выше советы, типа Sciter. Вообще сторонние либы я не очень-то хотел, ибо там обычно заморочки с лицензированием.
Здравствуйте, CEMb, Вы писали:
CEM>Гипотеза: процесс окна проверяет у себя WS_VISIBLE.
Там стандартнейший и простейший IWebBrowser2 (этот проект), я несколько сомневаюсь, что там есть такое. Но таки да, подозрения эти обоснованы!
Тем не менее SetWindowLong() тоже не помог. Может, я где-то накосячил в другом месте.
ЗЫ. Нагуглил еще AttachThreadInput() ("Функция AttachThreadInput подключает или отключает механизм обработки ввода данных одного из потоков к механизму другого потока.") Кажется, тоже есть смысл попробовать. Создать для того скрытого окна отдельный поток и продублировать туда сообщения из основного 3D приложения. Опять же не факт, что таким макаром сообщения таки будут приходить туда.
Если кому вдруг интересно, привожу ниже код.
Вот так создаю окно:
//hWndMain = CreateWindowEx(WS_EX_TOPMOST, szWndClassMain, szWndTitleMain, WS_OVERLAPPEDWINDOW, dwWndX, dwWndY, wWndWidth, wWndHeight, NULL, NULL, hInst, NULL);
hWndMain = CreateWindowEx(WS_EX_TRANSPARENT, szWndClassMain, szWndTitleMain, WS_OVERLAPPEDWINDOW, dwWndX, dwWndY, wWndWidth, wWndHeight, NULL, NULL, hInst, NULL);
А вот 2 ключевые функции:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool GrabTheScreen()
{
RECT rc;
HWND hwnd = FindWindow(szWndClassMain, NULL); //the window can't be min
if (hwnd == NULL)
{
Write2Log(fpLogFile, "Error: window not found.\n");
return false;
}
GetClientRect(hwnd, &rc);
//create
HDC hdcScreen = GetDC(NULL);
HDC hdc = CreateCompatibleDC(hdcScreen);
HBITMAP hbmp = CreateCompatibleBitmap(hdcScreen, rc.right - rc.left, rc.bottom - rc.top);
SelectObject(hdc, hbmp);
// Code to debug:
if (byCodeOption <= 100)
ShowWindow(hwnd, SW_SHOWNOACTIVATE);
//SetForegroundWindow(hwnd);
else if (byCodeOption > 100)
{
DWORD dwStyle=GetWindowLong(hwnd, GWL_STYLE);
SetWindowLongPtr(hwnd, GWL_STYLE, dwStyle | WS_VISIBLE); // SetWindowLong(hwnd, GWL_STYLE, dwStyle | WS_VISIBLE);
}
//Print to memory hdc
PrintWindow(hwnd, hdc, PW_CLIENTONLY);
// Code to debug:
if (byCodeOption <= 100)
ShowWindow(hwnd, SW_HIDE);
pTablet->setBitmap(hbmp); // m_pRT->CreateBitmap(...)
//release
DeleteDC(hdc);
DeleteObject(hbmp);
ReleaseDC(NULL, hdcScreen);
Write2Log(fpLogFile, "Image grabbed successfully!\n");
return true;
} // GrabTheScreen
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool EFB::ProcessTheMouseClick(PPIXPOINT relative_point, WebBrowser *pWebBrowser)
{
POINT ptOld; // previous cursor location
GetCursorPos(&ptOld);
IOleInPlaceFrame *pFrame;
IOleInPlaceUIWindow *pDoc;
RECT prcPosRect;
RECT prcClipRect;
tagOIFI pFrameInfo;
pFrameInfo.cb = sizeof(tagOIFI);
HRESULT hr = pWebBrowser->GetWindowContext(&pFrame, &pDoc, &prcPosRect, &prcClipRect, &pFrameInfo);
HWND hwnd = pFrameInfo.hwndFrame;
RECT rcClip; // new area for ClipCursor
RECT rcOldClip; // previous area for ClipCursor
GetClipCursor(&rcOldClip); // Record the area in which the cursor can move.
GetWindowRect(hwnd, &rcClip); // Get the dimensions of the application's window.
// Code to debug:
static bool bClipCursor = true;
if (bClipCursor)
ClipCursor(&rcClip); // Confine the cursor to the application's window.
//
// Process input from the confined cursor.
//
POINT pt;
const int nSizeX = rcClip.right - rcClip.left;
const int nSizeY = rcClip.bottom - rcClip.top;
int nRelX = Round(((double) relative_point->x / m_wGaugeWidth) * nSizeX) + rcClip.left;
int nRelY = Round(((double) relative_point->y / m_wGaugeHeight) * nSizeY) + rcClip.top;
Write2Log(fpLogFile, "PPIXPOINT: [%d*%d]=>[%d*%d].\n", relative_point->x, relative_point->y, nRelX, nRelY);
pt.x = nRelX;
pt.y = nRelY;
// Code to debug:
static BYTE byShowTheWnd = 0;
if (byCodeOption > 100)
{
//static int PosX = 30000;
//static int PosY = PosX;
//static UINT uFlags = SWP_NOSIZE;
//SetWindowPos(hwnd, HWND_TOP, PosX, PosY, nSizeX, nSizeY, uFlags);
DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE);
SetWindowLongPtr(hwnd, GWL_STYLE, dwStyle | WS_VISIBLE); // SetWindowLong(hwnd, GWL_STYLE, dwStyle | WS_VISIBLE);
}
else if (byShowTheWnd == 0)
ShowWindow(hwnd, SW_SHOWNORMAL);
else if (byShowTheWnd == 1)
ShowWindow(hwnd, SW_SHOWNOACTIVATE);
// Code to debug:
static BYTE byForegroundFuncAlt = 1;
if (byForegroundFuncAlt == 1)
//SetForegroundWindow(hwnd);
SetActiveWindow(hwnd);
else if (byForegroundFuncAlt == 2)
{
static int PosX = -3000;
static int PosY = PosX;
static UINT uFlags = 0;
//SetWindowPos(hwnd, HWND_NOTOPMOST, PosX, PosY, nSizeX, nSizeY, uFlags);
SetWindowPos(hwnd, HWND_TOP, PosX, PosY, nSizeX, nSizeY, uFlags);
}
SetCursorPos(pt.x, pt.y);
//
// Process mouse button press.
//
// Code to debug:
if (byCodeOption == 0 || byCodeOption == 101)
{
INPUT input={0};
// left down
input.type = INPUT_MOUSE;
input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN; // mouse button down
SendInput(1,&input,sizeof(INPUT)); //call SendInput, pass in input to click
Sleep(1);
// left up
ZeroMemory(&input,sizeof(INPUT)); //clear out input
input.type = INPUT_MOUSE;
input.mi.dwFlags = MOUSEEVENTF_LEFTUP; // mouse button up
SendInput(1,&input,sizeof(INPUT)); //call SendInput, pass in input to unclick
/*
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
*/
}
else if (byCodeOption == 2)
{
// Option B1
SendMessage(hwnd, WM_LBUTTONDOWN, pt.x, pt.y);
Sleep(10);
SendMessage(hwnd, WM_LBUTTONUP, pt.x, pt.y);
}
else if (byCodeOption == 4)
{
// Option B2
SendMessage(hwnd, WM_LBUTTONDOWN, MK_LBUTTON, MAKELPARAM(pt.x, pt.y));
Sleep(10);
SendMessage(hwnd, WM_LBUTTONUP, MK_LBUTTON, MAKELPARAM(pt.x, pt.y));
}
else if (byCodeOption == 6)
{
// Option C1
PostMessage(hwnd, WM_LBUTTONDOWN, 0, MAKELPARAM(pt.x, pt.y));
Sleep(5);
PostMessage(hwnd, WM_LBUTTONUP, 0, MAKELPARAM(pt.x, pt.y));
}
else if (byCodeOption == 8)
{
// Option C2
PostMessage(hwnd, WM_LBUTTONDOWN, MK_LBUTTON, MAKELPARAM(pt.x, pt.y));
Sleep(5);
PostMessage(hwnd, WM_LBUTTONUP, MK_LBUTTON, MAKELPARAM(pt.x, pt.y));
}
else if (byCodeOption == 10)
{
// Option C3
PostMessage(hwnd, WM_LBUTTONDOWN, pt.x, pt.y);
Sleep(5);
PostMessage(hwnd, WM_LBUTTONUP, pt.x, pt.y);
}
else if (byCodeOption == 100)
{
CString Name;
CPoint CursorPos;
GetCursorPos(&CursorPos);
IAccessiblePtr pIAcc;
_variant_t vt;
BSTR pName = NULL; BSTR pValue = NULL;
//assert((pt.x == CursorPos.x) && (pt.y == CursorPos.y));
if (!((pt.x == CursorPos.x) && (pt.y == CursorPos.y)))
{
Write2Log(fpLogFile, "Cursor out or range: [%d,%d] vs [%d,%d].\n", CursorPos.x, CursorPos.y, pt.x, pt.y);
static bool bUseAnotherCoords = false;
if (bUseAnotherCoords)
{
CursorPos.x = pt.x;
CursorPos.y = pt.y;
}
}
HRESULT hr;
static bool bAccessCursor = true;
if (bAccessCursor)
hr = AccessibleObjectFromPoint(CursorPos, &pIAcc, &vt);
else
hr = AccessibleObjectFromWindow(hwnd, OBJID_CURSOR, IID_IAccessible, (void **) &pIAcc);
if (SUCCEEDED(hr))
{
VARIANT varChild;
hr = pIAcc->accHitTest(pt.x, pt.y, &varChild);
//if ((hr == S_OK) && (varChild.lVal != CHILDID_SELF))
//{
// hr = pIAcc->accSelect((SELFLAG_TAKEFOCUS | SELFLAG_TAKESELECTION), varChild);
//}
{
//pIAcc->get_accChild(
BSTR pName = NULL;
hr = pIAcc->get_accName(vt, &pName);
if (SUCCEEDED(hr))
{
if (pName && ::SysStringLen(pName))
Name = pName;
VARIANT varId;
varId.vt = VT_I4;
varId.lVal = CHILDID_SELF;
hr = pIAcc->accDoDefaultAction(varId);
if (SUCCEEDED(hr))
Write2Log(fpLogFile, "Webbrowser DoAction OK: '%s'.\n", Name.Left(200));
else
//Write2Log(fpLogFile, "Webbrowser DoAction failed: %05s, 0x%X.\n", Name.GetBuffer(), hr);
Write2Log(fpLogFile, "Webbrowser DoAction failed: '%s', 0x%X.\n", Name.Left(200), hr);
::SysFreeString(pName);
}
}
}
}
ClipCursor(&rcOldClip); // Restore the cursor to its previous area.
SetCursorPos(ptOld.x, ptOld.y); // Restore previous coordinates of the cursor.
// Code to debug:
static bool bHideWnd = false;
if (bHideWnd)
ShowWindow(hwnd, SW_HIDE);
return true;
} // ProcessTheMouseClick
Как тут видно, перепробовал разные варианты (закомментированные, через static, через "else if (byCodeOption == ***)", и т.д.).
В том числе попробовал посоветованные выше IAccessible, SetWindowLong() и сильно отрицательные координаты. Что-то ничто не помогает.
Теперь уже буду пробовать другие упомянутые выше советы, типа Sciter. Вообще сторонние либы я не очень-то хотел, ибо там обычно заморочки с лицензированием.
Здравствуйте, CEMb, Вы писали:
CEM>Гипотеза: процесс окна проверяет у себя WS_VISIBLE.
Там стандартнейший и простейший IWebBrowser2 (этот проект), я несколько сомневаюсь, что там есть такое. Но таки да, подозрения эти обоснованы!
Тем не менее SetWindowLong() тоже не помог. Может, я где-то накосячил в другом месте.
ЗЫ. Нагуглил еще AttachThreadInput() ("Функция AttachThreadInput подключает или отключает механизм обработки ввода данных одного из потоков к механизму другого потока.") Кажется, тоже есть смысл попробовать. Создать для того скрытого окна отдельный поток и продублировать туда сообщения из основного 3D приложения. Опять же не факт, что таким макаром сообщения таки будут приходить туда.