воскресенье, 26 декабря 2010 г.

Что делает LockWindowUpdate?

Это перевод What does LockWindowUpdate do? Автор: Реймонд Чен.

Бедная LockWindowUpdate, которую никто не понимает.

Это первый пост в серии про LockWindowUpdate, то, что она делает, то, для чего она предназначена, и (возможно, самое важное) то, для чего она не предназначена.

Что LockWindowUpdate делает - это очень просто. Когда окно заблокировано, все попытки нарисовать что-то на нём или дочерних окнах проваливаются. Вместо рисования оконный менеджер запоминает, в каких частях окна рисовали, и обновляет эти области через отправку сообщения WM_PAINT, когда окно разблокируется - восстанавливая этим синхронизацию между видимым на экране и логическим состоянием.

Это поведение "запоминай, что приложение пыталось нарисовать, пока условие X имеет силу, и перерисуй это, когда условие X не выполняется" вы уже видели в другом обличье: CS_SAVEBITS. В этом смысле LockWindowUpdate делает ту же работу учёта, которая произошла если бы вы закрыли целевое окно другим с установленным стилем CS_SAVEBITS, но только она не сохраняет никаких пикселей.

Документация совершенно чётко указывает, что только одно окно (в расчёте на рабочий стол, конечно же) может быть заблокировано в любой момент времени, но это же следует и из прототипа функции. Если два окна могли бы быть заблокированы одновременно, то вы не смогли бы однозначно использовать LockWindowUpdate. Что должно произойти при этом?
LockWindowUpdate(hwndA); // блокирует окно A
LockWindowUpdate(hwndB); // также блокирует окно B
LockWindowUpdate(0); // ???
Что сделает этот третий вызов LockWindowUpdate? Разблокирует все окна? Или только окно A? Или только B? Не важно что вы выберите - любой ответ сделает использование следующего кода ненадёжным:
procedure BeginOperationA;
begin
  LockWindowUpdate(hwndA);
  ...
end;

procedure EndOperationA;
begin
  ...
  LockWindowUpdate(0);
end;

procedure BeginOperationB;
begin
  LockWindowUpdate(hwndB);
  ...
end;

procedure EndOperationB;
begin
  ...
  LockWindowUpdate(0);
end;
Представьте что функции BeginOperation начинают какие-то операции, которые запускаются асинхронно. К примеру, пусть операция A - это рисование во время drag/drop, так что она запускается, когда зажимают мышь, а заканчивается, когда мышь отпускают.

Теперь предположим, что операция B заканчивается пока происходит drag/drop. Тогда EndOperationB должна вызвать LockWindowUpdate(0). Если вы предполоджили что третий вызов должен разблокировать все окна, то вы только что сломали операцию A, которая ожидает, что окно hwndA будет заблокировано. Аналогично, если вы решили, что должно разблокироваться только окно hwndA, то снова операция A обречена, но также и операция B (поскольку тогда hwndB останется заблокированным, даже хотя операция B завершена). С другой стороны, если вы решили, что должно разблокироваться окно hwndB, то рассмотрите случай, когда первой заканчивается операция A.

Если бы LockWindowUpdate могла блокировать больше одного окна, то у неё был бы иной прототип, позволяющий узнать, какая операция завершена. Есть много способов это сделать. К примеру, у неё мог быть дополнительный параметр или обратная функция:
// Метод A - новый параметр
// fLock = True для блокировки, False для разблокировки
function LockWindowUpdate(wnd: HWND; fLock: BOOL): BOOL;

// Метод B - отдельная функция
function LockWindowUpdate(wnd: HWND): BOOL;
function UnlockWindowUpdate(wnd: BOOL): BOOL;
Но ни один из этих случаев не имеет места. Функция LockWindowUpdate блокирует только одно окно. И причина для такого дизайна станет ясна, когда мы разберём для чего предназначена LockWindowUpdate.

1 комментарий:

  1. Большое спасибо за прояснение. Я действительно был уверен, что LockWindowUpdate блокирует всё подряд, что в нее передаем, а потом одним махом LockWindowUpdate(0) все перерисовывается, пока не начал разбираться, почему происходит не так :)

    ОтветитьУдалить

Можно использовать некоторые HTML-теги, например:

<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>

Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку.

Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.

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

Примечание. Отправлять комментарии могут только участники этого блога.