Что такое насос сообщений?

104

В этой ветке (опубликованной около года назад) обсуждаются проблемы, которые могут возникнуть при запуске Word в неинтерактивном сеансе. Приведенный здесь (довольно сильный) совет - не делать этого. В одном сообщении говорится: «Все API-интерфейсы Office предполагают, что вы запускаете Office в интерактивном сеансе на рабочем столе, с монитором, клавиатурой и мышью и, что наиболее важно, с насосом сообщений». Я не уверен, что это такое. (Я программировал на C # всего около года; другой мой опыт программирования в основном связан с ColdFusion.)

Обновить:

Моя программа просматривает большое количество файлов RTF, чтобы извлечь два фрагмента информации, которые используются для построения номера медицинского отчета. Вместо того, чтобы пытаться выяснить, как работают инструкции по форматированию в RTF, я решил просто открыть их в Word и вытащить оттуда текст (без фактического запуска GUI). Иногда программа зависала в середине обработки одного файла и оставляла поток Word открытым, прикрепленный к этому документу (мне все еще нужно выяснить, как закрыть этот файл). Когда я перезапустил программу, я, конечно, получил уведомление о том, что этот файл использует поток, и хочу ли я открыть копию, доступную только для чтения? Когда я сказал «Да», графический интерфейс Word внезапно появился из ниоткуда и начал обрабатывать файлы. Мне было интересно, почему это произошло;

Мэтт Гаттинг
источник
3
Почему это помечено как win32? - Система сообщений была в Windows V1 (которая, насколько я помню, была 8-битной.)
Хоган

Ответы:

187

Цикл сообщений - это небольшой фрагмент кода, который существует в любой собственной программе Windows. Примерно это выглядит так:

MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{ 
   TranslateMessage(&msg); 
   DispatchMessage(&msg); 
} 

Win32 API GetMessage () получает сообщение из Windows. Ваша программа обычно проводит там 99,9% времени, ожидая, пока Windows сообщит ей о том, что произошло что-то интересное. TranslateMessage () - это вспомогательная функция, которая переводит сообщения клавиатуры. DispatchMessage () гарантирует, что оконная процедура вызывается с сообщением.

Каждая программа .NET с графическим интерфейсом пользователя имеет цикл сообщений, который запускается Application.Run ().

Значение цикла сообщений для Office связано с COM. Программы Office - это программы с поддержкой COM, вот как работают классы Microsoft.Office.Interop. COM заботится о потоковой передаче от имени компонентного класса COM, он гарантирует, что вызовы, выполняемые в интерфейсе COM, всегда выполняются из правильного потока. У большинства классов COM есть раздел реестра в реестре, который объявляет их ThreadingModel, но наиболее распространенные (включая Office) используют «Apartment». Это означает, что единственный безопасный способ вызвать метод интерфейса - это сделать вызов из того же потока, который создал объект класса. Или, другими словами, большинство COM-классов не являются потокобезопасными.

Каждый поток с поддержкой COM принадлежит COM-апартаменту. Есть два типа: однопоточные (STA) и многопоточные (MTA). В потоке STA должен быть создан отдельный поток COM-класса. Вы можете увидеть это еще в программах .NET, точка входа потока пользовательского интерфейса программы Windows Forms или WPF имеет атрибут [STAThread]. Модель квартиры для других потоков задается методом Thread.SetApartmentState ().

Большие части сантехники Windows не будут работать правильно, если поток пользовательского интерфейса не является STA. В частности, Drag + Drop, буфер обмена, диалоговые окна Windows, такие как OpenFileDialog, элементы управления, такие как WebBrowser, приложения автоматизации пользовательского интерфейса, такие как программы чтения с экрана. И многие COM-серверы, такие как Office.

Жесткое требование к потоку STA состоит в том, что он никогда не должен блокироваться и должен перекачивать цикл сообщений. Цикл сообщений важен, потому что это то, что COM использует для маршалинга вызова метода интерфейса из одного потока в другой. Хотя .NET упрощает маршалинг вызовов (например, Control.BeginInvoke или Dispatcher.BeginInvoke), на самом деле это очень сложная задача. Поток, выполняющий вызов, должен находиться в хорошо известном состоянии. Вы не можете просто произвольно прервать поток и заставить его выполнить вызов метода, что могло бы вызвать ужасные проблемы с повторным входом. Поток должен быть «простаивающим», не занятым выполнением какого-либо кода, изменяющего состояние программы.

Возможно, вы видите, к чему это ведет: да, когда программа выполняет цикл сообщений, она простаивает. Фактический маршалинг происходит через скрытое окно, которое создает COM, он использует PostMessage, чтобы оконная процедура этого окна выполняла код. В ветке STA. Цикл сообщений обеспечивает выполнение этого кода.

Ганс Пассан
источник
Очень красивый и подробный ответ. Просто добавлю - существует также специальная STA, называемая основной STA, которая является первой созданной STA. Который в идеале должен быть создан вашим потоком пользовательского интерфейса. В основной STA создаются компоненты с потоковой моделью = none. Если ваш основной STA не тот, который создан вашим потоком пользовательского интерфейса, вы можете столкнуться с интересными проблемами при использовании старых элементов управления ActiveX, у которых нет потоковой модели.
quixver
12

«Перекачивание сообщений» - это основная часть любой программы Windows, которая отвечает за отправку оконных сообщений различным частям приложения. Это ядро ​​программирования пользовательского интерфейса Win32. Из-за его повсеместного распространения многие приложения используют насос сообщений для передачи сообщений между различными модулями, поэтому приложения Office не работают, если они запускаются без какого-либо пользовательского интерфейса.

В Википедии есть базовое описание .

JSB ձոգչ
источник
Я считаю, что невозможно написать приложение для Windows без цикла сообщений, поэтому все приложения используют насос сообщений.
Hogan
2
Вы также можете писать простые приложения с графическим интерфейсом без такового - например, вы можете открывать всплывающие окна сообщений без вашего собственного приложения, имеющего цикл сообщений в вашем приложении.
Если вы создаете диалог через DialogBox или DialogBox косвенно - вам не нужен цикл сообщений, вам просто нужно предоставить функцию (dlgproc), которая будет вызываться окнами. (и окно сообщения только простой диалог)
quixver
6

Джон говорит о том, как система Windows (и другие оконные системы - X Window , оригинальная Mac OS ...) реализуют асинхронные пользовательские интерфейсы с использованием событий через систему сообщений.

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

В статье Википедии Цикл сообщений в Microsoft Windows показан пример кода базовой программы Windows, и, как вы можете видеть, на самом базовом уровне программа Windows - это просто «насос сообщений».

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

Хоган
источник
6

В COM насос сообщений сериализует и де-сериализует сообщения, отправляемые между квартирами. Апартамент - это мини-процесс, в котором могут запускаться COM-компоненты. Квартиры бывают однопоточными и свободнопоточными. Однопоточные апартаменты - это в основном устаревшая система для приложений COM-компонентов, которые не поддерживают многопоточность. Обычно они использовались с Visual BASIC (поскольку он не поддерживал многопоточный код) и устаревшими приложениями.

Я предполагаю, что требование перекачки сообщений для Word проистекает из того, что COM API или части приложения не являются потокобезопасными. Имейте в виду, что модели потоковой передачи и сборки мусора .NET не очень хорошо работают с COM из коробки. COM имеет очень упрощенный механизм сборки мусора и потоковую модель, которая требует, чтобы вы все делали так же, как COM. Использование стандартных Office PIA по- прежнему требует, чтобы вы явно закрывали ссылки на COM-объекты, поэтому вам нужно отслеживать каждый созданный дескриптор COM. Если вы не будете осторожны, PIA также создадут материал за кулисами.

Интеграция .NET-COM - это отдельная тема, и на эту тему даже есть книги. Даже использование COM API для Office из интерактивного настольного приложения требует, чтобы вы перепрыгнули через обручи и убедились, что ссылки явно освобождены.

Можно предположить, что Office небезопасен для потоков, поэтому вам потребуется отдельный экземпляр Word, Excel или других приложений Office для каждого потока. Вам придется нести начальные накладные расходы или поддерживать пул потоков. Пул потоков необходимо будет тщательно протестировать, чтобы убедиться, что все ссылки COM были выпущены правильно. Даже запуск и остановка экземпляров требует от вас правильного освобождения всех ссылок. Неспособность расставить точки над i и перечеркнуть здесь t приведет к утечке большого количества мертвых объектов COM и даже целых запущенных экземпляров Word.

ОбеспокоенныйTunbridgeWells
источник
1
В вашем ответе есть несколько неточностей. Существует 3 типа квартир - STA (однопоточная), MTA (многопоточная) и NTA (нейтральная резьба). Свободный многопоточный используется для описания компонента, который объединяет свободный многопоточный маршаллер, не существует такой вещи, как бесплатное многопоточное приложение. COM использует сообщения для связи с STA. Для компонентов, находящихся в MTA (или которые объединяют свободный многопоточный маршаллер), петли сообщений не требуются. AFAIK - lpc используется для маршалинга данных из вызывающего потока в поток из пула потоков RPC, который затем фактически вызывает метод.
quixver
0

Я думаю, что это обсуждение на канале 9 имеет хорошее краткое объяснение:

Этот процесс оконной коммуникации стал возможным благодаря так называемой Windows Message Pump. Думайте о Message Pump как о сущности, которая обеспечивает взаимодействие между окнами приложений и рабочим столом.

Ричард Ив
источник
2
вау ... это ужасная и вводящая в заблуждение цитата. («Сущность»? Эээ .. нет.)
Хоган
4
entity - object: что-то, что существует или воспринимается как единый отдельный объект encarta.msn.com/dictionary_1861608661/entity.html
Мэтью Уайтт,