То есть, у функции, экспортируемой как _SumFunc, будет
псевдоним SumFunc, который мы исключительно для удобства делаем идентичным
оригинальному имени этой функции в коде (хотя псевдоним может быть каким
угодно).
Созданный .def-файл добавляется (Project -> Add to
Project) к проекту dll. После компиляции, проанализировав dll c помощью
impdef.exe, получим следующее
ExplicitDll.def
libRARY EXPLICITDLL.DLL
EXPORTS
SumFunc @4 ; SumFunc
ViewStringGridWnd @2 ;
ViewStringGridWnd
_SumFunc @1 ; _SumFunc
___CPPdebugHook @3 ;
___CPPdebugHook
Имеем на одну экспортируемую функцию больше, но при
этом реальное количество функций в dll осталось неизменным, а функция с именем
SumFunc (функция-псевдоним) является ссылкой на свой оригинал, то есть на
функцию, экспортируемую под именем _SumFunc.
ПРИМЕЧАНИЕ
Более правильным будет сказать, что
функция-псевдоним попросту добавляется в таблицу экспорта dll: ее имя SumFunc
добавляется в массив имен функций, а в массив порядковых номеров добавляется
присвоенный ей порядковый номер. Однако соответствующий функции-псевдониму
RVA в массиве относительных виртуальных адресов будет равен RVA функции с
именем _SumFunc. Убедиться в этом можно последовательно вызывая
GetProcAddress для имен функций SumFunc и _SumFunc и анализируя возвращаемый
адрес (можно, разумеется, воспользоваться различными программами, позволяющими просмотреть содержимое исполняемого файла). В обоих случаях
адрес функции будет одинаков.
Таким образом, с помощью .def-файла псевдонимов при
экспорте функций, определенных как __cdecl, мы избавляем пользователей от
необходимости вызова функций по их измененным именам, хотя и такая возможность
остается.
ПРЕДУПРЕЖДЕНИЕ
Поскольку __stdcall- и __cdecl-функции
по-разному работают со стеком, не пытайтесь из клиентского приложения
вызывать __stdcall-функции как __cdecl, и наоборот, иначе стек будет
поврежден, и дальнейшее выполнение приложения будет невозможно.
В результате изложенного выше мы получили dll, экспортирующую функции с именами SumFunc и ViewStringGridWnd. При этом их
названия не зависят от того, какое соглашение о вызове использовалось при
объявлении этих функций. Теперь рассмотрим пример использования нашей dll в
приложении VC. Создадим в среде Visual C++ 6.0 (или Visual C++ 7.0) простое
MFC-приложение, которое будет представлять собой обычное диалоговое окно (File
-> New -> MFC AppWizard(exe) -> Dialog based -> Finish). Добавим к
исходному диалогу две кнопки: кнопку “SumFunc” и кнопку “ViewStringGridWnd”.
Затем для каждой кнопки создадим обработчик события BN_CLICKED: OnSumFunc() и
OnViewStringGridWnd() соответственно. Нам также понадобятся обработчики
сообщений для событий формы WM_CREATE и WM_DESTROY. Полный рабочий код этого
приложения находится в примерах к статье, здесь же будет приведена только
часть, демонстрирующая работу с нашей dll, поскольку оставшаяся часть кода
генерируется средой разработки.