Как остановить 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справиться.

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

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

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

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

12

Решение

Почему мое приложение зависает? Введение в циклы сообщений & Потоки

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

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

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

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

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

Особые случаи: модальные циклы обработки событий

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

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

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

Стоит отметить, что вы являются уведомил, что это изменение режима произошло через 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и тд и тп).

И на самом деле нет веских причин для обработки 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. Это действительно редкий случай, когда вам нужна функциональность, предоставляемая хуком.

23

Другие решения

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

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

2

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

1
По вопросам рекламы [email protected]