Уведомления о смене состояния
Feb 21, 2012 · CommentsПрограммирование
А давайте разберем антипаттерн - реализацию механизма уведомления о смене состояния чего-либо с помощью очереди сообщений. Для примера возьмем Session Change Notifications в Windows. Пример, как очевидно, совершенно произвольный и не имеющий никакого отношения к тому, что я их сегодня полдня ковырял. :-)
По идее, все просто. Хотим отслеживать состояние сессий - регистрируем окошко или обработчик, куда будут посылаться уведомления о смене состояния сессий и все дела. Если присмотреться, то все не так просто. Как, к примеру, синхронизировать получение начального состояния всех сессий и установку обработчика? Оба действия не атомарны и, следовательно, можно либо пропустить некоторые уведомления, либо получить уведомления, уже учтенные начальным состоянием сессий. Или другая проблема: состояние сессии, вычисленное по полученным уведомлениям может не соответствовать актуальному состоянию сессии. Т.е. получаем WTS_SESSION_LOGON, а сессия уже давно завершилась и даже соответствующее уведомление было послано, просто сообщение все еще сидит в очереди оконных сообщений необработанное. Именно с сессиями, такое вряд ли конечно может случиться на практике. Слишком они тяжеловесны. Но теоретически такое возможно. Хотя стресс тесты и не такое, бывает, находят.
Возникает вопрос, а что же делать? Одно из возможных решений - разделить доставку уведомления о смене состояния и получение текущего состояния. Получение уведомления будет означать, что состояние, возможно, поменялось. Обработчик уведомления должен будет опросить текущее состояние и сравнить его с сохраненной копией. Опять же, в моем случае, все что мне нужно было отследить - это номер сессии, присоединенной в физической консоли. Для этого есть отдельная, и очень эффективная функция - WTSGetActiveConsoleSessionId(). Вот её полный листинг:
0:000> uf kernel32!WTSGetActiveConsoleSessionId
kernel32!WTSGetActiveConsoleSessionId:
75133f49 a1d802fe7f mov eax,dword ptr [SharedUserData+0x2d8 (7ffe02d8)]
75133f4e c3 ret
:-)
При таком подходе представления обработчика о текущем состоянии гораздо ближе к реальности, однако некоторые переходы между состояниями могут быть пропущены. В прочем, для асинхронных уведомлений это возможно в любом случае.