Windows Management Instrumentation. События в WMI. Библиотека классов MyWMI. Особенности работы с большим деревом, страница 16

Он все делает правильно, но (в .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;