Студопедия

КАТЕГОРИИ:

АвтоАвтоматизацияАрхитектураАстрономияАудитБиологияБухгалтерияВоенное делоГенетикаГеографияГеологияГосударствоДомЖурналистика и СМИИзобретательствоИностранные языкиИнформатикаИскусствоИсторияКомпьютерыКулинарияКультураЛексикологияЛитератураЛогикаМаркетингМатематикаМашиностроениеМедицинаМенеджментМеталлы и СваркаМеханикаМузыкаНаселениеОбразованиеОхрана безопасности жизниОхрана ТрудаПедагогикаПолитикаПравоПриборостроениеПрограммированиеПроизводствоПромышленностьПсихологияРадиоРегилияСвязьСоциологияСпортСтандартизацияСтроительствоТехнологииТорговляТуризмФизикаФизиологияФилософияФинансыХимияХозяйствоЦеннообразованиеЧерчениеЭкологияЭконометрикаЭкономикаЭлектроникаЮриспунденкция

Связывание по символьным именам против связывания по порядковым номерам




Связывание по порядковым номерам в Win16 было эффективнее и предлагалось по умолчанию. В Win32 связывание по символьным именам усовершенствовано, и теперь Microsoft рекомендует использовать именно его. Однако в DLL-версии библиотеки MFC применяется связывание по порядковым номерам. Дело в том, что типичная программа на базе MFC подключается к сотням функций этой библиотеки, и при связывании по порядковым номерам ЕХЕ-файл получается компактнее, поскольку отпадает необходимость хранить длинные символьные имена импортируемых элементов. Если Вы создаете DLL со связыванием по порядковым номерам, определите в DEF-файле проекта номера, которые не слишком часто используются в Win32. Если Вы экспортируете функции С++, то должны указывать в DEF-файле их расширенные имена (или объявить функции как “extern С”). Вот краткий фрагмент одного из DEF-файлов MFC-библиотеки:

?ReadList@CRecentFileList@@UAEXXZ @ 5458 NONAME

?ReadNameDictFromStream@CPropertySection@@QAEHPAUIStream@@@Z @ 5459 NONAME

?ReadObject@CArchive@@QAEPAVCObject@@PBUCRuntimeClass@@@Z @ 5460 NONAME

?ReadString@CArchive@@QAEHAAVCString@@@Z @ 5461 NONAME

?ReadString@CArchive@@QAEPADPADI@Z @ 5462 NONAME

?ReadString@CInternetFile@@UAEHAAVCString@@@Z @ 5463 NONAME

?ReadString@CInternetFile@@UAEPADPADI@Z @ 5464 NONAME

 

Числа после символов @ – это и есть порядковые номера. Ну что, Вы no-прежнему хотите использовать этот метод?

Точка входа в DLL: функция DllMain

По умолчанию компоновщик предполагает, что точкой входа в вашу DLL является функция _DllMaintCRTStartup(). Windows, загружая DLL, вызывает эту функцию, а та сначала вызывает конструкторы глобальных объектов, потом — глобальную функцию DllMain(), которую Вы, естественно, должны были написать. DIIMain() вызывается не только при подключении DLL к процессу, но и при отключении от него, а также в ряде других случаев. Взгляните на заготовку функции DllMain():

HINSTANCE g_hInstance;

extern "C" int APIENTRY

DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)

{

if (dwReason == DLL_PROCESS_ATTACH)

{

   TRACE0("EX22A.DLL Initializing!\n");

   // здесь реализуйте инициализацию

}

else if (dwReason == DLL_PROCESS_DETACH)

{

   TRACE0("EX22A.DLL Terminating!\n");

   // здесь реализуйте очистку

}

return 1; // ok

}

 

Если Вы не пишете функцию DllMain() для своей DLL, вместо нее подставляется заглушка из стандартной библиотеки выполнения.

Функция DllMain вызывается также при запуске и завершении отдельных потоков, что определяется параметром dwReason. Впрочем, эта тема исчерпывающе изложена в книге Джеффри Рихтера.

 

Описатели экземпляров и загрузка ресурсов

 

Каждая DLL в процессе идентифицируется уникальным 32-разрядным значением HINSTANCE. Сам процесс тоже имеет описатель HINSTANCE. Все эти описатели экземпляров действительны только в рамках конкретного процесса и представляют собой начальный виртуальный адрес DLL- или ЕХЕ-модуля. В Win32 значения описателей HINSTANCE и HMODULE совпадают и эти описатели равнозначны. Описатель экземпляра процесса почти всегда равен 0х400000, а у DLL с базовым адресом по умолчанию — 0х10000000. Если ваша программа использует несколько DLL, то у них будут разные значения HINSTANCE — либо потому, что в DLL указаны разные базовые адреса (определяется на этапе разработки), либо потому, что загрузчик скопировал и переместил код DLL.

Описатели экземпляров особенно важны при загрузке ресурсов. В частности, параметр HINSTANCE необходим Win32-функции FindResource(). В ЕХЕ- и DLL-модулях могут быть свои ресурсы. Если нужен ресурс из DLL, указывают описатель экземпляра DLL, а если из ЕХЕ – описатель экземпляра ЕХЕ.

Как же получить описатель экземпляра? Вы уже видели, что описатель экземпляра DLL передается как параметр в функцию DllMain(). Если Вы хотите выяснить описатель ЕХЕ-модуля, вызовите Win32-функцию GetModuleHandle(NULL). Если же Вам нужен описатель DLL, вызовите GetModuleHandle() с именем DLL в качестве параметра. Позднее мы рассмотрим еще один метод загрузки ресурсов, реализованный в MFC-библиотеке, основанный на просмотре модулей в определенной последовательности.

 

Порядок поиска DLL клиентской программой

 

Если Вы компонуете программу явным образом, с использованием LoadLibrary(), то можете указывать полное имя DLL (с определением пути). Если же полный путь не указан или при неявной компоновке Windows будет искать вашу DLL в следующем порядке:

1 В каталоге, содержащем данный ЕХЕ-файл.

2 В текущем каталоге процесса.

3 В системном каталоге Windows.

4 В каталоге Windows.

5 В каталогах, прописанных в переменной окружения PATH.

 

Здесь есть одна ловушка, в которую можно запросто угодить. Вы создаете DLL как отдельный проект, затем копируете DLL-файл в системный каталог Windows и, наконец, загружаете DLL в клиентскую программу. До сих пор все было прекрасно. Но вот Вы обновляете DLL, забываете скопировать ее в системный каталог и при следующем запуске клиентской программы загружается старая версия DLL. Что тут сказать — будьте внимательны!

Отладка DLL

Как, при необходимости, трассировать код библиотечных функций и, например, просмотреть значения переменных, если библиотека не может исполняться как самостоятельное приложение? Загрузите в ИС проект библиотеки RegDLL и попробуйте запустить отладку с помощью F5, предварительно установив контрольную точку в какой-либо библиотечной функции (это можно сделать и потом или использовать другие команды и приемы отладки). При первом таком запуске библиотеки на отладку вам будет показано окно с сообщением об ошибке: невозможно запустить библиотеку.

Выход на самом деле есть: надо для библиотеки указать вызывающий процесс и тогда отладчик запустит его и позволит отлаживать библиотеку. Последовательность действий такова.

1. Во вкладке Solution Explorer выберите проект библиотеки: в нашем случае это RegDLL.

2. В меню View выполите команду Property Pages.

3. В окне RegDLL Property Pages в выпадающем списке Configuration выберите Active(Debug) В папке Configuration Properties выберите категорию Debugging.

4. В списке Debugger to launch list выберите Local Windows Debugger.

5. Откройте выпадающий список Command, выберите в нем команду Browse и с ее помощью укажите расположение и имя вызывающего процесса, в нашем случае это Lab1.exe.

6. Закончите работу нажатием Применить и ОК.

 

Заметьте, что после выполнения этих настроек библиотеки ее надо вновь построить и вновь скопировать файл RegDLL.dll в папку с исполняемым файлом Lab1.exe.










Последнее изменение этой страницы: 2018-04-12; просмотров: 394.

stydopedya.ru не претендует на авторское право материалов, которые вылажены, но предоставляет бесплатный доступ к ним. В случае нарушения авторского права или персональных данных напишите сюда...