Я считаю, что использование обоих __declspec (dllexport) и файла .def может быть полезным при создании переносимых библиотек DLL, то есть библиотек DLL, которые можно вызывать из кода, скомпилированного с помощью другого компилятора или с другими настройками компилятора.
Простое добавление __declspec (dllexport) в ваши объявления функций приведет к тому, что эти функции будут «экспортированы» вашей DLL (по крайней мере, в Windows), чтобы их можно было вызывать извне DLL.
Однако добавление в сборку файла .def, в котором перечислены все ваши экспортируемые функции, позволяет запретить компиляторам Microsoft (например) добавлять начальную подчеркивание и конечную информацию о ширине параметра к имени экспортируемой функции (по крайней мере, при объединении). с директивой __stdcall, также полезной для переносимости). Например. объявление функции
void foo(int i);
может закончиться экспортом как "_foo @ 4", если вы не будете осторожны с соглашением о вызовах и использованием файла .def.
Сохранение экспортированных имен функций в таблице символов без таких декораций имен становится очень удобным при выполнении вызовов GetProcAddress () как части загрузки и явного подключения к DLL во время выполнения. т.е. чтобы получить указатель на указанную выше функцию foo () (при условии, что она вообще была экспортирована) во время выполнения, в идеале вы просто хотите вызвать:
HANDLE dllHandle = LoadLibrary("mydll.dll");
void* fooFcnPtr = GetProcAddress(dllHandle, "foo");
С некоторой соответствующей проверкой ошибок, конечно!
Использование файла .def плюс __stdcall, __declspec (dllexport) и extern "C" в ваших объявлениях функций при сборке DLL обеспечит работу вышеуказанного клиентского кода для широкого диапазона компиляторов и настроек компилятора.