Как предотвратить ProcessMessages в Delphi

Команда Application.ProcessMessages хорошо известна, и я использую ее в длинных процессах, чтобы моя программа не нагружала компьютер.

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

Есть ли способ:

  1. Запретить Application.ProcessMessages, пока моя процедура не будет завершена, или

  2. Перехватывать все сообщения, сгенерированные во время моей процедуры, и не выпускать их до конца процедуры.


person lkessler    schedule 09.08.2009    source источник
comment
Перечитывая ваш вопрос, я действительно не понимаю его - если у вас есть быстрая процедура выполнения и вы не хотите, чтобы Application.ProcessMessages обрабатывал любые нежелательные сообщения, зачем вы вообще вызываете Application.ProcessMessages в этой процедуре?   -  person mghie    schedule 10.08.2009
comment
mghie: Я не вызываю это явно, но я добавляю в очередь некоторые события (например, положение полосы прокрутки), которые будут выполняться, если сообщения будут обработаны. Мне нужно просто убедиться, что во время этого кода никакие ожидающие события не выполняются.   -  person lkessler    schedule 10.08.2009
comment
Но в том-то и дело: если вы находитесь внутри своей процедуры, если вы явно не вызовете Application.ProcessMessages, никакие события не будут обрабатываться. Они будут добавлены в очередь и обработаны позже, но это все. Как будут обрабатываться сообщения, если вы этого не хотите?   -  person mghie    schedule 11.08.2009


Ответы (4)


Разрешение ProcessMessages продолжать работу, даже если оно отправляет сообщения, которые вам не нужны, не должно рассматриваться как проблема. С небольшим рефакторингом кода вы можете переместить метод буферизации в отдельный поток и перейти оттуда.

Если вы пытаетесь скопировать «визуальное содержимое» элемента управления в файл,

  • посмотрите на сообщение WM_PRINT(xxx), которое позволяет дочерним элементам управления рисовать себя в растровые изображения
  • попробуйте вызов метода LockWindowUpdate Win32 API, который отключит все сообщения рисования для этого элемента управления.
  • переопределите метод WndProc/DefaultWndProc в вашем управляющем классе или даже в родительском классе, если вам нужно, и просто верните "true" для каждого отправленного сообщения
  • переопределить определенные методы управления (такие как «перемещенная полоса прокрутки», «OnPaint», «OnPaintBackground» и т. д.) в классе управления или даже родительском и просто ничего не делать, если ваша буферизация выполняется

Переопределение WndProc или DefaultWndProc и просто возврат значения true для каждого сообщения по существу "отключает" ProcessMessages но делать это таким образом небезопасно, поскольку для правильной работы элементу управления может потребоваться обработать одно или несколько сообщений.

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

person Mike J    schedule 09.08.2009
comment
LockWindowUpdate кажется тем, что я искал. И другие ваши идеи тоже хороши. Спасибо. - person lkessler; 10.08.2009
comment
По словам Рэймонда Чена, никогда не следует использовать API LockWindowUpdate(): blogs.msdn.com/oldnewthing/archive/2007/02/23/1747713.aspx По крайней мере, его не следует использовать для того, для чего вы хотите: blogs.msdn.com/oldnewthing/archive/2007/02/22/1742084.aspx - person mghie; 10.08.2009
comment
Хм. Спасибо, мги. Ваше предупреждение будет услышано. Но Майк все же дал мне много информации о процессинге, чтобы вести меня в правильном направлении. - person lkessler; 10.08.2009
comment
@mghie: никогда не следует использовать LockWindowsUpdate. Правда - я тоже жду чего-то, что работает. Использование SetWindowUpdate ведет себя иначе. Я действительно пытаюсь сказать нет..... Брайан. - person Brian Frost; 17.05.2010
comment
@Brian: тоже работает - как так, если LockWindowsUpdate() на самом деле не работает? Что может быть хуже? Или, что более важно, что вы пытаетесь сделать, что LockWindowsUpdate() помогает вам делать, а альтернативы - нет? Какие проблемы у вас есть с SetWindowUpdate() в частности? - person mghie; 18.05.2010
comment
@mghie: Ну, это странно. Я начинаю использовать пару процедур блокировки и разблокировки (ниже), которые, как я считаю, являются рекомендуемым методом, но иногда я получаю элементы управления, которые не перекрашиваются, не выравниваются или другие артефакты, которые, к сожалению, всегда отсутствуют, когда я затем пытаюсь использовать LockWindowUpdate. Может я еще что-то не так делаю....? Блокировка: SendMessage(Hnd, WM_SETREDRAW, 0, 0); Разблокировать: SendMessage(Hnd, WM_SETREDRAW, 1, 0); Флаги: = RDW_FRAME + RDW_INVALIDATE + RDW_ALLCHILDREN + RDW_NOINTERNALPAINT; Если UpdateNow, то Inc(Flags, RDW_UPDATENOW); RedrawWindow(Hnd, ноль, 0, флаги); - person Brian Frost; 19.05.2010
comment
@Brian: не знаю, у меня никогда не было проблем с WM_SETREDRAW. Обычно за ним следует либо только Invalidate(), либо Invalidate() и Update() (или Repaint(), что делает и то, и другое). О проблемах с выравниванием можно позаботиться, позвонив по номеру Realign(). Комбинация из них до сих пор всегда работала для меня... - person mghie; 19.05.2010

Перехватывать все сообщения, сгенерированные во время моей процедуры, и не выпускать их до конца процедуры.

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

Вы можете просматривать (перехватывать) любые сообщения, используя Перехватчики Win32.
В частности, используйте SetWindowsHookEx с WH_CALLWNDPROC в качестве значения idHook.
Затем вы можете записать их в список /queue и отправьте их повторно, когда захотите.

person Nick Dandoulakis    schedule 09.08.2009
comment
Думаю, это тоже сработает. Немного больше работы, чем идеи Майка, но это даст мне полный контроль. Спасибо. - person lkessler; 10.08.2009

Еще в Windows 2 я узнал, что сообщения Windows будут появляться в то время, когда вы их не ожидаете. Любая часть библиотеки может вызвать обработку сообщений вашим приложением. Вместо того, чтобы сдерживать волну, сделайте свой код устойчивым к ситуации. Это может быть как простое использование пары BeginUpdate/EndUpdate, так и более сложное (использование временного и окончательное обновление в конце).

person mj2008    schedule 10.08.2009
comment
Это то, что я ищу: пара BeginUpdate/EndUpdate, которая предотвратит все события Windows, пока работа не будет выполнена. Но я считаю, что они работают только для предотвращения перекраски. - person lkessler; 10.08.2009

На педантическом уровне способ «предотвращения» Application.ProcessMessages заключается в том, чтобы не вызывать какой-либо код, который

  1. показывает модальный диалог
  2. вызывает SendMessage
  3. запускает собственный локальный цикл сообщений
  4. вызывает Application.ProcessMessages (который является локальным циклом сообщений)

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

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

person dthorpe    schedule 08.06.2010