Экспортирование и импортирование интерфейсов
Самое значительное изменение, которое требуется внести в функцию, прежде чем ее можно будет поместить в DLL, — это объявить ее экспортируемой (UNIX и некоторые другие системы не требуют явного выполнения этого шага). Это достигается либо за счет использования .DEF-файла, либо, что проще и возможно в Microsoft С, за счет использования в объявлениях модификатора _declspec (dllexport) следующим образом:
_declspec(dllexport) DWORD MyFunction (…);
Далее в процессе сборки создаются .DLL-файл и .LIB-файл. .LIB-файл — это библиотека-заглушка, которая должна быть скомпонована с вызывающей программой для разрешения внешних ссылок и создания актуальных связей с . DLL-файлом во время загрузки.
Вызывающая, или клиентская, программа должна объявить о том, что функцию следует импортировать, используя для этого модификатор _declspec (dllexport). Стандартный метод заключается в создании включаемого файла, использующего переменную препроцессора, имя которой формируется на основе имени проекта Microsoft Visual C++, набранного в верхнем регистре и дополненного суффиксом _EXPORTS.
Вам также может потребоваться еще одно объявление. Если вызывающая (клиентская) программа написана на C++, то для нее будет определена переменная препроцессора __cplusplus, и вы должны будете указать на необходимость использования системы соглашений о вызовах, принятой в С, с помощью следующего выражения:
extern "С"
Если, например, в качестве части сборки DLL в проекте MyLibrary определена функция MyLibrary, то содержимое заголовочного файла должно быть таким:
#if defined(MYLIBRARY_EXPORTS)
#define LIBSPEC _declspec(dllexport)
#elif defined(__cplusplus)
#define LIBSPEC extern "C" _declspec(dllimport)
#else
#define LIBSPEC _declspec(dllimport)
#endif
LIBSPEC DWORD MyFunction (…);
Visual C++ автоматически определяет MYLIBRARY_EXPORTS при вызове компилятора, если проект предназначен для создания DLL MyLibrary. Клиентский проект, который использует DLL, переменную MYLIBRARYEXPORTS не определяет, и поэтому имя функции импортируется из библиотеки.
При построении вызывающей программы укажите соответствующий .DLL-файл. Когда будете запускать вызывающую программу на выполнение, убедитесь в наличии доступа к этому файлу; часто это обеспечивается размещением .DLL-файла в одном каталоге с исполняемым файлом. Как ранее уже отмечалось, существует ряд правил поиска DLL, определяющих последовательность просмотра каталогов, в которых Windows будет осуществлять поиск указанного .DLL-файла, а также других DLL и исполняемых файлов, необходимых указанному файлу, прекращая этот поиск, как только будет найден первый подходящий экземпляр. Ниже приведена стандартная последовательность просмотра каталогов при поиске,используемая как в случае явного, так и в случае неявного связывания.
• Каталог, в котором находится загружаемое приложение.
• Текущий каталог, если он отличен от каталога, содержащего исполняемый модуль.
• Системный каталог Windows. Вы можете определить этот путь, вызвав функцию GetSystemDirectory; таковым обычно является каталог с:\WINDOWS\SYSTEM32.
• Системный каталог 16-разрядной Windows, который отсутствует в системах Windows 9x. Функция, позволяющая получить путь доступа к этому каталогу, отсутствует, и для наших целей он оказывается ненужным.
• Каталог Windows (используйте функцию GetWindowsDirectory).
• Каталоги, перечисленные в переменной окружения PATH, которые будут просматриваться в той последовательности, в какой они указаны.
Заметьте, что этот стандартный порядок просмотра каталогов при поиске можно изменить, о чем говорится в разделе "Явное связывание". Для получения более подробной информации относительно стратегии поиска посетите Web-сайт по адресу http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/loadlibrary.asp, а также ознакомьтесь с описанием функции SetDllDirectory, введенной в Windows NT 5.1 (то есть Windows XP). Изменить стратегию поиска позволяет также функция LoadLibraryEx, описанная в следующем разделе.
Применение стандартной стратегии поиска иллюстрируется в проекте Utilities, доступном на Web-сайте книги, а такие вспомогательные функции, как ReportError, используются почти в каждом примере проектов.
Возможно также экспортирование и импортирование переменных, а также точек входа функций, хотя эти возможности в примерах не иллюстрируются.