#c #windows #winapi
Вопрос:
Я пишу очень простую программу для закрепления мыши в указанном окне. Он запускается из системного трея без видимого окна. Поскольку будет несколько экземпляров одного и того же окна, он использует EnumWindows()
итерацию для каждого окна верхнего уровня и сравнивает их с hwnd GetForegroundWindow()
. Когда значение true, он запускает стандартный ClipCursor()
код. ClipCursor()
возвращает TRUE
, и я утверждал, что RECT
набор by GetClipCursor()
точно такой же, как RECT
и переданный ClipCursor()
. Тем не менее, курсор может свободно перемещаться в любом месте экрана.
Я проверил, что значения в окне RECT
являются точными значениями окна, я скомпилировал программу в режиме выпуска и запустил ее с правами администратора, по-прежнему ничего. Приведенный ниже код-это именно то, что выполняется после того, как мы нашли HWND
GetForegroundWindow()
:
// Get the window client area.
GetClientRect(hwnd, amp;rc);
// Convert the client area to screen coordinates.
POINT pt = { rc.left, rc.top };
POINT pt2 = { rc.right, rc.bottom };
ClientToScreen(hwnd, amp;pt);
ClientToScreen(hwnd, amp;pt2);
SetRect(amp;rc, pt.x, pt.y, pt2.x, pt2.y);
clipped = true;
ClipCursor(amp;rc);
RECT rect;
GetClipCursor(amp;rect);
assert(rect.bottom == rc.bottom);
assert(rect.left == rc.left);
assert(rect.right == rc.right);
assert(rect.top == rc.top);
Я удалил много проверок, потому что они начинали раздражать (я использовал MessageBox()
«ы»), но этот код, безусловно, запускается, когда это необходимо. Курсор просто не обрезается, и я не могу понять, почему.
Комментарии:
1. Является ли окно, которое вы вырезаете, тем же самым процессом? Я не уверен, но я не удивлюсь, если вы не сможете обрезать курсор в другом процессе.
2. Курсор является общим ресурсом, поэтому любой процесс может обрезать его (вы не привязываете его к процессу, а скорее к прямоугольнику на экране).
Ответ №1:
Поскольку курсор является общим ресурсом, ваша попытка обрезать его перезаписывается любым другим пользователем, который звонит ClipCursor
, чтобы снять курсор. И множество операций автоматически отсоединяют курсор (например, любое изменение фокуса). Изменение клипа курсора в фоновом окне считается плохой формой для изменения клипа курсора.
Ответ №2:
Ну, мне потребовалось всего несколько дней, чтобы выяснить, что, несмотря на общий характер ресурса курсора, если процесс обрезает курсор при определенных обстоятельствах, о которых я еще не до конца знаю (может быть, процесс должен быть приложением переднего плана? или что-то в этом роде… Я не совсем уверен.) затем ОС автоматически переводит курсор обратно в режим полного отсечения (или как бы вы это ни называли).
В любом случае, исправление состоит в том, чтобы выполнить низкоуровневый хук мыши и вызвать оттуда clipcursor. Вот краткое доказательство концептуального кода (протестировано и работает, хотя я удалил ненужные вещи, такие как создание окна или настройка системного блока и т. Д.):
// Some variables we'll use
bool clipped = false; // Do we need to clip the mouse?
RECT rc; // The clip rect
HHOOK hMouseHook; // Low level mouse hook
// Low level mouse hook callback function
__declspec(dllexport) LRESULT CALLBACK MouseEvent(int nCode, WPARAM wParam, LPARAM lParam)
{
// This part should be rewritten to make it not be a CPU-hog
// But works as a proof of concept
if ( clipped )
ClipCursor(amp;rc);
return CallNextHookEx(hMouseHook, nCode, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
// .... Blah blah blah ....
// Low level mouse hook
hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, (HOOKPROC)MouseEvent, hInstance, 0);
// Only included to show that you set the hook before this,
// And unhook after this.
while(GetMessage(amp;msg, NULL, 0, 0) > 0)
{
TranslateMessage(amp;msg);
DispatchMessage(amp;msg);
}
// Unhook the mouse
UnhookWindowsHookEx(hMouseHook);
return msg.wParam;
}