Симптомы:
Приложение зависает при вызове Control.Invoke() для фреймворка 2.0, под 1.1 все работает изумительно.
Вскрытие показало:
Не приходит эвент об окончании выполнения функции которую хотели вызвать через Control.Invoke(), более того сама функция, которую нужно вызвать в другой нитке, не вызывается вобще, однако запрос поставлен в очередь контрола. Дальнейшее исследование вопроса выявило, что выполнение очереди происходит по получении контролом специального зарегистренного сообщения "WindowsForms12_ThreadCallbackMessage". Вот тут то и выяснилось, что это сообщение вобще не посылается. Ниже приведен дизасмблеж кода с ошибкой.
private object MarshaledInvoke(Control caller, Delegate method, object[] args, bool synchronous) { int lpdwProcessId; if (!this.IsHandleCreated) throw new InvalidOperationException(SR.GetString("ErrorNoMarshalingThread")); if (((ActiveXImpl) this.Properties.GetObject(PropActiveXImpl)) != null) IntSecurity.UnmanagedCode.Demand(); bool flag = false; if ((SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(this, this.Handle), out lpdwProcessId) == SafeNativeMethods.GetCurrentThreadId()) && synchronous) flag = true; ExecutionContext executionContext = null; if (!flag) executionContext = ExecutionContext.Capture(); ThreadMethodEntry entry = new ThreadMethodEntry(caller, method, args, synchronous, executionContext); lock (this) { if (this.threadCallbackList == null) this.threadCallbackList = new Queue(); } lock (this.threadCallbackList) { if (threadCallbackMessage == 0x0) threadCallbackMessage = SafeNativeMethods.RegisterWindowMessage(Application.WindowMessagesVersion + "_ThreadCallbackMessage"); this.threadCallbackList.Enqueue(entry); // Ставим запрос в очередь вызываемого контрола } if (flag) this.InvokeMarshaledCallbacks(); // Если можно вызвать функцию синфронно, то вызываем синхронно else { UnsafeNativeMethods.PostMessage(new HandleRef(this, this.Handle), threadCallbackMessage, IntPtr.Zero, IntPtr.Zero); // Вызываем функцию асинхронно // БАГА ТУТ: нет проверки на невозможность поставить сообщение в очередь } if (!synchronous) return entry; if (!entry.IsCompleted) this.WaitForWaitHandle(entry.AsyncWaitHandle); // Ждем окончания выполнения для асинхронного вызова if (entry.exception != null) throw entry.exception; return entry.retVal; }
Причина смерти:
Горячо любимый Microsoft забыл выдать эксепшен в случае невозможности отсылки сообщения с помощью PostMessage. Оказалось, что на момент Control.Invoke() очередь сообщений заполнена на 100%!!!
Лечение: