попробуйте код ниже в Попытка # 3.Когда я сел, чтобы написать это, по телевизору ничего не было.
Спасибо за разъяснения!Я вижу, что здесь есть только одна нить;а также, поскольку кадр CloseShelf все еще находится в стеке, похоже, что вызов COM фактически блокируется.
Из трассировки стека выглядит, как будто com-объект вызывает GetMessage или PeekMessage API (или, если это VB6, DoEvents или аналогичный), который будет проверять очередь сообщений и сообщения PROCESS на нем - независимо от того, будет ли этовызвать повторный вход.AKA «качает очередь сообщений» - но если она использует peekmessage, она не будет блокироваться, если нет сообщений, но все равно будет выполнять сообщения.В вашем стеке эти вызовы могут быть скрыты в невидимой нативной части.Трассировка стека фактически доказывает, что вызов COM возвращается в ваш код!Похоже, вы знаете об этом.Если для какого-то читателя немного туманно, вот пара ссылок:
http://discuss.joelonsoftware.com/default.asp?joel.3.456478.15
http://en.wikipedia.org/wiki/Event_loop
Совместная многозадачность (один цикл сообщений для всей ОС, такой как win3.0): http://en.wikipedia.org/wiki/Computer_multitasking#Cooperative_multitasking.2Ftime-sharing
Этим призывам на самом деле делается цитата «добровольно переданное время ...».И это все еще происходит все время в потоке GUI каждого приложения Windows!
Поскольку фактический вызов находится в COM, если вы не можете его отредактировать, вам все равно придется кодировать его.Вероятно, это только один из этих методов (надеюсь).
Иногда программы и компоненты специально проверяют цикл сообщений, чтобы дать время ответить или перекрасить экран.Точно так же, как этот плакат пытается это сделать:
Есть ли такая функция, как PeekMessage, которая не обрабатывает сообщения?
Цитата «Система может также обрабатывать внутренниеevents 'похоронит вас здесь.
обратите внимание, где вы говорите, что «программист не ожидает, что вызов метода будет асинхронным» - это не асинхронно, иначе трассировка стека будет выглядеть иначе.он «возвращается» в ваш код.Как старый программист Win3.1, это был ад, с которым мы имели дело с КАЖДЫМ приложением в системе!
Я нашел эту ссылку, которая гуглила «проверить peekmessage очереди сообщений», пытаясь проверить, есть ли Get / Peekmessage и т. Д.. может быть запрещено обрабатывать сообщения.Вы можете видеть из этого документа ...
http://msdn.microsoft.com/en-us/library/windows/desktop/ms644943(v=vs.85).aspx
... что отправка что-то вроде PM_QS_INPUT |PM_QS_PAINT предотвратит проблему.К сожалению, так как вы не звоните, вы не можете это изменить!И я не видел никакого способа установить флаг для управления последующей обработкой сообщений от более поздних вызовов.
Если читатель все еще в замешательстве (если не пропустить этот код) ... Для простого примера сделайтеVB Winforms App и сделать одну кнопку и дважды щелкните его и вставьте этот код в - (я говорю VB, потому что application.doevents является самым удобным способом вызвать эту грязную проверку очереди сообщений):
For i As Integer = 0 To 20
Text = i.ToString
System.Threading.Thread.Sleep(100)
Application.DoEvents()
Next
Теперь нажмитекнопка.Обратите внимание, что вы можете увеличить окно и перерисовать фон - так как doevents позволяет этим событиям происходить путем проверки очереди сообщений (удалите doevents, и он будет «ждать», пока счетчик не будет завершен; также попробуйте нажать 3x, и вы получите 3считается подряд).
СЕЙЧАС ... кикер.Нажмите на кнопку без комментариев.Нажмите кнопку 3 раза - обратные отсчеты прерывают друг друга, а затем возобновляются по завершении предыдущего.Вы можете приостановить IDE и увидеть три клика стека вызовов.
Вкуснятина!
Я также вижу, что вы пытались сделать несколько вещей, чтобы остановить обработку сообщений или событий.Если код, который запускает EventShelfClosed, вызывается из повторного входа, вызванного PeekMessage или «Doevents», это может не повлиять.
Обратите внимание, что у этой практики есть свои недоброжелатели: http://www.codinghorror.com/blog/2005/08/is-doevents-evil-revisited.html
Лучше всего было бы изменить COM, чтобы он не выполнял никаких вызовов API, проверяющих цикл сообщений.
Удачи с этим!
Еще один способ изменить его - это перейти от событий, управляющих EventShelfClosed, и назвать его явным после завершения вызова CloseShelf (так как вызов com действительно происходит).К сожалению, архитектура вашей программы, вероятно, не допустит этого без серьезных изменений и / или повышенной сплоченности и других вещей, которые загрязняют симпатичных моделей (не модные модели, обратите внимание: -).
Другим способом было бы сделать новыйОбъект потока указывает на функцию, которая вызывает com, затем запускает его, затем присоединяется к нему, в надежде, что что-то вроде PeekMessage не найдет насос сообщений в новом потоке и, следовательно, не будет мешать вещам.Похоже, что некоторые из вас пытались задействовать этот тип вещей.К сожалению, если COM ищет сообщения, и нет сообщений в потоке, kaboom.Это, вероятно, взорвется вместо того, чтобы просто игнорировать вещи.Похоже, это то, что случилось.Более того, если COM полагается на другие элементы, к которым следует обращаться только из потока GUI / messagepump, у вас возникают проблемы с межпоточными вызовами (что, несомненно, будет иметь место, если COM взаимодействует с пользовательским интерфейсом или какими-либо объектами пользовательского интерфейса).).
Если вы не можете остановить проверку очереди сообщений или запретить запуск EventShelfClosed до позднего времени, у вас нет другого выбора, кроме как вызвать EventShelfClosed.Но то, что вы можете сделать, это заставить его ждать, а затем падать, когда CloseShelf закончен.
Таким образом, вы все равно должны иметь логическое поле уровня класса, установленное / не установленное CloseShelf, чтобы EventShelfClosed знал, что он работает.
К сожалению, просто проверяя это в цикле while, даже в режиме сна, вы заблокируете единственный поток, который у вас есть, и остановите приложение.Вы можете просто попытаться заставить EventShelfClosed повторно поднять себя и выйти из функции, пока установлен bool;но поскольку RaiseEvent остается внутри управляемого объекта, он сразу же запускает код и не проверяет очередь сообщений, в случае сбоя он переходит с помощью переполнения стека.Если вы можете выяснить, как повторно вызвать EventShelfClosed, вызвав API PostMessage (а не SendMessage, который запускает его сразу) - это будет продолжать помещать в очередь сообщений потока GUI столько раз, сколько вызов COM заставит окна проверять.Если не указано, что COM ожидает пустую очередь по какой-то глупой причине - очередная блокировка.Не рекомендую.
Так ... Вы можете бороться с огнем с помощью огня.Здесь я реализую еще один цикл проверки сообщений, чтобы ваши события происходили, пока вы ожидаете очистки bool.
Обратите внимание, что это только исправит эту проблему в этом одном случае.Аудит всех звонков ... это не волшебная пуля.Я думаю, что нет ни одного.Очень грязно, и это полный взлом.
Попытка # 3
Это на самом деле не попытка № 3, это больше похоже на возможность № 8.Но я сослался на это в своем старом ответе и мне лень это редактировать.
Boolean InCloseShelf
function CloseShelf(...)
InCloseShelf=True;
try
{
com call and all else
}
finally
InCloseShelf=False
function EventShelfClosed(...
while (InCloseShelf)
{
DoEvents
}
Теперь, конечно, в WPF нет DoEvents, он есть в "приложении" winforms.Этот блог имеет реализацию
http://dedjo.blogspot.com/2007/08/how-to-doevents-in-wpf.html
void DoEvents(){
DispatcherFrame f = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
(SendOrPostCallback)delegate(object arg) {
DispatcherFrame fr = arg as DispatcherFrame;
fr.Continue=True;
}, f);
Dispatcher.PushFrame(frame);
}
Не проверено, конечно!Обратите внимание, что это исправление в комментариях:
static void DoEvents()
{
DispatcherFrame frame = new DispatcherFrame(true);
Dispatcher.CurrentDispatcher.BeginInvoke
(
DispatcherPriority.Background,
(SendOrPostCallback) delegate(object arg)
{
var f = arg as DispatcherFrame;
f.Continue = false;
},
frame
);
Dispatcher.PushFrame(frame);
}
Или вы всегда можете ссылаться на WinForms и вызывать Application.DoEvents.
Полагаю, вы уже это знаете, но выв плохом месте прямо сейчас.Если это не делает, удачи!Если вы найдете лучший способ, пожалуйста, обновите пост, потому что, злые хаки, такие как этот отстой, но теперь вы можете понять, почему люди говорят, чтобы проверять очередь сообщений экономно!