ClipCursor преуспевает, но эффективно ничего не делает

#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;
}