Он все делает правильно, но (в .NET Framework 1.0) неожиданно обнаруживается необработанное исключение, которое сообщает, либо о том, что поток остановился, либо о том, что вызов должен производиться из другого потока с помощью метода Invoke. Помилуйте, какие потоки? Мы не создавали дополнительных потоков. Теперь я жалею, что не сразу стал читать документацию по методу Invoke, которым обладают все классы, производные от Control. Я сэкономил бы часы, проведенные в отладке.
Будучи уверенным, что знаю, как работает метод Invoke интерфейса IDispatch (см. технологию COM), и будучи уверенным, что COM (слава Богу) где-то далеко от того места, где мы сейчас находимся (мы ведь работаем с классами .NET), я думал, что указание на Invoke — это бред компилятора, вызванный каким-то моим крупным промахом. Видимо, FileSystemWatcher реализован в технологии COM и это печально, так как старая технология COM (основанная на интерфейсе IDispatch), по моему, является тупиковой и довольно унылой ветвью развития Microsoft-технологий. Читаем справку по методу Control.Invoke, которая необычайно богата и красноречива. Вот она вся:
Executes the specified delegate on the thread that owns the control's underlying window handle.
Все же английский язык в изощренных умах бывает очень емким. Эти слова (все вместе) означают, что:
¨ Создается поток, который владеет ресурсом типа HWND, принадлежащим элементу управления,
¨ Метод Invoke запускает в этом потоке функцию, соответствующую указанному делегатному типу.
Попробуйте на русском выразить эту мысль так же коротко. Если сможете, покажите ваш текст. Как бы там ни было, я не хочу признавать факт наличия еще одного потока, кроме потока, в котором выполняется наше приложение Client, и в адресное пространство которого загружена наша DLL Library. Но ведь меня не спрашивают. С другой стороны ситуация похожа на посылку сообщения самому себе, как в случае с функцией PostMessage).
PostMessage — традиционный трюк Win32-программиста. Он означает: выполни то, что хочешь, но позже, после окончания обработки текущего сообщения и сделай это в функции обратного вызова, то есть реакции на посланное себе же сообщение. Еще раз омечу, что рассматриваемая проблема существует в.NET Framework 1.0.
Что же делать? В качестве последнего шанса, я решил вызвать Invoke, соблюдая описанную тактику. Но для этого надо сначала создать делегатный тип. В вашем приложении он уже имеется, просто я не объяснял для чего он нужен. Приведем его вновь:
public delegate void DirCreatedHandler (TreeNode node);
Хорошо, функция такого типа будет вызвана методом Invoke. Теперь надо создать саму функцию (задание делегату, которого еще нет). Функция должна иметь сигнатуру, соответствующую делегатному типу. В этом месте почти все без исключения говорят—соответствующую делегату, а не делегатному типу. Это упрощение, так как делегат еще не создан. Вставьте функцию внутрь класса ExplorerControl (мы пока еще сидим в этом классе). Внутри нее вы видите код, который не работает (в попытке прямого вызова).
private void OnDirCreated (TreeNode node) { searchNode.Nodes.Add (node); }
Наконец, надо вызвать метод Invoke в том самом месте, где нас постигла неудача. При этом на ходу создается делегат, заряженный заданием—адресом функции обратного вызова OnDirCreated. Головоломно, не правда-ли? Но зато все работает (и в .NET Framework 1.0.). Чтобы избежать путаницы, приведем полную версию ветви Created.
case WatcherChangeTypes.Created:
string parent = Path.GetDirectoryName (e.FullPath);
if (NodeExists (parent))
Invoke (new DirCreatedHandler (OnDirCreated),
new object[] { new TreeNode (Path.GetFileName(e.Name), 10, 11) } );
msg = "Directory created: ";
break;
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.