Как остановить блокировку программы Windows во время перетаскивания окна или удержания кнопки меню?

Я новичок с для Win32, и я преследовал проблему (если ее вообще можно назвать проблемой) с Windows, блокирующей поток вашей программы во время события, когда пользователь захватывает строку заголовка окна и перемещает ее по экрану.

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

Я попытался добавить это к моему WindowProc

switch (uMsg)
{
    case WM_SYSCOMMAND:
        if (wParam == SC_CLOSE)
            PostQuitMessage(0);

        return 0;
    ...
    ...
    default:
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;

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

кроме того, я не знаю как вручную включить код, необходимый для перемещения окна, когда пользователь щелкает на заголовке и тащит мышь. Для начала я не знаю, какой uMsgи wParam's, чтобы справиться.

мой вопрос в том, как я могу запретить блокировку во время случая, когда пользователь нажимает кнопку выхода (или минимизировать / максимизировать кнопки), все еще обрабатывая случай, когда мышь нажата и отпущена над кнопкой, и как я могу разрешить пользователю перемещать / перетаскивать окно без блокировки программы (или какое сообщение отправляется при нажатии на строку заголовка без кнопки или меню)?

Я создаю окно с WS_SYSMENU | WS_MINIMIZEBOX.

Я все еще хочу, чтобы программа отвечала на команды минимизации, максимизации и выхода.

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

3 ответов


Почему Мое Приложение Замерзает?- Введение в петли сообщений & Threads

это явление не изолировано от какого-либо конкретного сообщения. Это фундаментальное свойство цикла сообщений Windows: когда одно сообщение обрабатывается, никакое другое сообщение не может быть обработано одновременно. Это не совсем реализовано таким образом, но вы можете думать об этом как о очереди, где ваше приложение вытаскивает сообщения из очереди для обработки в обратном порядке вставленный.

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

часто это означает делегирование обработки фоновому потоку. Вам все равно нужно будет обрабатывать все сообщения в основном потоке, а фоновые рабочие потоки должны вернуться к основному методу, когда они будут завершены. Все взаимодействие с GUI должно происходить в одном потоке, и это почти всегда основной поток в вашем приложении (именно поэтому его часто называют потоком UI).

(и чтобы ответить на возражение, высказанное в вашем вопросе, да,вы можете управлять несколькими потоками на одном процессоре. Вы не обязательно увидите какие-либо улучшения производительности, но это сделает пользовательский интерфейс больше отзывчивый. Логика здесь в том, что поток может делать только одну вещь за раз, но процессор может переключаться между потоками чрезвычайно быстро, эффективно имитируя выполнение более одной вещи за раз.)

более полезная информация доступна здесь, в этой статье MSDN:предотвращение зависаний в приложениях Windows

Особые Случаи: Циклы Обработки Модальных Событий

некоторые окна на окна модальные оперативный. Modal-это общее слово в вычислениях, которое в основном относится к блокировке пользователя в определенном режиме, где они не могут делать ничего другого, пока они не изменят (т. е. не выйдут из этого) режимы. Всякий раз, когда начинается модальная операция, запускается отдельный новый цикл обработки сообщений и происходит обработка сообщений здесь (вместо основного цикла сообщений) в течение всего режима. Общими примерами этих модальных операций являются перетаскивание, изменение размера окна и сообщение ящики.

рассматривая пример изменения размера окна, ваше окно получает WM_NCLBUTTONDOWN сообщение, которое вы передаете DefWindowProc для обработки по умолчанию. DefWindowProc выясняет, что пользователь намерен начать операцию перемещения или изменения размера, и ввел цикл сообщений перемещения/изменения размера, расположенный где-то глубоко в недрах собственного кода Windows. Таким образом, цикл сообщений вашего приложения больше не работает, потому что вы вошли в новый режим перемещения/размера.

Windows выполняет это цикл перемещения/изменения размера, пока пользователь интерактивно перемещает / изменяет размер окна. Он делает это так, что он может перехватывать сообщения мыши и обрабатывать их соответствующим образом. Когда операция перемещения/калибровки завершается (например, когда пользователь отпускает кнопку мыши или нажимает Esc key), control вернется к вашему коду приложения.

стоит отметить, что вы are уведомление о том, что это изменение режима произошло через WM_ENTERSIZEMOVE сообщение, соответствующую WM_EXITSIZEMOVE указывает, что модальный цикл обработки событий вышел. Что позволяет создать таймер, который будет продолжать генерировать WM_TIMER сообщения, которые ваше приложение может обрабатывать. Фактические детали того, как это реализовано, относительно неважны, но быстрое объяснение заключается в том, что DefWindowProc продолжает направлять WM_TIMER сообщения для вашего приложения внутри собственного цикла обработки модальных событий. Используйте SetTimer функция создать таймер в ответ на WM_ENTERSIZEMOVE сообщение, а KillTimer функции чтобы уничтожить его в ответ на WM_EXITSIZEMOVE сообщение.

я только указываю на это для полноты. В большинстве приложений Windows, которые я написал, мне никогда не приходилось это делать.

Итак, Что Не Так С Моим Кодом?

помимо всего этого, поведение, которое вы описываете в вопросе, необычно. При создании нового пустого Win32 приложение используя шаблон Visual Studio, я сомневаюсь, что вы сможете реплицировать это поведение. Не видя остальной части вашей процедуры окна, я не могу сказать, блокируете ли вы какие-либо сообщения (как описано выше), но часть I можете см. В вопросе неправильно. Вы должны всегда вызов DefWindowProc для сообщений, которые вы явно не самостоятельно.

в этом случае, вы можете быть обмануты, думая, что ты делаешь это, но WM_SYSCOMMAND может иметь много различных значений для wParam. Вы только обрабатывать один из них SC_CLOSE. Все остальные просто игнорируются, потому что вы return 0. Это включает в себя все функции перемещения и изменения размера окна (например,SC_MOVE, SC_SIZE, SC_MINIMIZE, SC_RESTORE, SC_MAXIMIZE, etc. так далее.).

и на самом деле нет никаких оснований для обработки WM_SYSCOMMAND сам, просто дайте DefWindowProc позаботьтесь об этом для вас. Единственный раз, когда вы нужно справиться WM_SYSCOMMAND - это когда вы добавили пользовательские элементы в меню окна, и даже тогда вы должны передать все команды, которые вы не распознаете на DefWindowProc.

основная процедура окна должна выглядеть следующим образом:

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch(uMsg)
    {
        case WM_CLOSE:
            DestroyWindow(hWnd);
            return 0;

        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
    }
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

это и возможно, что ваш цикл сообщений неверен. Идиоматический цикл сообщений Win32 (расположен в нижней части вашего WinMain функция) выглядит так:

BOOL ret;
MSG msg;
while ((ret = GetMessage(&msg, nullptr, 0, 0)) != 0)
{
    if (ret != -1)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    else
    {
        // An error occurred! Handle it and bail out.
        MessageBox(nullptr, L"Unexpected Error", nullptr, MB_OK | MB_ICONERROR);
        return 1;
    }
}

вам не нужны крюки любого добрый. Документация MSDN по ним очень хороша, но вы правы: они сложны. Держитесь подальше, пока не получите лучшее представление о модели программирования Win32. Это действительно редкий случай, когда вам нужна функциональность, предоставляемую крючком.


Если многопоточность может исправить это, то это интересно, но мне интересно если я смогу заставить его работать на одноядерных процессорах. И я прочитал о крючках, но страница MSDN по-прежнему трудно интерпретировать.

вы can используйте несколько потоков на одноядерном процессоре. Производительность была бы лучше в многоядерных системах, но это не должно мешать вам писать многопоточные приложения. В любом случае, давай.


путем печати всех сообщений, отправленных в WindowProc появляется WM_NCLBUTTONDOWN отправляется последним перед блоком. Вы можете проверить местоположение мыши после этого события, но это кажется неудобным способом решения простой проблемы.