Программирование PC Dependency Walker - помощник в разрешении зависимостей Thu, September 21 2017  

Поделиться

нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.


Dependency Walker - помощник в разрешении зависимостей Печать
Добавил(а) microsin   

Dependency Walker - бесплатная, свободная утилита, которая сканирует 32-битные и 64-битные исполняемые файлы Windows (exe, dll, ocx, sys и т. п.), и строит диаграмму - иерархическое дерево всех модулей (библиотек), от которых зависит программа. Для каждого найденного модуля выводится список всех функций, которые экспортирует этот модуль, и какие из этих функций реально вызываются другими модулями. Другой вид отображает минимальный набор необходимых файлов вместе с детальной информацией по каждому файлу, включая полный путь до файла, базовый адрес, номера версии, тип машины, отладочную информацию и другие данные. Здесь и далее перевод части документации, доступной на сайте программы [1].

Dependency-Walker-screenshot

Dependency Walker также очень полезен для разрешения системных ошибок, связанных с загрузкой и выполнением модулей. Dependency Walker детектирует множество общих проблем приложений, такие как отсутствующие модули, ошибочные модули, несоответствие импорта/экспорта (функций), ошибки циклических зависимостей, несоответствие модулей типу машины и отказы инициализации модулей.

Dependency Walker работает на Windows 95, Windows 98, Windows Me, Windows NT, Windows 2000, Windows XP, Windows 2003, Windows Vista, Windows 7 и Windows 8. Он может обрабатывать любой 32-битный или 64-битный модуль Windows, включая разработанные для Windows CE. Он может быть запущен как приложение с графическим интерфейсом или как консольное приложение. Dependency Walker поддерживает все типы зависимости модулей, включая неявные implicit, явные explicit (dynamic / runtime), перенаправленные (forwarded), с отложенной загрузкой (delay-loaded) и инжектируемые (injected). В программе имеется подробная подсказка (help).

Dependency Walker полностью свободен для использования. Однако Вам нельзя получать доход от его распространения, и нельзя поставлять совместно с другим продуктом. Dependency Walker является частью некоторых продуктов Microsoft, таких как Visual Studio, Visual C++, Visual Basic, Windows 2000/XP/2003 support tools (на Windows CD), Windows 98/NT/2000/XP/2003 Resource Kits, Platform SDK, Windows DDK, Windows SDK и MSDN. Также есть несколько мест на сайте Microsoft, откуда свободно можно загрузить Dependency Walker. Сайт [1] специально разработан для того, чтобы можно было распространять для тестирования последнюю версию Dependency Walker.

[Типы зависимостей, обрабатываемых Dependency Walker]

Есть несколько вариантов, как один модуль может зависеть от другого модуля:

1. Implicit Dependency, неявная зависимость (также известная как load-time dependency, зависимость времени загрузки или иногда некорректно называемая как static dependency, статическая зависимость): Модуль A неявно связан с библиотечным файлом Модуля B при компилировании/линковке, и исходный код Модуля A реально вызывает одну или больше функций Модуля B. Модуль B является зависимостью времени загрузки для Модуля A, и будет загружен в память независимо от того, что Модуль A на самом деле будет вызывать Модуль B во время выполнения. Модуль B будет перечислен в таблице импорта (import table) Модуля A.

2. Delay-load Dependency, зависимость отложенной загрузки: Модуль A связан по отложенной загрузке (delay-load linked) с библиотечным файлом Модуля B при компилировании/линковке, и исходный код Модуля A в действительности вызывает одну или больше функций Модуля B. Модуль B является динамической зависимостью и будет загружен в память только тогда, когда Модуль A на самом сделает обращение к функциям Модуля B во время выполнения. Модуль B будет перечислен в таблице импорта отложенной загрузки (delay-load import table) Модуля A.

3. Forward Dependency, перенаправленная зависимость: Модуль A связан с библиотечным файлом Модуля B при компиляции/линковке, и исходный код Модуля A реально вызывает одну или больше функций Модуля B. Некоторые из функций, вызываемых в Модуле B, в действительности перенаправляют вызов функции из Модуля C. Модуль B и Модуль C оба являются зависимостями для Модуля A, однако только Модуль B будет перечислен в таблице импорта Модуля A.

4. Explicit Dependency, явная зависимость (также известная как dynamic, динамическая или run-time dependency, зависимость времени выполнения): Модуль A не связан с Модулем B при компиляции/линковке. Во время выполнения Модуль A динамически загружает Модуль B с помощью вызова функции типа LoadLibrary. Модуль B становится для Модуля A зависимостью времени выполнения (run time dependency), но при этом Модуль B не будет перечислен ни в какой таблице Модуля A. Этот тип зависимости является общим для OCX-ов, объектов COM и приложений Visual Basic.

5. System Hook Dependency, зависимость хука системы (также известная как injected dependency, инжектированная зависимость): этот тип зависимости происходит, где другое приложение делает хук (привязку) к специфичному событию (наподобие события от мыши) в процесс. Когда этот процесс создает событие, операционная система может инжектировать модуль в процесс для обработки события. Модуль, который инжектируется в процесс, не является реальной зависимостью для другого модуля, однако он размещается в том же самом адресном пространстве процесса.

Dependency Walker полностью поддерживает модули, загружаемые по всем вышеперечисленным техникам. Случаи 1, 2 и 3 могут быть легко выявлены простым открытием модуля в Dependency Walker. Случаи 4 и 5 требуют профайлинга времени выполнения (runtime profiling), эта новая фича появилась в Dependency Walker 2.0. Для дополнительной информации по профилированию обратитесь к секции "Использование профайлинга (Application Profiling) для выявления динамических зависимостей (Dynamic Dependencies)".

[Использование профайлинга (Application Profiling) для выявления динамических зависимостей (Dynamic Dependencies)]

Начиная с Dependency Walker версии 2.0 добавлен профайлинг приложений - техника, используемая для отслеживания работающей программы, чтобы посмотреть, какие модули она загружает. Это позволяет Dependency Walker выявить динамически загружаемые модули, которые не обязательно будут перечислены в таблицах импорта других модулей. Профайлер Dependency Walker также может детектировать ситуации, когда модуль не может инициализироваться, что часто приводит к ошибке "The application failed to initialize properly" (приложение не может быть правильно инициализировано).

Когда модуль впервые открыт в Dependency Walker, он немедленно будет просканирован на все неявные, отложенные и перенаправленные зависимости (подробнее см. секцию "Типы зависимостей, обрабатываемых Dependency Walker"). Как только все модули просканированы, будут отображены результаты. В дополнение к этим известным зависимостям, модули имеют право загружать другие модули во время выполнения без предварительного оповещения об этом операционной системы. Такие типы зависимостей известны как динамические, или явные зависимости. Нет другого способа их обнаружить, кроме как запустить приложение и отследить, какие модули оно загружает во время выполнения. Именно это и делает профайлинг приложений в Dependency Walker.

Чтобы профайлинг заработал, открываемый модуль должен быть исполняемым (обычно имеет расширение файла .EXE), разработанным специально для запуска в той системе, в которой Вы запускаете профайлинг через Dependency Walker. Если это условие не соблюдается, то пункт меню Start Profiling и соответствующая кнопка на тулбаре будут недоступны. Когда Вы выбираете профайлинг приложения, Ваше приложение должно начать работу. Как только приложение запустилось и заработало, Dependency Walker захватит нужную информацию и выведет её в область лога (Log View), а также обновит и другие области просмотра в рабочем окне.

Работа пользователя состоит в том, чтобы "потренировать" приложение с целью убедиться, что все динамические зависимости найдены. Обычно динамические зависимости загружаются только тогда, когда реально нужны. Например, модули, связанные с печатью, могут быть загружены только когда приложение что-то на самом деле печатает. В случае наподобие этого, если приложение не выполняет действий для печати при профайлинге, то Dependency Walker не обнаружит модули, связанные с печатью. Другие модули могут загружаться, если с приложением произойдет ошибка. Иногда похожие сценарии бывают сложны для воспроизведения, чтобы можно было отследить зависимости. По этой причине нельзя гарантировать, что найдены все динамические зависимости, однако чем больше приложение тестируется, тем больше шансов найти все зависимости.

Профайлер приложения Dependency Walker отслеживает каждый загруженный модуль и пытается определить, какой модуль запрашивал загружаемый модуль. Это позволяет динамически загружаемым модулям быть вставленными в вид дерева зависимостей вида (Module Dependency Tree View) как дочерние для того модуля, который на самом деле загрузил модуль.

Профайлер работает, цепляя (hooking) некоторые вызовы в профилируемом сетевом процессе (remote process). На Windows 95, Windows 98 и Windows Me могут быть подцеплены только несистемные модули. В результате когда системный модуль динамически загружает другой модуль, профайлер не может указать, какой модуль является родительским для динамически загруженного модуля. Модули без родителя будут добавлены в корневой список Module Dependency Tree View. Все модули, которые были загружены от общесистемного хука, будут также добавлены в корень Module Dependency Tree View, поскольку эти типы модулей загружаются напрямую операционной системой, и не имеют родительского модуля. Даже при том, что Dependency Walker может быть не в состоянии детектировать родителя для динамической зависимости, он может гарантировать, что будут детектированы все модули, загруженные приложением.

Одна заключительная польза от профайлера в том, что он может исправить пути любых модулей, которые возможно были неправильно определены при начальном сканировании, когда определяются неявные зависимости. Когда Вы первый раз открываете модуль в Dependency Walker, он рекурсивно сканирует все таблицы импорта и экспорта модулей, чтобы построить начальную иерархию модулей. В этих таблицах сохранены только имена файлов, при этом Dependency Walker использует Вами установленные правила в диалоге порядка поиска модулей (Module Search Order Dialog), чтобы определить полный путь до каждого модуля. Во время профайлинга, Dependency Walker проверяет реальный путь к каждому модулю, который был загружен, и сравнивает с модулями в дереве. Если модуль был загружен по другому пути, чем Dependency Walker ожидал, то он обновит иерархию модулей, и другие области просмотра также отразят эти изменения.

[Как интерпретировать предупреждения (Warnings) и ошибки (Errors) в Dependency Walker]

Для проверяемого приложения Dependency Walker может генерировать множество предупреждений (Warnings) и сообщений об ошибках (Errors). Некоторые ошибки могут привести к невозможности работы приложения, а другие безобидны и могут быть проигнорированы. Большинство проблем с приложением может быть отнесено к 2 категориям: проблемы при загрузке (load-time failures) и проблемы при работе (run-time failures).

Load-time failures означают, что приложение или модуль могут и не запуститься, чтобы начать работу. В технических терминах это обычно означает, что точка входа в модуль не достигнута, потому что операционная система не смогла найти все требуемые модули (зависимости). Это может произойти, если неявная или перенаправленная зависимость не разрешена из-за того, что файлы зависимости не найдены, или в них отсутствует нужная функция (для получения дополнительной информации по типам зависимостей обратитесь к секции "Типы зависимостей, обрабатываемых Dependency Walker"). Вы также получите load-time failure, если приложение попытается загрузить испорченный модуль, модуль не для Windows, модуль для другого типа CPU (отличающегося от того, что Вы используете), или 16-битный модуль в 32-битное приложение. Вот некоторые из сообщений об ошибках времени загрузки (load-time error):

The dynamic link library BAR.DLL could not be found in the specified path ... (динамически загружаемая библиотека не найдена по указанному пути).
The procedure entry point FOO could not be located in the dynamic link library BAR.DLL (точка входа в нужную процедуру не обнаружена в динамически загружаемой библиотеке).
The application or DLL BAR.DLL is not a valid Windows image (приложение или DLL не являются допустимым образом для Windows).
The application failed to initialize properly (приложение не смогло должным образом инициализироваться).
Initialization of dynamic link library BAR.DLL failed (неудачная инициализация динамически загружаемой библиотеки). The process is terminated abnormally (выполнение процесса было аварийно прервано).
The image file BAR.EXE is valid, but is for a machine type other than current machine (исполняемый файл корректный, но он предназначен для другой среды выполнения).

Большинство проблем времени загрузки могут быть сразу выявлены с помощью Dependency Walker. Когда Вы впервые открываете модуль в Dependency Walker, он сканирует модуль на обнаружение неявных, перенаправленных и отложенных по загрузке зависимостей. Если есть какие-нибудь отсутствующие неявные или перенаправленные зависимости, или есть ошибки, то возможно приложение встретится с проблемами, если будет запущено. Зависимости с отложенной загрузкой не требуются операционной системой во время загрузки приложения, так что ошибки или предупреждения, связанные с зависимостью с отложенной загрузкой возможно и не создадут проблемы.

Зависимости времени выполнения - это модули, которые приложение загружает после того, как проинициализировалось и начало работу. Такая загрузка модулей обычно достигается вызовом одной из функций типа LoadLibrary. Как только модуль загружен, приложение делает вызов функции GetProcAddress, чтобы найти нужную функцию в только что загруженном модуле. Dependency Walker может отследить все эти вызовы и сообщить от любых проблемах. Однако, если приложение написано таким образом, чтобы обработать эти проблемы, то предупреждения Dependency Walker можно проигнорировать.

Есть несколько причин использовать зависимости времени загрузки. Во-первых, это может ускорить загрузку приложения, поскольку приложение может отложить загрузку определенных модулей, которые пока не нужны. Например, если приложение использует DLL для печати, то DLL может не загружаться, пока Вы не будете что-то печатать из приложения. Во-вторых, это может использоваться в случаях, когда модуль или функция в модуле могут отсутствовать. Например, приложение может нуждаться в вызове специфичной для Windows NT функции, когда работает на Windows NT, но этот модуль или функция не существуют на Windows 9x. Если приложение сделает неявную связь с модулем, где должна быть такая функция, то на операционных системах Windows 9x может произойти ошибка при загрузке, поскольку операционная система в процессе загрузки не сможет найти функцию. Если же сделать явную зависимость, то приложение может проверить - есть ли такая функция, и сделать её вызов только тогда, когда функция найдена - по крайней мере, приложение может продолжить работу всегда, хотя иногда функционал приложения может быть урезан.

Имеется два типа зависимостей времени выполнения: явная зависимость (часто называемая динамической зависимостью) и зависимость с отложенной загрузкой. Явная зависимость может быть загружена в любой момент в течение жизни приложения, без предварительного уведомления операционной системы. Поэтому есть только один способ обнаружить явную зависимость - запуск приложения и отслеживание, какие модули оно загружает в процессе работы (для дополнительной информации по профайлингу см. раздел "Использование профайлинга (Application Profiling) для выявления динамических зависимостей (Dynamic Dependencies)"). При наличии явной зависимости приложение делает прямой вызов LoadLibrary и GetProcAddress, чтобы сделать свою работу.

Зависимости с отложенной загрузкой реально реализованы так же как и явные зависимости, но загрузку должна выполнить библиотека-помощник (helper library) и линковщик. Большинство всех модулей Windows имеют встроенную "таблицу импорта" (import table). Эта таблица строится линковщиком и используется операционной системой для определения явных или перенаправленных зависимостей для имеющегося модуля. Любой модуль или функция в этом списке, которые не найдены, приведут к ошибке модуля. Если Вы укажете линкеру создать для модуля зависимость с отложенной загрузкой, то вместо того, чтобы сохранить информацию о модуле в основной import table, линкер сохранит её в отдельной таблице импорта с отложенной загрузкой (delay-load import table). Во время выполнения, если модуль обращается к модулю с отложенной зависимостью, вызов перехватывается хелпером (helper library). Этот хелпер использует LoadLibrary для загрузки модуля и GetProcAddress для опроса всех функций, которые представлены в модуле. Как только этот процесс завершится, вызов будет передан в реальную функцию, и выполнение программы продолжится без модуля, который сделал загрузку. Все будущие вызовы из специфичного модуля к модулю с отложенной загрузкой дальше будут делаться в уже загруженный модуль напрямую, без перехвата вызовов библиотекой хелпера.

Библиотека хелпера, реализующая отложенную загрузку, имеет механизм оповещения вызывающей программы, если произошла ошибка. Как и в случае с явными зависимостями, если приложение подготовлено к такой ошибке, то это не должно создавать проблемы.

Если обобщить сказанное, то неявные и отложенные зависимости требуют наличия соответствующего кода (реализованных зависимостей), чтобы в работе приложения не было ошибок и предупреждений. Явные или отложенные зависимости могут отсутствовать, и могут не нуждаться в экспорте всех функций, которые хотел бы импортировать родительский (вызывающий) модуль. Однако если приложение не подготовлено для обработки отсутствующих модулей явной или отложенной зависимости, или к обработке отсутствия функций в модуле явной или отложенной зависимости, то это может привести к ошибке времени выполнения приложения. Dependency Walker не может предсказать - планирует ли приложение обработать ошибки, так что он может просто предупредить Вас о всех потенциальных проблемах. Если Вы считаете, что приложение работает хорошо, то возможно Вы можете игнорировать большинство предупреждений, которые выдает Dependency Walker. Однако если Ваше приложение рушится, то предупреждения могут предоставить некоторую информацию о причине происходящего.

Есть также другой тип предупреждения, генерируемого Dependency Walker при профайлинге, которое стоит упомянуть. Оно связано с первым и вторым исключениями (exceptions). Когда в приложении произойдет исключение (exception, наподобие access violation, нарушение доступа к памяти), приложению дают шанс обработать исключение. Эта возможность называется first chance exceptions. Если приложение обработает exception, то при этом не будет проблемы, и exception наверное можно проигнорировать. Если приложение не обрабатывает first chance exception, то исключение переходит в стадию second chance exception, которое обычно фатально для приложения. Когда произойдет second chance exception, операционная система обычно отображает диалог, который сообщает Вам, что приложение упало и должно быть завершено.

Dependency Walker всегда записывает в лог second chance exceptions и может опционально записывать в лог first chance exceptions. Многие приложения обычно генерируют при работе first chance exceptions и обрабатывают их. Это не признак плохого приложения, поскольку есть много законных причин для генерации first chance exceptions и обработки их.

[Обзор номеров версий модуля (Module Version Numbers)]

Имеется 4 поля номера версии, которые гарантировано имеются в каждом модуле Windows. Все они имеют номера версий, состоящих из 2 частей (#.#). Эти поля включают:

Image Version Версия образа - это значение устанавливает разработчик модуля, используя оператор VERSION в своем файле DEF, или используя опцию линковщика /VERSION. Это обычно представляет версию модуля или продукта, частью которого является модуль. Здесь может содержаться любое значение, поскольку его устанавливает разработчик. Если разработчик не указал версию, то по умолчанию это значение будет 0.0.

OS Version Версия операционной системы - это значение показывает, для какой операционной системы был разработан модуль (где он должен работать). Определенные функции могут вести себя по-разному в зависимости от этого значения, чтобы сохранить совместимость приложения, которое было собрано для определенной версии операционной системы.

Subsystem Version Версия подсистемы - это значение показывает, для какой версии подсистемы был разработан модуль. Большинство модулей используют значение по умолчанию, но разработчики могут это поменять, используя опцию линковщика /SUBSYSTEM, если целью является частная версия подсистемы, отличающаяся от той, что по умолчанию. Определенные функции подсистемы могут работать по-разному в зависимости от этого значения, чтобы сохранить совместимость с приложениями, собранными для частной версии подсистемы.

Linker Version Версия линковщика - это значение представляет версию линкера, который использовался для сборки модуля. Это может использоваться для определения специфической особенности (доступен или нет определенный функционал) линкера во время сборки приложения. Например, зависимость отложенной загрузки является новой фичей, появившейся начиная с версии 6.0 линкера, так что если это значение меньше 6.0, то в модуле не должно быть зависимостей с отложенной загрузкой.

В дополнение к 4 стандартным значениям версии разработчики могут добавить еще 4 дополнительных номера версии путем включения ресурса VERSION_INFO как части своего файла ресурсов. Эта структура ресурса имеет два поля, состоящих из 4 цифровых частей (#.#.#.#) и два текстовых поля. Они включают:

File Version Value Версия файла - это поле известно как поле "FILEVERSION" в структуре ресурса VERSION_INFO. Это числовое значение, которое обычно соответствует собственной версии модуля, но может содержать любое значение, поскольку его может установить разработчик. Это значение в большинстве программ используется для того, чтобы сравнить два модуля, и узнать, какой из них новее.

Product Version Value Версия продукта - это поле известно как поле "PRODUCTVERSION" в структуре ресурса VERSION_INFO. Это числовое значение, которое обычно соответствует версии продукта, частью которого является модуль, но может содержать любое значение, поскольку его может установить разработчик. Например, "Acme Tools version 3.0" является набором утилит, в который входит "Acme Virus Checker version 1.5" (антивирус Acme версии 1.5). Исполняемый файл антивируса может иметь версию файла 1.5.0.0 и версию продукта 3.0.0.0.

File Version Text Текст версии файла - это поле известно как поле "FileVersion" в структуре ресурса VERSION_INFO. Это текстовая строка, обычно представляющая собственную версию модуля, но может содержать любой текст, поскольку его может установить разработчик.

Product Version Text - это поле известно как поле "ProductVersion" в структуре ресурса VERSION_INFO. Это текстовая строка, обычно представляющая версию продукта, частью которого является модуль, но может содержать любую строку текста, поскольку его может установить разработчик.

Dependency Walker показывает действительные значения версии FILEVERSION и PRODUCTVERSION, не текстовые строковые версии. Другие программы, такие как диалог Windows Properties, показывают текстовые значения версии, поскольку разработчик хотел бы, чтобы именно их увидел обычный рядовой пользователь. Например, Вы можете увидеть только "2.0" в диалоге Windows Properties для модуля, который имеет реальную версию 2.0.5.2034. Если Вы хотите узнать действительную версию файла, то Вам нужно использовать Dependency Walker, не диалог Windows Properties.

Большим web-сайтом для поиска номеров версий модулей является база данных DLL компании Microsoft [2]. Этот сайт имеет детальную историю версий файлов DLL и перечисляет продукты, которые поставляются с каждой версией. Эта база данных может быть полезной в разрешении проблем с версиями.

[Dependency Walker FAQ]

Q001. Dependency Walker, как мне кажется, показывает только некоторые зависимости моего приложения. Почему он не показывает все зависимости?
A001. Когда впервые открываете модуль в Dependency Walker, он покажет только неявные (implicit), перенаправленные (forwarded), и отложенные (delay-load) зависимости. Многие зависимости загружаются динамически и не будут детектированы, пока Вы не выполните профайлинг (profile) приложения внутри Dependency Walker. Для дополнительной информации см. разделы "Типы зависимостей, обрабатываемых Dependency Walker" и "Использование профайлинга (Application Profiling) для выявления динамических зависимостей (Dynamic Dependencies)".

Q002. Почему во многих приложениях MPR.DLL показана красным под SHLWAPI.DLL, потому что в нем отсутствует функция с именем WNetRestoreConnectionA? Также я получаю сообщение "Warning: At least one module has an unresolved import due to a missing export function in a delay-load dependent module" (предупреждение: как минимум в одном модуле есть нереализованный импорт из-за отсутствия экспорта функции в модуле зависимости с отложенной загрузкой).
A002. Некоторые версии SHLWAPI.DLL (как на Windows XP) имеют зависимость с отложенной загрузкой (delay-load dependency) функции WNetRestoreConnectionA в MPR.DLL. Пропащая функция с отложенной загрузкой не создает проблему, пока вызванная DLL подготовлена для обработки этой ситуации. Dependency Walker помечает все потенциальные проблемы, так как он не может правильно детектировать их, если приложение предназначено для обработки подобной проблемы. В случае SHLWAPI.DLL проблемы нет, поскольку не требуется, чтобы существовала WNetRestoreConnectionA, и обработка проблемы осуществляется во время выполнения. Это предупреждение можно проигнорировать. См. секцию help "How to Interpret Warnings and Errors in Dependency Walker" для получения подробностей.

Q003. Почему MSJAVA.DLL показана желтым (отсутствующий модуль, missing module) и выдается сообщение "Warning: At least one delay-load dependency module was not found" (предупреждение: не найден как минимум один модуль с отложенной загрузкой)?
A003. Модуль MSHTML.DLL, который появился вместе с Windows XP SP2 и Windows 2003 SP1, имеет зависимость с отложенной загрузкой от MSJAVA.DLL. Отсутствующие зависимости с отложенной загрузкой не создают проблему, пока вызывающая DLL подготовлена к обработке ситуации с отсутствием модуля. Dependency Walker помечает все потенциальные проблемы, так как он не может правильно детектировать их, если приложение предназначено для обработки подобной проблемы. В этом частном случае MSJAVA.DLL является необязательным модулем, и MSHTML.DLL сделана так, чтобы поддержать отсутствие этого модуля. Это предупреждение можно проигнорировать. См. секцию help "How to Interpret Warnings and Errors in Dependency Walker" для получения подробностей.

Q004. Dependency Walker говорит, что у меня отсутствует APPHELP.DLL. Где я могу получить этот модуль?
A004. APPHELP.DLL используется в обработке совместимости приложений Windows XP. Эта DLL есть только в Windows XP/2003/Vista/7/8/+. Если Вы видите это предупреждение, то скорее всего Вы установили Internet Explorer 6.0 на вашем компьютере с операционной системой, которая является предшественницей Windows XP (Windows 95/98/ME/2000). Internet Explorer 6.0 инсталлирует новую SHWAPI.DLL, которая имеет отложенную зависимость от APPHELP.DLL. Это нормально, поскольку SHWAPI.DLL не ожидает найти APPHELP.DLL на версиях Windows, которые предшествовали Windows XP. Это предупреждение можно проигнорировать. Вам не нужно искать APPHELP.DLL на Windows 95/98/ME/2000.

Q005. Может ли мне помочь Dependency Walker, если мой компонент не хочет регистрироваться? Или: почему REGSVR32.EXE отказывается регистрировать мою DLL, но Dependency Walker не показывает никаких ошибок в моей DLL?
A005. Многие модули нуждаются в "регистрации" на компьютере, чтобы они могли работать. Это включает многие контролы ActiveX, OCX-ы, компоненты COM, компоненты ATL, компоненты Visual Basic и многие другие. Эти типы модулей обычно регистрируются с помощью REGSVR32.EXE или чего-нибудь подобного. По большей части REGSVR32.EXE загружает Вашу DLL, вызывает GetProcAddress для функции DllRegisterServer в DLL, и затем вызывает эту функцию. Общая ошибка бывает, когда Ваша DLL обращается к другой DLL, которая отсутствует или не зарегистрирована. Если Вы просто откроете Вашу DLL в Dependency Walker, Вы можете не увидеть проблему, в зависимости от типа ошибки при регистрации.

Самый лучший метод отладки и исправления ошибки модуля, который отказывается регистрироваться - открыть REGSVR32.EXE в Dependency Walker, а не Вашу DLL. Затем выберите запуск профайлинга (start profiling, F7). В диалоге профайлинга введите полный путь к Вашей DLL в поле "Program arguments" (аргументы программы). Для поля "Starting directory" (директория запуска), Вы можете захотеть ввести директорию, где находится DLL. Проверьте опции, которые хотите использовать, и нажмите Ok. Это действие запустит REGSVR32.EXE, которая сделает попытку зарегистрировать Вашу DLL. При действительно запущенной REGSVR32.EXE Вы сможете увидеть больше типов ошибок времени выполнения (runtime errors).

Q006. Мое приложение лучше работает, когда я делаю его профайлинг в Dependency Walker, чем если оно работает самостоятельно. Почему так?
A006. Было уже несколько сообщений о том, что приложения сами по себе не работают и завершаются по ошибке, но работают под профайлингом из Dependency Walker. Dependency Walker работает как отладчик, когда Вы делаете профайлинг приложения. Это само по себе запускает Вашу программу по-другому.

Во-первых, создаются дополнительная нагрузка от Dependency Walker, так что Ваше приложение будет работать медленнее. Если Ваше приложение зависает по причине гонки процессов во время реального выполнения (race condition), то замедление работы может предотвратить гонку и устранить зависание. Если причина в этом, то значит проблема заключается в ошибочном дизайне приложения, и Вам просто повезло, если приложение не рассыпается в некоторых случаях (как например при работе из профайлинга Dependency Walker).

Во-вторых, когда обычно потоки блокируются на критических секциях, событиях, семафорах, мьютексах и т. д., они будут разблокированы по принципу первый-вошел-первый-вышел (first-in-first-out, FIFO). Это не гарантируется операционной системой, но обычно это именно так. Когда идет выполнение приложения под отладчиком, то запросы FIFO иногда перемешиваются и получают случайный порядок, так что потоки могут быть заблокированы и разблокированы (block, resume) в другом порядке, отличающемся от порядка работы без отладчика. Это могло бы уменьшить условия для race condition или изменить условия выполнения для того, чтобы заставить потоки работать. И снова, если приложение написано так плохо, то Вам просто повезло, что оно иногда работает.

И, наконец, приложения при работе под отладчиком автоматически получают в распоряжение пул памяти - кучу системы, system debug heap. Все функции, работающие с памятью, теперь буду обработаны несколько по-другому. Выделенные блоки памяти дополняются защитными байтами - чтобы проверить, не вышла ли запись за пределы выделенного региона буфера (buffer overrun/underrun). Выделения памяти могут также по-другому размещаться в памяти - в сравнении с тем, как если бы выделения были бы сделаны без отладчика. Так что если Вы пишете за конец буфера под отладчиком (случай переполнения буфера), то Вы можете перезаписать защитные байты, уже освобожденную память, или что-нибудь не очень критичное для работы. Однако, когда запустите программу без отладчика, Ваше переполнение буфера может вызывать критически последствия (например, портить указатель), и Ваше приложение зависнет или вызовет сбой.

Вы можете отключить debug heap в Dependency Walker, и увидеть сбой при переполнении буфера во время профайлинга приложения. Если это так, то возможно у Вас проблема с переполнением буфера, забытым/плохим/освобожденным указателем и т. п. Чтобы сделать это, запустите командную строку (cmd). Введите команду "SET _NO_DEBUG_HEAP=1". Затем запустите Dependency Walker из командной строки. Это запретит debug heap для этого экземпляра Dependency Walker. Имейте в виду, что это работает только на Windows XP и более поздних системах.

Q007. Как мне просмотреть параметры и возвращаемые типы функции?
A007. Для многих функций эта информация просто не представлена в модуле. Формат модуля Windows только предоставляет одну текстовую строку для идентификации каждой функции. Здесь нет структурированного способа для перечисления количества параметров, типа параметра или возвращаемого типа. Однако некоторые языки программирования делают для вызываемой функции нечто, называемое "декорирование" (decoration) или "искажение" (mangling), которое заключается в кодировании информации в текстовой строке. Например, функция наподобие Foo(int, int) с простым декорированием может быть экспортирована как _Foo@8. Цифра 8 кодирует количество байт, используемых под параметры. Если используется C++ decoration, функция может экспортироваться как ?Foo@@YGHHH@Z, что может быть декодировано обратно для получения оригинального прототипа функции: int Foo(int, int). Dependency Walker поддерживает декодирование C++ decoration с использованием команды Undecorate C++ Functions.

Q008. Почему имена моих функций экспортируются не так, как я их декларировал?
A008. Многие компиляторы по умолчанию "декорируют" (decorate) имена функций. Если вы не дали компилятору прямых указаний как экспортировать функции, то функции наподобие int Foo(int, int) могут быть экспортированы как _Foo@8, или даже ?Foo@@YGHHH@Z, если используется C++ decoration. Языки наподобие C++ позволяют перезагрузку функций (function overloading) с возможностью задавать несколько функций с одним и тем же именем, но с разными параметрами. По этой причине каждая (перегружаемая) функция должна иметь уникальную строковую сигнатуру, поскольку при простом экспортировании только имени получится конфликт имен. Для запрета C++ decoration Вы можете использовать внешнюю нотацию "С" (extern "C" notation), где декларируете Ваши функции в файле исходного кода C++. Чтобы предотвратить декларирование целиком, Вы можете добавить файл DEF в Ваш проект C/C++, и декларировать действительные имена функций, которые Вы хотите экспортировать.

Q009. Мое приложение выглядит нормально работающим во время профайлинга, но я вижу ошибки в области просмотра лога красные или желтые иконки в других областях просмотра Dependency Walker. Это нормально?
A009. Может быть, что это нормально, когда Вы видите ошибки или предупреждения во время профилирования в Dependency Walker. Одну из общих ошибок можно увидеть, когда один модуль пытается динамически загрузить другой модуль (используя одну из функций LoadLibrary), но модуль не найден. Dependency Walker сделает заметку об этой ошибке, но если приложение подготовлено для обработки такой ситуации, то это не создаст проблемы. Другая общая ошибка - когда модуль пытается динамически определить местонахождение функции (используя GetProcAddress) в модуле. И опять-таки нет проблем, когда приложение само разрешает подобные ошибки. Вы можете также увидеть в окне лога событие исключения последнего шанса (first-chance exceptions). Если приложение обрабатывает исключения, и не вызывает исключения вторичной обработки (second-chance exceptions), то это не создаст проблем. Все эти случаи нормальны, и могут обычно игнорироваться. Однако, если профилируемое приложение валится, или не может правильно работать, то ошибки могут указать на внутреннюю причину, вызвавшую проблему. Для получения дополнительной информации см. раздел "Как интерпретировать предупреждения (Warnings) и ошибки (Errors) в Dependency Walker".

Q010. Ого, мое приложение зависит от всех этих файлов? Какие из них мне нужно распространять с моим приложением?
A010. Для начинающих: есть некоторые модули, которые Вам никогда не надо распространять вместе со своими программами, такие как kernel32.dll, user32.dll и gdi32.dll. Чтобы посмотреть, какие файлы разрешены для распространения, Вы можете найти файл под названием REDIST.TXT на Вашем компьютере для разработки. Этот файл поставляется вместе со средствами разработки наподобие Microsoft Visual C++ или Visual Basic. Вы можете также просмотреть "redistributable files" и "redist.txt" в индексе MSDN для получения дополнительной информации - какие файлы распространять, как их распространять, как проверять версии файлов, и т. д.

Q011. Что означает "Shared module not hooked" (общий модуль не подцеплен), и почему некоторые вызовы DllMain модуля не видны в логе?
A011. Dependency Walker подцепляет (hooks) модули, как они загружаются, чтобы отслеживать вызовы функций наподобие DllMain, LoadLibrary и GetProcAddress. Любой модуль, загруженный выше адреса 0x80000000 (обычно модули системы) на Windows 95/98/Me является общим на уровне системы (shared system-wide) и не может быть перехвачен для отладки (hooked). В результате Dependency Walker не может выводить в лог информацию по вызовам функций в этих модулях. Windows NT/2000/XP/2003/Vista/7/8/+ не имеют такого ограничения. Дополнительную информацию см. в разделе "Использование профайлинга приложения (Application Profiling) для обнаружения динамических зависимостей (Dynamic Dependencies)".

Q012. Почему некоторые модули показаны несколько раз под одним родительским модулем?
A012. Dependency Walker может показать модуль несколько раз, чтобы информировать Вас о том, что есть зависимость не только по одной причине. Это возможно для модуля, чтобы показать неявно связанную зависимость (implicitly linked dependency), перенаправленную зависимость (forwarded dependency), или динамическую зависимость (dynamic dependency), все под одним родительским модулем. Для получения подробностей см. вид дерева зависимостей модуля (Module Dependency Tree View). В реальности во время выполнения в памяти размещена только одна копия модуля.

Q013. Имеется ли версия Dependency Walker для работы из командной строки?
A013. Dependency Walker может работать как GUI-приложение или как утилита командной строки. Когда используется командная строка (консольный режим), Dependency Walker может обработать модуль, сохранить результаты и выйти без создания какого-либо графического интерфейса или запроса к пользователю. Для дополнительной информации см. [3].

Q014. Будет ли Will Dependency Walker обрабатывать модули COM, Visual Basic или .NET?
A014. Да. Dependency Walker будет работать только с 32-битным или 64-битным модулем Windows, и при этом не имеет значения, в каком языке он был разработан. Однако многие языки имеют свои собственные способы для указания взаимоотношения зависимостей между модулями. Например, модули COM могут иметь встроенные библиотеки типов (embedded type libraries) и информацию о регистрации в реестре, и модули .NET могут использовать сборки .NET. Все эти техники реализованы как слои выше ядра Windows API. В результате эти слои все еще нуждаются в обращении к нижним (call down) функциям ядра Windows, таким как LoadLibrary и GetProcAddress, чтобы выполнить свою работу. Именно на этом уровне ядра Dependency Walker понимает, что происходит. Таким образом, Dependency Walker может не понимать все сложности специфики языка и его верхнего интерфейса в Вашем приложении, но он все еще может отследить активность всех модулей на уровне ядра Windows API.

Q015. Работает ли Dependency Walker с 64-битными модулями?
A015. Да. Dependency Walker будет обрабатывать любые 32-битные или 64-битные модули Windows. Имеются 32-битная и 64-битная версии Dependency Walker. каждая из версий может открыть 32-битные и 64-битные модули. Однако есть главное преимущество при использовании обработки 32-битным Dependency Walker 32-бытных модулей и 64-битным Dependency Walker 64-битных модулей. Это особенно верно на 64-битной версии Windows, которая может выполнять программы как 32-битных, так и 64-битных версий. 32-битная подсистема на 64-битной Windows (известна как "WOW64") имеет свой собственный частный реестр, "AppPaths", "KnownDlls", системные папки и обработку манифеста (Manifest Processor). Только 32-битная версия Dependency Walker может получить доступ к 32-битному рабочему окружению (environment), которое нужно для точной обработки 32-битного модуля. И аналогично, только 64-битная версия Dependency Walker может получить полный доступ к 64-битному рабочему окружению, так что он всегда должен использоваться для обработки 64-битных модулей.

Q016. Почему запрещены кнопка "Start Profiling" (запуск профайлинга) и соответствующий пункт в меню?
A016. Опция профайлинга работает только при реальном выполнении Вашего приложения и отслеживании, что оно делает при загрузке. Чтобы это стало возможным, Вам нужно открыть исполняемый файл (обычно он имеет расширение EXE), а не DLL. Если Вы хотите профилировать DLL, Вам нужно открыть какой-нибудь исполняемый файл (не DLL), который будет загружать DLL (см. FAQ про использование REGSVR32.EXE для загрузки DLL). Опция профайлинга также требует, чтобы исполняемый файл был загружен для той же архитектуры CPU, в какой сейчас работает сам Dependency Walker. Например, Вам нужно использовать 32-bit x86 версию Dependency Walker для профайлинга исполняемого файла 32-bit x86, и 64-bit x64 версию Dependency Walker для профайлинга исполняемого файла 64-bit x64.

Q017. Будет ли Dependency Walker работать с модулями Windows CE?
A017. Да. Модули Windows CE используют тот же формат модуля (известен как формат "Portable Executable"), который используется для модулей, написанных для Windows 95, Windows 98, Windows Me, Windows NT, Windows 2000, Windows XP, Windows 2003, Windows Vista, Windows 7, Windows 8 и подобных версий. Сам Dependency Walker не имеет версии, которая работает под Windows CE, но Вы можете открыть модули Windows CE на Dependency Walker, работающем на стандартном компьютере с установленной операционной системой Windows. Однако Dependency Walker автоматически пытается найти зависимые модули, используя путь поиска модулей Windows по умолчанию. Для модулей Windows CE это может быть причиной ошибок, поскольку в пути поиска default search path могут оказаться не-CE модули. Чтобы исправить это, Вы можете использовать диалог Dependency Walker "Configure Module Search Order" (конфигурирование порядка путей поиска модулей), чтобы удалить все стандартные пути и затем добавить свой путь, по которому будут находиться только CE-модули. Если Вам нужно часто это делать снова и снова, то можете сохранить Ваш собственный порядок поиска в файл и затем передать этот файл для Dependency Walker, используя опцию командной строки "/d:your_file.dwp" (для получения подробностей см. [3]).

Q018. Будет ли Dependency Walker работать с 16-битными модулями?
A018. Нет. Dependency Walker поддерживает только 32-битные и 64-битные модули Windows. Он никогда не поддерживал и не будет поддерживать 16-битные модули.

Q019. Что означают все номера версий?
A019. См. раздел "Обзор номеров версий модуля (Module Version Numbers)" для получения подробной информации.

Q020. Могу ли я вывести на печать результаты сессии?
A020. Нет, но Вы можете сохранить результаты в несколько разных текстовых форматов, которые можно просмотреть в программах наподобие Notepad.

Q021. Как мне послать кому-нибудь результаты сессии?
A021. Dependency Walker поддерживает несколько форматов для захвата данных сессии. Все области вывода окна GUI поддерживают простую команду копирования (Copy). Dependency Walker поддерживает также несколько методов сохранения всей сессии в файл. Здесь доступны различные текстовые форматы файлов, которые могут быть просто распечатаны или отправлены по email кому-нибудь, кому нужно их посмотреть. Можно также сохранить результаты в файл образа Dependency Walker Image (DWI), который можно загрузить в Dependency Walker на другом компьютере, чтобы просмотреть захваченный результат с Вашего компьютера. Для дополнительной информации см. секции "Save Command" и "File Save Dialog" в документации (help).

Q022. Что означают все иконки?
A022. Каждая информационная область окна в Dependency Walker детально описана в help, где также описано назначение иконок. См. секцию "Module Session Window".

Q023. Можно мне искать функцию по её имени или порядку следования?
A023. Все области просмотра Dependency Walker могут быть рассортированы и имеют поиск. Любой текст, который Вы вводите в на фокусе в списке, приведет к поиску этого текста в том столбце, по которому список сейчас рассортирован. Например, если список экспорта функций (export function list) рассортирован по именам функций, и Вы введете "Get", то будет подсвечена первая функция, которая начинается на "Get". Это будет работать для любого столбца в любом списке. Для получения дополнительной информации см. секции help по нужным спискам просмотра.

Q024. Диалог открытия файла Dependency Walker (open dialog) не показывает тот файл, который я хочу открыть. Как мне это исправить?
A024. По умолчанию Windows "прячет" некоторые системные файлы (наподобие DLL) от пользователя. Чтобы поменять эту установку, откройте "My Computer" и выберите "Options" из меню. В зависимости от того, какую версию Windows Вы используете, это можно сделать из меню "View" или "Tools", и может называться "Folder Options" или просто "Options". В диалоге, который появится, выберите закладку "View". Вы должны увидеть опцию, которая читается либо как "Show all files" или "Show hidden files and folders". Убедитесь, что эта опция выбрана правильно - чтобы показывать нужные файлы. Вы увидите также чекбокс (галочку), который называется "Hide MS-DOS file extensions for file types that are registered" или "Hide file extensions for known file types". Вы можете захотеть убрать оттуда галочку. После всех исправлений нажмите "Ok" в этом диалоге. Теперь Dependency Walker должен показывать все системные файлы в своем диалоге открытия файла.

Q025. Как мне деинсталлировать (удалить) Dependency Walker?
A025. У Dependency Walker нет программы установки (setup) или деинсталляции (uninstall). Он разработан так, что Вам нужно просто запустить его, когда захотите, и просто удалить его, когда Dependency Walker больше не нужен. Если Вы указали обрабатывать некоторые расширения файлов через Dependency Walker, то наверное Вы захотите удалить эти связи перед удалением программы. Это можно сделать командой Handled File Extensions. Чтобы удалить Dependency Walker, который больше не нужен, просто удалите файлы depends.exe, depends.dll и depends.chm.

Q026. Почему некоторые модули ищут функцию с именем "IsTNT" в KERNEL32.DLL?
A026. TNT является 32-битным слоем эмуляции, который написан в Phar Lap. Все еще есть в использовании некоторые модули, которые имеют части кода, которые проверяют - работают ли они в окружении TNT путем вызова GetProcAddress("IsTNT") для KERNEL32.DLL. Это предупреждение можно проигнорировать.

Q027. Почему некоторые модули пытаются загрузить модуль с названием "AUX"?
A027. Это обычно связано с модулями, которые пытаются загрузить аудиодрайвер AUX. Поскольку AUX является зарезервированным именем DOS, загрузка будет ошибочной. Это предупреждение безобидно и может быть игнорировано.

Q028. MFC42.DLL пытается загрузить MFC42LOC.DLL, но она не найдена. Или: COMCTL32.DLL пытается загрузить CMCTLENU.DLL, но она не найдена. Почему так?
A028. И MFC42LOC.DLL, и CMCTLENU.DLL являются специфичным для языка DLL ресурсов (language specific resource DLL), которая может быть не нужна в Вашей системе. Многие модули Windows сохраняют все свои сообщения, привязанные к своему языку (language specific messages) во внешних DLL (одна DLL на язык). Во время выполнения модуль загружает языковую DLL на основе текущего языка операционной системы. Имена модулей обычно заканчиваются на "ENU" для языка США (United States English), "ESP" для испанского, "JPN" для японского, и т. д. Окончание "LOC", которое использует MFC, означает "localized" (локализовано). Когда установлена библиотека классов MFC, она копирует корректные языковые DLL в Вашу систему, и переименовывает их в MFC42LOC.DLL. Тогда почему же модуль не найден? Ну, большинство модулей защищают сами себя от ошибок, сохраняя один язык по умолчанию в своей собственной DLL. Если произойдет ошибка с загрузкой привязанной к языку DLL, то будут использованы ресурсы по умолчанию, встроенные в локальный модуль. В большинстве случаев эти ресурсы по умолчанию те же самые, что и должны быть в версии ENU для языковой DLL с ресурсами. Поэтому не нужна версия ENU ресурсов DLL, и поэтому во время выполнения она не найдена. Это нормально.

[Ссылки]

1. Сайт Dependency Walker site:www.dependencywalker.com.
2. Microsoft DLL Help Database site:support.microsoft.com.
3. Dependency Walker: опции командной строки и возвращаемые значения (Command Line Options and Return Values site:dependencywalker.com).

 

Добавить комментарий


Защитный код
Обновить

Top of Page