DLL с использованием __stdcall без украшения имени: почему он работает?

Если я объявляю такую ​​функцию:

#ifdef TEST_EXPORTS
#define TEST_API __declspec(dllexport)
#else
#define TEST_API __declspec(dllimport)
#endif

TESTAPI int __stdcall myadd(int a, int b);

Символ в DLL [email protected], что имеет для меня смысл (после нескольких часов чтения здесь других вопросов).

Но библиотеки окон, похоже, делают что-то другое. Они также используют __stdcall (замаскированы под WINAPI), но символы в библиотеках DLL не имеют названия. Если метод выше, где в библиотеках windows, символ будет myadd.

Я предполагаю, что они используют файл def для псевдонима символов. Но почему мой линкер знает об этом, когда я связываюсь с одной из этих DLL?

Файлы заголовков Windows объявляют эти функции с помощью WINAPI, поэтому, если я их назову, компоновщик должен искать декорированное имя, так как это функция __stdcall. Однако каким-то образом компоновщик знает, как отказаться от названия.

Я попытался воспроизвести это, написав небольшую DLL и удалив украшение имен с помощью файла def. Как и ожидалось, я получаю ошибки компоновщика, поскольку компоновщик все еще ищет украшенное имя. Я сделал это в чистом C, чтобы убедиться, что С++ name mangling не влияет на него.

изменить: уточнить, MSVC 14.0/VS2015, 32-разрядный

Ответ 1

Здесь есть мало документированная магия. Давайте посмотрим на функцию WIN32 API, например RegQueryValueExW. Он определен в файле winreg.h следующим образом:

WINADVAPI LSTATUS APIENTRY RegQueryValueExW(...);

Где WIADVAPI является __declspec(dllimport), а APIENTRY является прозвищем для соглашения об именах __stdcall. Также обратите внимание, что все функции в заголовке объявлены как extern "C". Поэтому, во что бы то ни стало, эта функция должна использовать украшение имен, а ее экспорт DLL должен быть [email protected]. Тем не менее, когда мы смотрим advapi32.dll экспорт с использованием команды dumpbin /exports, мы видим недекорированное имя:

advapi exports

Теперь внимательно рассмотрим файл advapi32.lib с помощью команды dumpbin /headers advapi32.lib:

введите описание изображения здесь

Обратите внимание на спецификатор undecorate, который позволяет связать украшенное имя с неэкспорированным экспортом. Вы можете добиться того же результата для вашей DLL, используя файл def с разделом EXPORTS, содержащий неадресные имена. Для получения дополнительной информации см. эту статью и этот ответ.

Кроме того, все написанное выше допустимо только для приложений x86. Функции C в x64-битовой среде связаны без украшения имени:

Форма украшения для функции C зависит от вызывающего которое используется в его декларации, как показано в следующей таблице. Это также формат оформления, который используется, когда код С++ заявлено, что имеет внешнюю связь "С". Соглашение о вызове по умолчанию __cdecl. Обратите внимание, что в 64-битной среде функции не оформлены.