Кликнуть в скрытое окно
От: Cannol  
Дата: 24.07.19 10:20
Оценка:
Прошу подсказать, как правильно кликать в скрытое окно!
Есть подобие виртуальной кабины автомобиля и там я моделирую планшет (как электронный гаджет) возле приборной панели. Надо, чтобы веб-браузер был отрисован на экране этого виртуального планшета.
Для этого:

1. Пришлось для браузера создать отдельное 2Д окно, и делаю я его неотображаемым (SW_HIDE, или вот щас WS_EX_TRANSPARENT), в том же потоке:
hWndMain = CreateWindowEx(WS_EX_TRANSPARENT, szWndClassMain, szWndTitleMain, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, wWndWidth, wWndHeight, NULL, NULL, hInst, NULL);

2. Туда вывожу работу браузера (простейший проект на IWebBrowser2, это как "Internet Explorer");

3. Захватываю содержимое этого скрытого окна, используя PrintWindow(),
(При этом, когда SW_HIDE, получается просто чёрный экран. И пришлось, перед тем как сграбить, менять окно на SW_SHOWNORMAL или щас на SW_SHOWNOACTIVATE, а потом сразу возвращать на SW_HIDE.)

4. Полученный Bitmap загружаю в DirectX окно: m_pRenderTarget->DrawBitmap();
До сих пор всё хорошо!

Сейчас который день мучаюсь над другой проблемой:
надо реализовать мышиные клики от пользователя. Т.е. юзер кликает в том DirectX окне по ссылкам на веб-страничке, а фактически всего лишь на Bitmap картинку от неё в DirectX.
И надо из этой DirectX-картинки переслать событие клика на само IWebBrowser2 окно. (А потом снова сграбить новое состояние страницы и показать bitmap в 3D.)
Думалось, есть небось какая-нибудь готовая функция типа webBrowser2->Navigate(хPos, уPos), но увы, такой не нашлось.

Поэтому пришлось транслировать координаты, и отправлять сообщения в то скрытое окно. Перепробовал много вещей, ничто не помогает:
I. Когда окно предварительно делаю активным и видимым (ShowWindow(hwnd, SW_SHOWNOACTIVATE); SetActiveWindow(hwnd)), тогда SetCursorPos() и SendInput() работают хорошо — клик отправляется, браузер меняет страницу, я ее снова граблю и т.д..
II. Когда к этому коду добавляю в самом конце скрытие окна (SW_HIDE), всё перестаёт работать, т.е. браузер больше не получает клики! Очень странно, я ведь событие отправил до вызова SW_HIDE!
Вот конец кода обработки клика:
    ...
    SendInput(1,&input,sizeof(INPUT)); //call SendInput, pass in input to unclick
    ClipCursor(&rcOldClip); // Restore the cursor to its previous area.
    SetCursorPos(ptOld.x, ptOld.y); // Restore previous coordinates of the cursor.
    ShowWindow(hwnd, SW_HIDE);

Вариант I не годится никак, потому что показывать на экране это 2D окно браузера мне никак не надо!

III. Не показывая окна вообще на экран (всегда SW_HIDE) пытаюсь посылать сообщения по-разному (SendMessage/PostMessage) — вообще глухо, ни один вариант не работает. Вот код:
...
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);
}
    // Завершение
    ClipCursor(&rcOldClip); // Restore the cursor to its previous area.
    SetCursorPos(ptOld.x, ptOld.y); // Restore previous coordinates of the cursor.
Re: Кликнуть в скрытое окно
От: Carc Россия https://vk.com/gosha_mazov
Дата: 24.07.19 11:01
Оценка: 3 (1)
Здравствуйте, Cannol, Вы писали:

C>Прошу подсказать, как правильно кликать в скрытое окно!

C>Есть подобие виртуальной кабины автомобиля и там я моделирую планшет (как электронный гаджет) возле приборной панели. Надо, чтобы веб-браузер был отрисован на экране этого виртуального планшета.
C>Для этого:

C>1. Пришлось для браузера создать отдельное 2Д окно, и делаю я его неотображаемым (SW_HIDE, или вот щас WS_EX_TRANSPARENT), в том же потоке:

C>
hWndMain = CreateWindowEx(WS_EX_TRANSPARENT, szWndClassMain, szWndTitleMain, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, wWndWidth, wWndHeight, NULL, NULL, hInst, NULL);

Можно как вариант сделать иначе. Окно с этим IWebBrowser2 оставить видимым, но не показывать его в пределах рабочего стола, и в панели задач.

Например, именно так делается, чтобы в списке окон приложений по Alt+Tab, чтобы все таки иконка приложения была, а вот само главное окно скрыто (когда, например свернуто в трей).

По сути создается фейковое видимое окно, задвигается за пределы рабочего стола (что-нить в стиле SetWindowPos(…, -3000,-3000, 20,20), и скрывается из панели задач внизу рабочего стола. Тогда окно видимое, все работает, но пользователь его не увидит.

PS: в моем случае это именно для трея и делалось. Это фейковое окно, получало по Alt+Tab WM_ACTIVATE и просто дергало главне окно приложения, мол давай подымайся из трея (иконка в трее иконкой, но де факто само главное окно именно что скрыто).
Aml Pages Home
Отредактировано 24.07.2019 11:02 Carc . Предыдущая версия . Еще …
Отредактировано 24.07.2019 11:02 Carc . Предыдущая версия .
Re[2]: Кликнуть в скрытое окно
От: Cannol  
Дата: 24.07.19 14:53
Оценка:
Здравствуйте, Carc, Вы писали:
C>По сути создается фейковое видимое окно, задвигается за пределы рабочего стола (что-нить в стиле SetWindowPos(…, -3000,-3000, 20,20), и скрывается из панели задач внизу рабочего стола. Тогда окно видимое, все работает, но пользователь его не увидит.

Спасибо вам большое!
Надо попробовать. Такой вариант ко мне в голову не приходил еще.
Надеюсь, такое окно точно НЕ будет видно, даже если экран расширен на 2 или более монитора.

А навскидку, что вы использовали для скрытия из панели задач? Я погуглил, пишут про ShowWindow(SW_HIDE) из onShow(), но у меня не MFC.
Re[3]: Кликнуть в скрытое окно
От: Carc Россия https://vk.com/gosha_mazov
Дата: 24.07.19 15:20
Оценка:
Здравствуйте, Cannol, Вы писали:

C>А навскидку, что вы использовали для скрытия из панели задач?

Можно такому фейковому окну, назначить hwndParent какой-нить…
Ну и плюс интерфейс ITaskbarList::DeleteTab
Aml Pages Home
Re: Кликнуть в скрытое окно
От: LuciferSaratov Россия  
Дата: 24.07.19 15:50
Оценка: 3 (1)
Здравствуйте, Cannol, Вы писали:

C>Прошу подсказать, как правильно кликать в скрытое окно!


IWebBrowser2 должен поддерживать IAccessible/IAccessibleEx/IAccessible2, попробуй в этом направлении поискать.
это интерфейс для интеграции с инструментами для помощи инвалидам, думаю, у него должно быть достаточно возможностей для контроля веб-браузера так, как тебе надо.
Re: Кликнуть в скрытое окно
От: LuciferSaratov Россия  
Дата: 24.07.19 15:57
Оценка:
Здравствуйте, Cannol, Вы писали:

C>Прошу подсказать, как правильно кликать в скрытое окно!


ну и вдогонку, я бы в такой ситуации применил https://sciter.com/, гораздо более гибкое решение подобных задач.
понятно, что если уже кода вокруг интернет-эксплорера много, то переделывать уже может быть поздно, но это так, непрошенный совет в копилку инструментов.
Re: Кликнуть в скрытое окно
От: Cannol  
Дата: 24.07.19 21:28
Оценка:
Большое вам человеческое спасибо, буду пробовать!
Re[2]: Кликнуть в скрытое окно
От: Mystic Artifact  
Дата: 24.07.19 22:21
Оценка: 3 (1) +1
Здравствуйте, Cannol, Вы писали:

C>Большое вам человеческое спасибо, буду пробовать!

На этом форуме есть оценки.

Вообще вам идеально бы подошли т.н. headless browsers, особенно, если нужно потребить реальный веб-контент. Их достаточно много, и... задача немного не для них наверное (пузатые бинарники и т.п.). Но, в сущности, безголовые фреймворки/браузеры умеют рендерить контент в битмап или текстуру — и это как раз ваш случай, где это необходимо. Решение с махинацией окнами — это скорее хак.
Но я это говорю, просто для справки, и ни в коем случае не агитирую за тот же CEF, т.к. это будет оверкил и прийдется знакомится с кучей всего лишнего.
Я крайне позитивно отношусь к Sciter — но это решение для своего контента, а не внешнего контента (насколько я знаю).
Re: Кликнуть в скрытое окно
От: CEMb  
Дата: 01.08.19 07:13
Оценка: 2 (1)
Здравствуйте, Cannol, Вы писали:

C>Сейчас который день мучаюсь над другой проблемой:

C>надо реализовать мышиные клики от пользователя. Т.е. юзер кликает в том DirectX окне по ссылкам на веб-страничке, а фактически всего лишь на Bitmap картинку от неё в DirectX.
C>И надо из этой DirectX-картинки переслать событие клика на само IWebBrowser2 окно. (А потом снова сграбить новое состояние страницы и показать bitmap в 3D.)
C>Думалось, есть небось какая-нибудь готовая функция типа webBrowser2->Navigate(хPos, уPos), но увы, такой не нашлось.

C>Поэтому пришлось транслировать координаты, и отправлять сообщения в то скрытое окно. Перепробовал много вещей, ничто не помогает:


C>II. Когда к этому коду добавляю в самом конце скрытие окна (SW_HIDE), всё перестаёт работать, т.е. браузер больше не получает клики! Очень странно, я ведь событие отправил до вызова SW_HIDE!


C>III. Не показывая окна вообще на экран (всегда SW_HIDE) пытаюсь посылать сообщения по-разному (SendMessage/PostMessage) — вообще глухо, ни один вариант не работает.


Гипотеза: процесс окна проверяет у себя WS_VISIBLE.
Ок, насколько я знаю, ShowWindow вызывает перерисовку, а SetWindowLong — нет. Поэтому можно попробовать "показать" окно через SetWindowLong(hWnd, GWL_STYLE, dwFlags | WS_VISIBLE), окно для глаз будет невидимо, но процесс будет считать, что оно видимо. Тут есть косяк: как только сменится страница — пойдёт перерисовка.
Re[2]: Кликнуть в скрытое окно
От: Carc Россия https://vk.com/gosha_mazov
Дата: 01.08.19 08:16
Оценка:
Здравствуйте, CEMb, Вы писали:

CEM>Гипотеза: процесс окна проверяет у себя WS_VISIBLE.

CEM>Ок, насколько я знаю, ShowWindow вызывает перерисовку, а SetWindowLong — нет. Поэтому можно попробовать "показать" окно через SetWindowLong(hWnd, GWL_STYLE, dwFlags | WS_VISIBLE), окно для глаз будет невидимо, но процесс будет считать, что оно видимо. Тут есть косяк: как только сменится страница — пойдёт перерисовка.
Не думаю, что это хорошая идея…
Потом кто-нибудь по пьяни или и вовсе вполне нормальный код позовет SetWindowPos + SWP_FRAMECHANGED, и тут оно вот оно! Окошко и прорисуется. Имхо, логичнее убрать окно с панели задач и задвинуть его за края видимого десктопа.
Ну разве что придется помедититировать на предмет дополнительных мониторов.
Aml Pages Home
Re[3]: Кликнуть в скрытое окно
От: CEMb  
Дата: 02.08.19 06:11
Оценка:
Здравствуйте, Carc, Вы писали:

CEM>>Гипотеза: процесс окна проверяет у себя WS_VISIBLE.

CEM>>Ок, насколько я знаю, ShowWindow вызывает перерисовку, а SetWindowLong — нет. Поэтому можно попробовать "показать" окно через SetWindowLong(hWnd, GWL_STYLE, dwFlags | WS_VISIBLE), окно для глаз будет невидимо, но процесс будет считать, что оно видимо. Тут есть косяк: как только сменится страница — пойдёт перерисовка.
C>Не думаю, что это хорошая идея…
C>Потом кто-нибудь по пьяни или и вовсе вполне нормальный код позовет SetWindowPos + SWP_FRAMECHANGED, и тут оно вот оно! Окошко и прорисуется. Имхо, логичнее убрать окно с панели задач и задвинуть его за края видимого десктопа.
C>Ну разве что придется помедититировать на предмет дополнительных мониторов.
Ну можно PrintWindow сделать, потом сразу убрать WS_VISIBLE
С убиранием окон за край экрана тоже могут быть проблемы: некоторые окна при передёргивании настроек десктопа (кто-то дёрнул за рубильник таскбар) норовят влезть обратно сами.
Я тут не так давно делал тумбнейлы (thumbnail) для винды до NT5 включительно, что важно, и с возможностью их смотреть и кликать(в родное окно через тумбнейл). Примерно та же задача, что у ТС, но только окна могут быть минимизированы. MS на это забил, у них в таскбаре если окно минимизировано, показывается иконка. Но мы-то простых путей, блин, не ищем я взял бубен и через стили, таймеры и позиции делал незаметное восстановление-отрисовку-свёртывание окна, в результате у меня даже что-то работало, хотя мелькания иногда были, поэтому релиз так и не случился В том числе и из-за Internet Explorer, у которого PrintWindow даже на статичном окне срабатывает почему-то не всегда.
Re[4]: Кликнуть в скрытое окно
От: Carc Россия https://vk.com/gosha_mazov
Дата: 02.08.19 06:23
Оценка:
Здравствуйте, CEMb, Вы писали:


C>>Потом кто-нибудь по пьяни или и вовсе вполне нормальный код позовет SetWindowPos + SWP_FRAMECHANGED, и тут оно вот оно! Окошко и прорисуется. Имхо, логичнее убрать окно с панели задач и задвинуть его за края видимого десктопа.

C>>Ну разве что придется помедититировать на предмет дополнительных мониторов.
CEM>Ну можно PrintWindow сделать, потом сразу убрать WS_VISIBLE
CEM>С убиранием окон за край экрана тоже могут быть проблемы: некоторые окна при передёргивании настроек десктопа (кто-то дёрнул за рубильник таскбар) норовят влезть обратно сами.
Други, я ж не об том…

Просто когда у окна нет Visible, то сторонний код может вести себя ну очень странно. И не потому что кривой код, а ибо «дизайн бай бехавиор» ©

Ну к примеру, у меня в моем приложении есть фейковое окно (не верхнего уровня). И это окно исключительно для внутреннего использования — сама софтина там много чего в фоне танце-бубнит с данными. Дык вот когда окно теряет WS_VISIBLE, мой фейковый танцо-бубновый окошек ну очень сильно меняет поведение. Нафиг отключает всякие нотификации, блокирует прорисовку к чертям и.т.д.

Соответственно, убирая WS_VISIBLE можно напоророться на несколько непредвиденное поведение.
И это имеет смысл держать в уме… Я только и только об этом.
Aml Pages Home
Re[5]: Кликнуть в скрытое окно
От: CEMb  
Дата: 02.08.19 07:05
Оценка:
Здравствуйте, Carc, Вы писали:

C>Соответственно, убирая WS_VISIBLE можно напоророться на несколько непредвиденное поведение.

C>И это имеет смысл держать в уме… Я только и только об этом.

Не, ну речь только про окна, у которых не было WS_VISIBLE изначально. Иначе они работают штатно, и надо только снять картинку.
А когда у них не было WS_VISIBLE, вот тогда сначала быстро ставим WS_VISIBLE, снимаем скальп, убираем WS_VISIBLE.
А с минимизированными окнами всё ещё хуже
Re[2]: Кликнуть в скрытое окно
От: Cannol  
Дата: 06.08.19 04:48
Оценка:
Бесполезно, не фурычит.
Если кому вдруг интересно, привожу ниже код.
Вот так создаю окно:

//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 приложения. Опять же не факт, что таким макаром сообщения таки будут приходить туда.
Отредактировано 06.08.2019 5:06 Cannol . Предыдущая версия .
Re[3]: Кликнуть в скрытое окно
От: Mystic Artifact  
Дата: 06.08.19 07:11
Оценка:
Здравствуйте, Cannol, Вы писали:

CC>В том числе попробовал посоветованные выше IAccessible, SetWindowLong() и сильно отрицательные координаты. Что-то ничто не помогает.

Сильно-отрицательные координаты (при постоянном видимом окне) как раз должны бы работать. Что если вообще сделать нормально видимым — принимает ли оно ввод? А если оставить пару пикселов?

C>Теперь уже буду пробовать другие упомянутые выше советы, типа Sciter. Вообще сторонние либы я не очень-то хотел, ибо там обычно заморочки с лицензированием.

С встраиванием IE проблем сильно больше. традиционные: непонятно какая версия (ну это уже не актуально), неясное (глобальное) конфигурирование, например, звук навигации, и дикие воркэраунды через изменение реестра.

C>Там стандартнейший и простейший IWebBrowser2 (этот проект), я несколько сомневаюсь, что там есть такое. Но таки да, подозрения эти обоснованы!

C>Тем не менее SetWindowLong() тоже не помог. Может, я где-то накосячил в другом месте.
SetWindowLong нейтрализуется с помощью GetWindowLong на вызываемой стороне.


C>ЗЫ. Нагуглил еще AttachThreadInput()

Тут, по идее это лишнее, если IE создает окно связанное с твоим потоком. Если же со своим — тогда стоит покопать в этом направлении.

PS: В дополнение ко всему IE наверняка еще и вызывает что-то на подобии GetKeyState для определения состояния клавиш-модификаторов, в том числе и мышиных (VK_LBUTTON). Он вполне может реагировать на внешние события как триггер обработки клавиатуры/мышки, ставить в очередь, и дочитывать реальное/текущее состояние для фильтрации событий совсем чуток погодя. Таким образом если вы пробрасываете настоящие нажатия — оно будет/должно работать, а если синтезируете события полностью программно, то не будет.

PPS: Не меняет ли IE размер своего окна на 0х0 в случае, если окно прячется?
Отредактировано 06.08.2019 7:12 Mystic Artifact . Предыдущая версия .
Re[4]: Кликнуть в скрытое окно
От: Cannol  
Дата: 06.08.19 20:21
Оценка:
Здравствуйте, Mystic Artifact, Вы писали:
MA> Сильно-отрицательные координаты (при постоянном видимом окне) как раз должны бы работать. Что если вообще сделать нормально видимым — принимает ли оно ввод? А если оставить пару пикселов?
Только что проверил еще раз: [-3000, -3000] при нормально видимом окне не работает. Координаты в SetCursorPos проверил — там везде в районе [-3000 плюс смещение].
А фактически при любых отрицательных координатах клика я вижу, что генерируется клик на левый верхний угол приложения (где [0,0]) и открывает системное меню окна.


Вчера еще забыл упомянуть, в том коде:
static int PosX = 30;
static int PosY = PosX;
static UINT uFlags = 0;
SetWindowPos(hwnd, HWND_TOP, PosX, PosY, nSizeX, nSizeY, uFlags);

когда координаты входят в видимую область экрана, то клики проходят. Даже когда какой-нибудь [2500,20], который всё равно в зоне видимости (там у меня второй монитор), тоже ввод принимает.
А когда я рантайм меняю их на более отдалённые — ввод уже перестает работать.
И ЕМНИП, когда виден лишь кусочек окна в углу экрана — работает частично, только на размер этого видимого куска.


C>>ЗЫ. Нагуглил еще AttachThreadInput()

MA> Тут, по идее это лишнее, если IE создает окно связанное с твоим потоком. Если же со своим — тогда стоит покопать в этом направлении.
Создаю новый поток, и уже с этого нового потока линкуюсь к браузеру и т.д.:
    if (byCodeOption == 102) //  moved to a separate thread
    {
        void * hUpdateThread = (HANDLE)_beginthread(BrowserThread, 0, (void *)0);
        const DWORD dwCurrentThread = GetCurrentThreadId();
        if (webBrowser1)
        {
            HWND hWnBrwsr;
            webBrowser1->GetWindow(&hWnBrwsr);
            hUpdateThread = (void *)GetWindowThreadProcessId(hWnBrwsr, NULL);
        }
        AttachThreadInput(dwCurrentThread, (DWORD) hUpdateThread, TRUE);
    }
    else
        CreateBrowser();

Тут пока разбираюсь.

MA> PS: В дополнение ко всему IE наверняка еще и вызывает что-то на подобии GetKeyState для определения состояния клавиш-модификаторов, в том числе и мышиных (VK_LBUTTON). Он вполне может реагировать на внешние события как триггер обработки клавиатуры/мышки, ставить в очередь, и дочитывать реальное/текущее состояние для фильтрации событий совсем чуток погодя. Таким образом если вы пробрасываете настоящие нажатия — оно будет/должно работать, а если синтезируете события полностью программно, то не будет.

Намекните, пожалуйста, что за настоящие нажатия имеются в виду? На уровне драйвера мыши?

MA> PPS: Не меняет ли IE размер своего окна на 0х0 в случае, если окно прячется?

Надо бы попробовать в Spy++.
Re[5]: Кликнуть в скрытое окно
От: Mystic Artifact  
Дата: 06.08.19 21:26
Оценка:
Здравствуйте, Cannol, Вы писали:

MA>> PS: В дополнение ко всему IE наверняка еще и вызывает что-то на подобии GetKeyState для определения состояния клавиш-модификаторов, в том числе и мышиных (VK_LBUTTON). Он вполне может реагировать на внешние события как триггер обработки клавиатуры/мышки, ставить в очередь, и дочитывать реальное/текущее состояние для фильтрации событий совсем чуток погодя. Таким образом если вы пробрасываете настоящие нажатия — оно будет/должно работать, а если синтезируете события полностью программно, то не будет.

C>Намекните, пожалуйста, что за настоящие нажатия имеются в виду? На уровне драйвера мыши?
Имелось ввиду — нажатие на настоящей мышки. Если просто послать сообщение, то это состояние никак не изменится.
Вот из документации к GetKeyboardState:

An application can call this function to retrieve the current status of all the virtual keys. The status changes as a thread removes keyboard messages from its message queue. The status does not change as keyboard messages are posted to the thread's message queue, nor does it change as keyboard messages are posted to or retrieved from message queues of other threads. (Exception: Threads that are connected through AttachThreadInput share the same keyboard state.)

Я просто это сказал к слову, т.к. практически любая обработка событий клавиатуры/мышки сопряжена с этими или подобными вызовами.

Но это вообще не очень похоже на текущую проблему. Это бы скорее всего проявлялось, в том, что JS события не наполнены до конца, или там комбинации какие-нибудь не учитываются.

ADD: Ну да, в моём отрывке как раз сказано обратное , что состояние меняется с тем как сообщения удаляются из очереди сообщений. Это не совсем то, что я сказал... Но исходя из того, что лично видел я в коде одного другого браузера — они используются как попало и где попало, пока не примут какую-то внятную внутреннюю форму.

ADD: Я видать изначально перепутал с GetAsyncKeyState.
Отредактировано 06.08.2019 21:34 Mystic Artifact . Предыдущая версия . Еще …
Отредактировано 06.08.2019 21:33 Mystic Artifact . Предыдущая версия .
Re[5]: Кликнуть в скрытое окно
От: Mystic Artifact  
Дата: 06.08.19 22:08
Оценка: 3 (1) -1
Здравствуйте, Cannol, Вы писали:

C>Только что проверил еще раз: [-3000, -3000] при нормально видимом окне не работает. Координаты в SetCursorPos проверил — там везде в районе [-3000 плюс смещение].

C>А фактически при любых отрицательных координатах клика я вижу, что генерируется клик на левый верхний угол приложения (где [0,0]) и открывает системное меню окна.

SetCursorPos обрезает координаты к заданным через ClipCursor или видимой области рабочего стола.

Вообще, зачес вообще SetCursorPos нужен? Почему бы просто не посылать сообщения WM_MOUSEDOWN с нужными координатами? (Я мог чего-то упустить или забыть в треде).
Re[6]: Кликнуть в скрытое окно
От: Cannol  
Дата: 07.08.19 01:19
Оценка:
Здравствуйте, Mystic Artifact, Вы писали:
MA>SetCursorPos обрезает координаты к заданным через ClipCursor или видимой области рабочего стола.
Гм, я как-то упустил этот факт.

MA>Вообще, зачес вообще SetCursorPos нужен? Почему бы просто не посылать сообщения WM_MOUSEDOWN с нужными координатами? (Я мог чего-то упустить или забыть в треде).

Я не очень с WinAPI, прошу уточнить:
1. WM_MOUSEDOWN не нашёл в хидерах/документации, видимо, можно использовать WM_LBUTTONDOWN/WM_LBUTTONUP вместо этого?
2. В вышеприведённом моём примере есть варианты с использованием этих 2 сообщений:
...
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);
}


Т.е., после вызова SetCursorPos() я всё равно пробовал отправлять сообщения о нажатии вместе с координатами клика (pt.x, pt.y).
Так всё равно не работает. Или может, у меня тут где-то ошибка? Или по отдельности надо отправлять сообщение (отдельно перемещение и отдельно клик)?
Re[5]: Кликнуть в скрытое окно
От: Carc Россия https://vk.com/gosha_mazov
Дата: 07.08.19 04:18
Оценка: +1
C>Намекните, пожалуйста, что за настоящие нажатия имеются в виду? На уровне драйвера мыши?

Можно юзать SendInput — обычно работает. Но запросто ловится хуками, по флагу в хуковом параметре видно, внедренный ввод или нативный. Соответственно, таргетное приложение\компонент может такое нажатие просечь и предпринять что-нить…
Aml Pages Home
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.