четверг, 4 марта 2010 г.

Почему SystemParametersInfo повисает, когда я передаю флаг SPIF_SENDCHANGE?

Это перевод Why does SystemParametersInfo hang when I pass the SPIF_SENDCHANGE flag? Автор: Реймонд Чен.

Если вы передадите флаг SPIF_SENDCHANGE в функцию SystemParametersInfo, она сделает широковещательную отправку (broadcast) сообщения WM_SETTINGCHANGE с wParam равному коду, что вы указали.

Например, если вы вызовете:
SystemParametersInfo(SPI_SETDOUBLECLICKTIME, 500, 0, SPIF_UPDATEINIFILE or SPIF_SENDCHANGE);
то система разошлёт сообщение:
SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETDOUBLECLICKTIME, 0);
И если у нас есть где-то окно, которое не отвечает на сообщения (зависло), то эта рассылка тоже подвиснет до того момента, как это окно не проснётся и не обработает это сообщение, либо же пока зависшее приложение не убьют.

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

Во-первых, вы можете вызвать SystemParametersInfo в фоновом рабочем потоке. Тогда заблокированным будет ваш рабочий поток вместо вашего UI-потока.

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

Вы также можете вызвать SystemParametersInfo без указания флага SPIF_SENDCHANGE, а затем вручную разослать уведомление через SendMessageTimeout:
var
dwResult: DWORD;
...
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETDOUBLECLICKTIME, 0, SMTO_ABORTIFHUNG or SMTO_NOTIMEOUTIFNOTHUNG, 5000, dwResult);
Это означает, что зависшие окна не получат уведомление о смене параметров. Это приемлемо, если вы изменяете маловажную настройку, так что пропуск уведомления программой не является такой уж большой проблемой. Другими словами, когда зависшее приложение наконец-то просыпается, оно не знает, что настройки были изменены, потому что оно проспало уведомление.

Вы можете скомбинировать эти два способа: использовать фоновый поток и отправлять сообщение вручную с таймаутом.

Вероятно, наилучшим вариантом будет использование функции SendNotifyMessage. Как мы узнали ранее, функция SendNotifyMessage похожа на SendMessage, но только она не ждёт ответа. Это позволит вашей программе продолжить работу, без влияния подвисших программ.
SendNotifyMessage(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETDOUBLECLICKTIME, 0);
Однако недостатком будет являться то, что вы теперь не знаете, когда все окна наконец-то получат и обработают уведомление. Всё что вы знаете - что когда-нибудь они это узнают. Обычно вас не волнует этот аспект рассылки, так что отсутствие этой информации при таком способе не является критичным.

Комментариев нет:

Отправить комментарий

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

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

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

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

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