Программирование для Windows NT© Александр Фролов, Григорий ФроловТом 27, часть 2, М.: Диалог-МИФИ, 1996, 272 стр. Как работает DLL-библиотекаВ операционной системе Microsoft Windows версии 3.1 16-разрядная DLL-библиотека состоит из нескольких специфических функций и произвольного набора функций, выполняющих ту работу, для которой разрабатывалась данная библиотека. В заголовке загрузочного модуля DLL-библиотеки описаны экспортируемые точки входа, соответствующие всем или некоторым определенным в ней функциям. Приложения могут вызывать только те функции DLL-библиотеки, которые ей экспортируются. В процессе инициализации после загрузки 16-разрядной DLL-библиотеки в память Windows версии 3.1 вызывает функцию LibEntry, которая должна быть определена в каждой DLL-библиотеке. Задачей функции LibEntry является инициализация локальной области памяти, если она определена для DLL-библиотеки. Функция LibEntry должна быть дальней функцией, составленной на языке ассемблера, так как она получает параметры через регистры процессора. Мы подробно описали функцию LibEntry и ее параметры в 13 томе “Библиотеки системного программиста”. Заметим, что использование языка ассемблера затрудняет создание мультиплатформных приложенй, поэтому в мультиплатформной операционной системе Microsoft Windows NT используется другой способ инициализации. Создавая 16-разрядную DLL-библиотеку, вам не надо определять функцию LibEntry самостоятельно, так как при создании файла DLL-библиотеки редактор связей, входящий в систему разработки, включит уже имеющийся в стандартной библиотеке модуль. Этот стандартный модуль выполняет всю необходимую работу по инициализации локальной области памяти DLL-библиотеки (с помощью функции LocalInit) и затем вызывает функцию LibMain. Функция LibMain должна присутствовать в каждой 16-разрядной DLL-библиотеке. Эту функцию надо определить самостоятельно, причем вы можете воспользоваться языком программирования С. По своему назначению функция LibMain напоминает функцию WinMain обычного приложения Windows. Функция WinMain получает управление при запуске приложения, а функция LibMain - при первой загрузке DLL-библиотеки в память. Так же как и функция WinMain, функция LibMain имеет параметры, которые можно использовать для инициализации библиотеки. Прототип функции LibMain и ее параметры были описаны в 13 томе “Библиотеки системного программиста”. Другая функция, которая присутствует в каждой 16-разрядной DLL-библиотеке, это функция WEP. В среде Microsoft Windows версии 3.1 DLL-библиотека в любой момент времени может быть выгружена из памяти. В этом случае Windows перед выгрузкой вызывает функцию WEPXE. Эта функция, как и функция LibMain, вызывается только один раз. Она может быть использована для уничтожения структур данных и освобождения блоков памяти, заказанных при инициализации DLL-библиотеки. Вам не обязательно самостоятельно определять функцию WEP. Так же как и функция LibEntry, функция WEP добавляется в 16-разрядную DLL-библиотеку транслятором. Что же касается 32-разрядных DLL-библиотек операционной системы Microsoft Windows NT, то их инициализация и выгрузка из памяти происходит иначе. Инициализация DLL-библиотеки в среде Microsoft Windows NTВ 32-разрядных DLL-библиотеках операционной системы Microsoft Windows NT вместо функций LibMain и WEP используется одна функция DLLEntryPoint, которая выполняет все необходимые задачи по инициализации библиотеки и при необходимости освобождает заказанные ранее ресурсы (имя функции инициализации может быть любым). Функции LibMain и WEP вызываются только один раз при загрузке библиотеки в память и при ее выгрузке. В отличие от них, функция DLLEntryPoint вызывается всякий раз, когда выполняется инициализация процесса или задачи, обращающихся к функциям библиотеки, а также при явной загрузке и выгрузке библиотеки функциями LoadLibrary XE "LoadLibrary" и FreeLibrary XE "FreeLibrary" . Ниже мы привели прототип функции DLLEntryPoint: BOOL WINAPI DllEntryPoint( HINSTANCE hinstDLL, // идентификатор модуля DLL-библиотеки DWORD fdwReason, // код причины вызова функции LPVOID lpvReserved); // зарезервировано Через параметр hinstDLL функции DLLEntryPoint передается идентификатор модуля DLL-библиотеки, который можно использовать при обращении к ресурсам, расположенным в файле этой библиотеки. Что же касается параметра fdwReason, то он зависит от причины, по которой произошел вызов функции DLLEntryPoint. Этот параметр может принимать следующие значения:
Параметр lpvReserved зарезервирован. В SDK, тем не менее, сказано, что значение параметра lpvReserved равно NULL во всех случаях, кроме двух следующих: · когда параметр fdwReason равен DLL_PROCESS_ATTACH и используется статическая загрузка DLL-библиотеки; · когда параметр fdwReason равен DLL_PROCESS_DETACH и функция DLLEntryPoint вызвана в результате завершения процесса, а не вызова функции FreeLibrary В процессе инициализации функция DLLEntryPoint может отменить загрузку DLL-библиотеки. Если код причины вызова равен DLL_PROCESS_ATTACH, функция DLLEntryPoint отменяет загрузку библиотеки, возвращая значение FALSE. Если же инициализация выполнена успешно, функция должна возвратить значение TRUE. В том случае, когда приложение пыталось загрузить DLL-библиотеку функцией LoadLibrary, а функция DLLEntryPoint отменила загрузку, функция LoadLibrary возвратит значение NULL. Если же приложение выполняет инициализацию DLL-библиотеки неявно, при отмене загрузки библиотеки приложение также не будет загружено для выполнения. Приведем пример функции инициализации DLL-библиотеки: BOOL WINAPI DLLEntryPoint( HMODULE hModule, // идентификатор модуля DWORD fdwReason, // причина вызова функции DLLEntryPoint LPVOID lpvReserved)// зарезервировано { switch(fdwReason) { // Подключение нового процесса case DLL_PROCESS_ATTACH: { // Обработка подключения процесса . . . break; } // Подключение новой задачи case DLL_THREAD_ATTACH: { // Обработка подключения новой задачи . . . break; } // Отключение процесса case DLL_PROCESS_DETACH: { // Обработка отключения процесса . . . break; } // Отключение задачи case DLL_THREAD_DETACH: { // Обработка отключения задачи . . . break; } } return TRUE; } Экспортирование функций и глобальных переменныхХотя существуют DLL-библиотеки без исполнимого кода и предназначенные для хранения ресурсов, подавляющее большинство DLL-библиотек экспортируют функции для их совместного использования несколькими приложениям. Кроме функций LibMain и WEP в 16-разрядных DLL-библиотеках операционной системы Microsoft Windows версии 3.1 и функции DLLEntryPoint в 32-разрядных библиотеках операционных систем Microsoft Windows NT и Microsoft Windows 95 могут быть определены экспортируемые и неэкспортируемые функции. Экспортируемые функции доступны для вызова приложениям Windows. Неэкспортируемые являются локальными для DLL-библиотеки, они доступны только для функций библиотеки. При необходимости вы можете экспортировать из 32-разрядных DLL-библиотек не только функции, но и глобальные переменные. Самый простой способ сделать функцию экспортируемой - перечислить все экспортируемые функции в файле определения модуля при помощи оператора EXPORTSXE: EXPORTS ИмяТочкиВхода [=ВнутрИмя] [@Номер] [NONAME] [CONSTANT] . . . Здесь ИмяТочкиВхода задает имя, под которым экспортируемая из DLL-библиотеки функция будет доступна для вызова. Внутри DLL-библиотеки эта функция может иметь другое имя. В этом случае необходимо указать ее внутреннее имя ВнутрИмя. С помощью параметра @Номер вы можете задать порядковый номер экспортируемой функции. Если вы не укажите порядковые номера экспортируемых функций, при компоновке загрузочного файла DLL-библиотеки редактор связи создаст свою собственную нумерацию, которая может изменяться при внесении изменений в исходные тексты функций и последующей повторной компоновке. Заметим, что ссылка на экспортируемую функцию может выполняться двумя различными способами - по имени функции и по ее порядковому номеру. Если функция вызывается по имени, ее порядковый номер не имеет значения. Однако вызов функции по порядковому номеру выполняется быстрее, поэтому использование порядковых номеров предпочтительнее. Если при помощи файла определения модуля DLL-библиотеки вы задаете фиксированное распределение порядковых номеров экспортируемых функций, при внесении изменений в исходные тексты DLL-библиотеки это распределение не изменится. В этом случае все приложения, ссылающиеся на функции из этой библиотеки по их порядковым номерам, будут работать правильно. Если же вы не определили порядковые номера функций, у приложений могут возникнуть проблемы с правильной адресацией функции из-за возможного изменения этих номеров. Указав флаг NONAME и порядковый номер, вы сделаете имя экспортируемой функции невидимым. При этом экспортируемую функцию можно будет вызвать только по порядковому номеру, так как имя такой функции не попадет в таблицу экспортируемых имен DLL-библиотеки. Флаг CONSTANT позволяет экспортировать из DLL-библиотеки не только функции, но и данные. При этом параметр ИмяТочкиВхода задает имя экспортируемой глобальной переменной, определенной в DLL-библиотеке. Приведем пример экспортирования функций и глобальных переменных из DLL-библиотеки: EXPORTS DrawBitmap=MyDraw @4 ShowAll HideAll MyPoolPtr @5 CONSTANT GetMyPool @8 NONAME FreeMyPool @9 NONAME В приведенном выше примере в разделе EXPORTS перечислены имена нескольких экспортируемых функций DrawBitmap, ShowAll, HideAll, GetMyPool, FreeMyPool и глобальной переменной MyPoolPtr. Функция MyDraw, определенная в DLL-библиотеке, экспортируется под именем DrawBitmap. Она также доступна под номером 4. Функции ShowAll и HideAll экспортируются под своими “настоящими” именами, с которыми они определены в DLL-библиотеке. Для них не заданы порядковые номера. Функции GetMyPool и FreeMyPool экспортируются с флагом NONAME, поэтому к ним можно обращаться только по их порядковым номерам, которые равны, соответственно, 8 и 9. Имя MyPoolPtr экспортируется с флагом CONSTANT, поэтому оно является именем глобальной переменной, определенной в DLL-библиотеке, и доступной для приложений, загружающих эту библиотеку. Импортирование функцийКогда вы используете статическую компоновку, то включаете в файл проекта приложения соответствующий lib-файл, содержащий нужную вам библиотеку объектных модулей. Такая библиотека содержит исполняемый код модулей, который на этапе статической компоновки включается в exe-файл загрузочного модуля. Если используется динамическая компоновка, в загрузочный exe-файл приложения записывается не исполнимый код функций, а ссылка на соответствующую DLL-библиотеку и функцию внутри нее. Как мы уже говорили, эта ссылка может быть организована с использованием либо имени функции, либо ее порядкового номера в DLL-библиотеке. Откуда при компоновке приложения редактор связей узнает имя DLL-библиотеки, имя или порядковый номер экспортируемой функции? Для динамической компоновки функции из DLL-библиотеки можно использовать различные способы. Библиотека импортаДля того чтобы редактор связей мог создать ссылку, в файл проекта приложения вы должны включить так называемую библиотеку импорта (import library). Эта библиотека создается автоматически системой разработки Microsoft Visual C++. Следует заметить, что стандартные библиотеки систем разработки приложений Windows содержат как обычные объектные модули, предназначенные для статической компоновки, так и ссылки на различные стандартные DLL-библиотеки, экспортирующие функции программного интерфейса операционной системы Windows. Динамический импорт функций во время выполнения приложенияВ некоторых случаях невозможно выполнить динамическую компоновку на этапе редактирования. Вы можете, например, создать приложение, которое состоит из основного модуля и дополнительных, реализованных в виде DLL-библиотек. Состав этих дополнительных модулей и имена файлов, содержащих DLL-библиотеки, может изменяться, при этом в приложение могут добавляться новые возможности. Если вы, например, разрабатываете систему распознавания речи, то можете сделать ее в виде основного приложения и набора DLL-библиотек, по одной библиотеке для каждого национального языка. В продажу система может поступить в комплекте с одной или двумя библиотеками, но в дальнейшем пользователь сможет купить дополнительные библиотеки и, просто переписав новые библиотеки на диск, получить возможность работы с другими языками. При этом основной модуль приложения не может "знать" заранее имена файлов дополнительных DLL-библиотек, поэтому статическая компоновка с использованием библиотеки импорта невозможна. Однако приложение может в любой момент времени загрузить любую DLL-библиотеку, вызвав специально предназначенную для этого функцию программного интерфейса Windows с именем LoadLibrary. Приведем ее прототип: HINSTANCE WINAPI LoadLibrary(LPCSTR lpszLibFileName); Параметр функции является указателем на текстовую строку, закрытую двоичным нулем. В эту строку перед вызовом функции следует записать путь к файлу DLL-библиотеки или имя этого файла. Если путь к файлу не указан, при поиске выполняется последовательный просмотр следующих каталогов: · каталог, из которого запущено приложение; · текущий каталог; · 32-разрядный системный каталог Microsoft Windows NT; · 16-разрядный системный каталог; · каталог в котором находится операционная система Windows NT; · каталоги, перечисленные в переменной описания среды PATH Если файл DLL-библиотеки найден, функция LoadLibrary возвращает идентификатор модуля библиотеки. В противном случае возвращается значение NULL. При этом код ошибки можно получить при помощи функции GetLastError. Функция LoadLibrary может быть вызвана разными приложениями для одной и той же DLL-библиотеки несколько раз. В этом случае в среде операционной системы Microsoft Windows версии 3.1 загрузка DLL-библиотеки выполняется только один раз. Последующие вызовы функции LoadLibrary приводят только к увеличению счетчика использования DLL-библиотеки. Что же касается Microsoft Windows NT, то при многократном вызове функции LoadLibrary различными процессами функция инициализации DLL-библиотеки получает несколько раз управление с кодом причины вызова, равным значению DLL_PROCESS_ATTACH. В качестве примера приведем фрагмент исходного текста приложения, загружающего DLL-библиотеку из файла DLLDEMO.DLL: typedef HWND (WINAPI *MYDLLPROC)(LPSTR); MYDLLPROC GetAppWindow; HANDLE hDLL; hDLL = LoadLibrary("DLLDEMO.DLL"); if(hDLL != NULL) { GetAppWindow = (MYDLLPROC)GetProcAddress(hDLL, "FindApplicationWindow"); if(GetAppWindow != NULL) { if(GetAppWindow(szWindowTitle) != NULL) MessageBox(NULL, "Application window was found", szAppTitle, MB_OK | MB_ICONINFORMATION); else MessageBox(NULL, "Application window was not found", szAppTitle, MB_OK | MB_ICONINFORMATION); } FreeLibrary(hDLL); } Здесь вначале с помощью функции LoadLibrary выполняется попытка загрузки DLL-библиотеки DLLDEMO.DLL. В случае успеха приложение получает адрес точки входа для функции с именем FindApplicationWindow, для чего используется функция GetProcAddress XE "GetProcAddress" . Этой функцией мы займемся немного позже. Если точка входа получена, функция вызывается через указатель GetAppWindow. После использования DLL-библиотека освобождается при помощи функции FreeLibrary, прототип который показан ниже: void WINAPI FreeLibrary(HINSTANCE hLibrary); В качестве параметра этой функции следует передать идентификатор освобождаемой библиотеки. При освобождении DLL-библиотеки ее счетчик использования уменьшается. Если этот счетчик становится равным нулю (что происходит, когда все приложения, работавшие с библиотекой, освободили ее или завершили свою работу), DLL-библиотека выгружается из памяти. Каждый раз при освобождении DLL-библиотеки вызывается функция DLLEntryPoint с параметрами DLL_PROCESS_DETACH или DLL_THREAD_DETACH, выполняющая все необходимые завершающие действия. Теперь о функции GetProcAddress. Для того чтобы вызвать функцию из библиотеки, зная ее идентификатор, необходимо получить значение дальнего указателя на эту функцию, вызвав функцию GetProcAddress: FARPROC WINAPI GetProcAddress(HINSTANCE hLibrary, LPCSTR lpszProcName); Через параметр hLibrary вы должны передать функции идентификатор DLL-библиотеки, полученный ранее от функции LoadLibrary. Параметр lpszProcName является дальним указателем на строку, содержащую имя функции или ее порядковый номер, преобразованный макрокомандой MAKEINTRESOURCE. Приведем фрагмент кода, в котором определяются адреса двух функций. В первом случае используется имя функции, а во втором - ее порядковый номер: FARPROC lpMsg; FARPROC lpTellMe; lpMsg = GetProcAddress(hLib, "Msg"); lpTellMe = GetProcAddress(hLib, MAKEINTRESOURCE(8)); Перед тем как передать управление функции по полученному адресу, следует убедиться в том, что этот адрес не равен NULL: if(lpMsg != (FARPROC)NULL) { (*lpMsg)((LPSTR)"My message"); } Для того чтобы включить механизм проверки типов передаваемых параметров, вы можете определить свой тип - указатель на функцию, и затем использовать его для преобразования типа адреса, полученного от функции GetProcAddress: typedef int (PASCAL *LPGETZ)(int x, int y); LPGETZ lpGetZ; lpGetZ = (LPGETZ)GetProcAddress(hLib, "GetZ"); А что произойдет, если приложение при помощи функции LoadLibrary попытается загрузить DLL-библиотеку, которой нет на диске? В этом случае операционная система Microsoft Windows NT выведет на экран диалоговую панель с сообщением о том, что она не может найти нужную DLL-библиотеку. В некоторых случаях появление такого сообщения нежелательно, так как либо вас не устраивает внешний вид этой диалоговой панели, либо по логике работы вашего приложения описанная ситуация является нормальной. Для того чтобы отключить режим вывода диалоговой панели с сообщением о невозможности загрузки DLL-библиотеки, вы можете использовать функцию SetErrorMode, передав ей в качестве параметра значение SEM_FAILCRITICALERRORS: UINT nPrevErrorMode; nPrevErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS); hDLL = LoadLibrary("DLLDEMO.DLL"); if(hDLL != NULL) { // Работа с DLL-библиотекой . . . } SetErrorMode(nPrevErrorMode); Приведем прототип функции SetErrorMode: UINT WINAPI SetErrorMode(UINT fuErrorMode); Эта функция позволяет отключать встроенный в Windows обработчик критических ошибок. В качестве параметра этой функции можно указывать комбинацию следующих значений:
Функция SetErrorMode возвращает предыдущий режим обработки ошибки. Файл определения модуля для DLL-библиотекиФайл определения модуля для DLL-библиотекиотличается от соответствующего файла обычного приложения Windows. В качестве примера приведем образец такого файла: LIBRARY DLLNAME DESCRIPTION 'DLL-библиотека DLLNAME' EXPORTS DrawBitmap=MyDraw @4 ShowAll HideAll MyPoolPtr @5 CONSTANT GetMyPool @8 NONAME FreeMyPool @9 NONAME В файле определения модуля DLL-библиотеки вместо оператора NAME должен находиться оператор LIBRARYXE , определяющий имя модуля DLL-библиотеки, под которым она будет известна Windows. Однако формат строк файла описания модуля зависит от используемой системы разработки. Например, если вы создаете DLL-библиотеку при помощи Microsoft Visual C++ версии 4.0, оператор LIBRARY можно не указывать. Анализ DLL-библиотек при помощи программы dumpbin.exeВ комплекте системы разработки Microsoft Visual C++ входит программа dumpbin.exe, предназначенная для запуска из командной строки. С помощью этой утилиты вы сможете проанализировать содержимое любого загрузочного файла в формате COFF, в том числе DLL-библиотеки, определив имена экспортируемых функций, их порядковые номера, имена DLL-библиотек и номера функций, импортируемых из этих библиотек и т. д. Можно даже дизассемблировать секции кода с использованием таблицы символов, если такая имеется в файле. Выберем для исследования DLL-библиотеку comdlg32.dll, в которой находятся функции для работы со стандартными диалоговыми панелями. Вначале запустим программу dumpbin.exe, передав ей в качестве параметра имя DLL-библиотеки: c:\msdev\bin>dumpbin comdg32.dll > lst.txt Перед запуском программы dumpbin.exe мы скопировали файл comdg32.dll в каталог c:\msdev\bin. Программа запишет в файл lst.txt информацию о типе файла (DLL-библиотека) и перечислит названия секций и их размер, как это показано ниже: Microsoft (R) COFF Binary File Dumper Version 3.10.6038 Copyright (C) Microsoft Corp 1992-1996. All rights reserved. Dump of file comdlg32.dll File Type: DLL Summary 4000 .data 1000 .edata 2000 .rdata 2000 .reloc 9000 .rsrc 17000 .text Ниже мы перечислили названия некоторых стандартных секций (полное описание вы найдете в документации, которая поставляется в составе использованного вами средства разработки приложений для Microsoft Windows NT):
Для просмотра более подробной информации о секции следует воспользоваться параметром /SECTION: c:\msdev\bin>dumpbin comdg32.dll /SECTION:.data > lst.txt Результат выполнения этой команды показан ниже: Microsoft (R) COFF Binary File Dumper Version 3.10.6038 Copyright (C) Microsoft Corp 1992-1996. All rights reserved. Dump of file comdlg32.dll File Type: DLL SECTION HEADER #3 .data name 35E4 virtual size 1A000 virtual address E00 size of raw data 18C00 file pointer to raw data 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers C8000040 flags Initialized Data Not Paged (no align specified) Read Write Summary 4000 .data Аналогичная информация о секции .text приведена ниже: Microsoft (R) COFF Binary File Dumper Version 3.10.6038 Copyright (C) Microsoft Corp 1992-1996. All rights reserved. Dump of file comdlg32.dll File Type: DLL SECTION HEADER #1 .text name 16FF9 virtual size 1000 virtual address 17000 size of raw data 400 file pointer to raw data 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers 68000020 flags Code Not Paged (no align specified) Execute Read Summary 17000 .text Для просмотра списка имен экспортируемых функций и глобальных переменных запустите программу dumpbin.exe с параметром /EXPORTS: c:\msdev\bin>dumpbin comdg32.dll /EXPORTS > lst.txt Список появится в следующем виде: Microsoft (R) COFF Binary File Dumper Version 3.10.6038 Copyright (C) Microsoft Corp 1992-1996. All rights reserved. Dump of file comdlg32.dll File Type: DLL Section contains the following Exports for comdlg32.dll 0 characteristics 30D0D8ED time date stamp Fri Dec 15 05:09:49 1995 0.00 version 1 ordinal base 23 number of functions 23 number of names ordinal hint name 1 0 ChooseColorA (00012442) 2 1 ChooseColorW (0001164F) 3 2 ChooseFontA (00009235) 4 3 ChooseFontW (00014028) 5 4 CommDlgExtendedError (0000F92A) 6 5 FindTextA (0001373A) 7 6 FindTextW (0001372A) 8 7 GetFileTitleA (0000FB57) 9 8 GetFileTitleW (0000FAF1) 10 9 GetOpenFileNameA (00004952) 11 A GetOpenFileNameW (0000F9EE) 12 B GetSaveFileNameA (0000493B) 13 C GetSaveFileNameW (0000FA37) 14 D LoadAlterBitmap (0000A450) 15 E PageSetupDlgA (00014277) 16 F PageSetupDlgW (00014373) 17 10 PrintDlgA (0000738E) 18 11 PrintDlgW (00014238) 19 12 ReplaceTextA (0001375A) 20 13 ReplaceTextW (0001374A) 21 14 WantArrows (000122D5) 22 15 dwLBSubclass (000018F7) 23 16 dwOKSubclass (000018C4) Summary 4000 .data 1000 .edata 2000 .rdata 2000 .reloc 9000 .rsrc 17000 .text Наряду с именами функций здесь отображаются порядковые номера функций и их адреса (в скобках). Указав параметр /DISASM, вы можете дизассемблировать секцию кода, однако полученный в результате листинг может оказаться слишком большим и трудным для анализа. Вот фрагмент такого листинга: Microsoft (R) COFF Binary File Dumper Version 3.10.6038 Copyright (C) Microsoft Corp 1992-1996. All rights reserved. Dump of file comdlg32.dll File Type: DLL 77DF1000: 83 3D EC C4 E0 77 cmp dword ptr ds:[77E0C4ECh],0 00 77DF1007: 56 push esi 77DF1008: 75 2D jne 77DF1037 77DF100A: 83 3D 8C C7 E0 77 cmp dword ptr ds:[77E0C78Ch],0 00 77DF1011: 8B 74 24 08 mov esi,dword ptr [esp+8] 77DF1015: 0F 84 93 97 00 00 je 77DFA7AE 77DF101B: 83 FE 01 cmp esi,1 77DF101E: 1B C0 sbb eax,eax 77DF1020: 24 FE and al,0FEh 77DF1022: 05 02 7F 00 00 add eax,7F02h 77DF1027: 50 push eax 77DF1028: 6A 00 push 0 В заключение этого раздела приведем список параметров программы dumpbin.exe.
|