Здравствуйте, Aniskin, Вы писали:
A>из-за незнания предметной области
Попробую прояснить в меру своего понимания.
Для STA-объекта гарантировано, что все вызовы методов данного объекта будут из того же потока, в котором он создан. Если его замаршаллили в другой поток или другой процесс, то там прокси, а в оригинальном STA-потоке есть невидимое окно COM, которое принимает SendMessage вызовы.
То есть, ожидание через CoWaitForMultipleHandles отличается от WaitForMultipleObjectsEx тем, что помимо указанных объектов ждёт ещё сообщения, и диспетчерезирует их. Но не все сообщения! Потому что было бы неожиданно во время COM вызова из обработчика Button1Click получить вызов Button2Click, либо даже повторный вызов Button1Click. Обрабатываются специфические сообщения для того самого секретного COM окна, плюс, возможно, какие-то служебные, вроде WM_COPYGLOBALDATA.
CoWaitForMultipleHandles могла бы быть сделана через MsgWaitForMultipleObjectsEx, и скорее всего, как-то так и сделана. Ну а MsgWaitForMultipleObjectsEx реализована через реализацию WaitForMultipleObjectsEx, в которой к массиву объектов подложили ещё объект, соотвествующий очереди сообщений.
Флаги CoWaitForMultipleHandles. Когда-то были только COWAIT_WAITALL и COWAIT_ALERTABLE, это по сути соотвествующие параметры WaitForMultipleObjectsEx. Смысл остальных трёх понятен из их описания, если знать про MsgWaitForMultipleObjectsEx внутри и невидимое COM окно. Обращаю внимание, что COWAIT_ALERTABLE и COWAIT_INPUTAVAILABLE могут приводить к возврату из ожидания при выполнении соотвествующих флагам условий, в этом случае обычно требуется выполнять соотвествующие действия, и повторно вызывать CoWaitForMultipleHandles. А флаг COWAIT_WAITALL вообще
ни к чему хорошему не приведет.
Так как входящий STA COM вызов — это входящее SendMessage сообщение, его можно принять не только CoWaitForMultipleHandles, но и обычным циклом сообщений. В большинстве приложений обычно так и происходит. Из того же цикла сообщений, из которого вызовется Button1Click, обработается и входящий STA COM вызов.
Всякие MessageBox и OpenDialog тоже обработают входящий COM вызов (Button2Click из них не придёт, но не потому, что они обрабатывают не все сообщения, сообщения они как раз обрабатывают все, а потому, что механизм модальных диалогов дизейблит owner-окно).
Исходящий COM-вызов, если он сделан на прокси-объекте (замаршалленом указателе), тоже может диспечеризировать входящие COM вызовы, иначе как придут коллбэки. При этом, все ли COM вызовы он диспечеризирует, и какие сообщения при этом проходят — не знаю.
Это всё было про STA. В MTA проще. Нет никакого скрытого окна, вызовы, сделанные в другом MTA потоке придут из того же MTA потока. В случае маршаллинга, входящие вызовы будут приходить из служебных COM-потоков, эти потоки тоже будут принадлежать MTA. В MTA CoWaitForMultipleHandles это просто WaitForMultipleObjectsEx
С точки зрения корректности, трюка с CoWaitForMultipleHandles достаточно. Но могут быть проблемы с производительностью.
Потому что все COM-вызовы будут проходить через SendMessage.
Однозначно, следует вызывать COM из другого потока как можно реже (на всяких AddRef/Release, при этом, можно не экономить, они не транслируются, кроме последнего Release).
Возможно, стоит избежать маршаллинга, поглотить параметры в IDropTarget::Drop и в рабочем потоке обходиться без COM. Особенно если не нужен feedback, или нужен только прогресс без интерактивности. Можно взять producer-consumer очередь, которая будет шустрее SendMessage на каждый чих, а то и впихнуть всё в один параметр рабочего потока.
С другой стороны, попытки избежать маршаллинга могут быть напрасными, если в IDropTarget::Drop приходит уже замаршалленый указатель. Вряд ли такое будет в проводнике, но я не сильно удивлюсь, если так будет вести себя wscript или ещё какой-то клиент.