Как программа может вызывать недекорированный экспорт stdcall DLL при компиляции с MSVC? - PullRequest
0 голосов
/ 15 мая 2019

У меня есть DLL, написанная на ассемблере x86. Все экспорты относятся к stdcall функциям. Весь экспорт не отделан.

Мне нужно, чтобы эта DLL-библиотека была связана с проектом C с использованием библиотеки импорта. LoadLibrary или изменение имени экспорта и т. Д. Не вариант.

У меня 2 проблемы

  1. Когда я создаю файл библиотеки импорта (.lib) для DLL из файла DEF с именами экспорта, все имена оформляются на cdecl! Я не могу найти способ указать их как недекорированные stdcall. Ни одно из этих украшенных имен не существует в DLL, поэтому во время выполнения ничего не будет работать, даже если компиляция была возможна.
  2. Когда я пишу код для вызова функции DLL в MSVC, независимо от того, указывается ли stdcall или нет, компилятор изменяет имена функций. Поэтому вызов ExampleFunction становится _ExampleFunction@12, и мне нужно, чтобы он был только ExampleFunction.

К счастью, то, что мне нужно, ДОЛЖНО быть возможным, потому что системные библиотеки Windows (например, kernel32.dll, user32.dll и т. Д.) Следуют точно таким же соглашениям. Каждая экспортируемая функция является stdcall и не украшена.

Я уже читал это: https://qualapps.blogspot.com/2007/08/how-to-create-32-bit-import-libraries.html

Я столкнулся с этими проблемами:

  1. с использованием extern "C" здесь не применимо, так как это C-проект. Этот спецификатор даже не будет работать, и, несмотря на то, что это C-проект, имена все равно изменяются.
  2. Создание файла .lib из файла DEF только с необработанными именами функций не работает. Имена изменяются.

Файл example-lib.def выглядит следующим образом:

LIBRARY example-lib.dll
EXPORTS
ExampleFunction

Файл .lib создается с помощью этой команды:

lib.exe /def:example-lib.def /OUT:example-lib.lib

Запись в файле lib выглядит следующим образом при использовании dumpbin.exe /headers example-lib.lib

Version      : 0
Machine      : 14C (x86)
TimeDateStamp: 5CDBA30C Wed May 15 06:26:36 2019
SizeOfData   : 00000016
DLL name     : example-lib.dll
Symbol name  : _ExampleFunction
Type         : code
Name type    : no prefix
Hint         : 20
Name         : ExampleFunction

Я импортирую, делая декларации этого:

extern void __stdcall ExampleFunction(int a, int b, int c);

Я вызываю функцию так:

ExampleFunction(1, 2, 3);

Файл lib и каталог, в котором он находится, указываются в настройках проекта VC.

Когда код, вызывающий ExampleFunction, компилируется, компоновщик не завершит работу, поскольку он не может найти _ExampleFunction@12 в моем DEF-файле. И даже если это присутствует в файле DEF, программа не запустится, потому что _ExampleFunction@12 не экспортируется в фактический раздел экспорта DLL. Только простая ExampleFunction экспортируется.

Так как мне добиться, чтобы программа на языке C вызывала ExampleFunction из DLL в точности как есть? Как это сделать, когда проект C вызывает функцию из user32.dll? Могу ли я использовать ту же процедуру для достижения этой цели?

1 Ответ

0 голосов
/ 15 мая 2019

Оказывается, информация в ссылке на статью, которую я разместил выше, действительно работает. (https://qualapps.blogspot.com/2007/08/how-to-create-32-bit-import-libraries.html)

Я уже создал библиотеку-заглушку / заглушку, как упоминалось в статье ... Но я не добавил файл определения модуля в библиотеку-заглушку так, как мне следовало бы. Я пренебрег этим, но я даже не понимаю, почему это сработало! Я создал его вне Visual Studio и сгенерировал .lib для него вне Visual Studio. Неудачно. Делая все это в Visual Studio, все заработало.

Простое добавление определения модуля, которое выглядит следующим образом ...

LIBRARY example-lib.dll
EXPORTS
ExampleFunction

... заставляет выводимый файл .lib сообщать об этом вместо того, что я изначально перечислил в своем вопросе:

Version      : 0
Machine      : 14C (x86)
TimeDateStamp: FFFFFFFF
SizeOfData   : 00000019
DLL name     : example-lib.dll
Symbol name  : _ExampleFunction@12
Type         : code
Name type    : undecorate
Hint         : 0
Name         : ExampleFunction

Это очень странно! Как упоминается в статье, это по сути недокументированное поведение, но оно решает мою проблему.

Если у вас возникли проблемы с выполнением статьи, как и у меня, решение проблемы с наличием файла .lib с неокрашенным импортом stdcall заключается в следующем:

  1. Создайте новый проект Visual C DLL (не статическая библиотека), назовите его так же, как DLL, для которой вы хотите создать .lib.

  2. В основном файле исходного кода, который является файлом C ++ (не имеет значения, что это так), скопируйте подписи для требуемых функций, как показано ниже. Например, lib, это весь исходный файл!

    include "stdafx.h"
    extern "C" __declspec(dllexport) void _stdcall ExampleFunction(int a, int b, int c) { }
    
  3. Это ключевая часть. В обозревателе решений Visual C щелкните правой кнопкой мыши Исходные файлы> Добавить> Новый элемент ... В открывшемся диалоговом окне выберите Код> Файл определения модуля (.def). Сделайте так, чтобы он соответствовал вашему имени DLL и именам экспорта, как показано ниже Поскольку этот шаг не требует каких-либо изменений настроек для ссылки на определение модуля, я действительно не знаю, почему это работает. Но это так ... Кажется, официально не задокументировано, что это будет работать, но эта функциональность присутствует в MSVC по крайней мере с 2005-2007 гг. (Когда была написана статья).

    LIBRARY example-lib.dll
    EXPORTS
    ExampleFunction
    
  4. Установите режим выпуска и создайте библиотеку. Перейдите в каталог проекта в проводнике Windows, и вы должны увидеть файл .dll и .lib. Вы можете запустить dumpbin /headers example-lib.lib, чтобы убедиться, что ExampleFunction не украшен, как показано выше.

  5. Чтобы использовать фиктивный файл .lib для ссылки на настоящую DLL, добавьте его в исходный проект, выбрав «Свойства проекта»> «Линкер»> «Ввод»> «Дополнительные зависимости» (и добавьте каталог зависимостей, если необходимо). Затем объявите ссылку на него в следующем коде:

    extern void __stdcall ExampleFunction(int a, int b, int c);
    

Создайте свой проект, и он должен работать. Если вы просмотрите свой проект EXE / DLL в утилите, вы увидите, что в разделе .idata будет указана ссылка на вашу DLL с недокументированными именами функций. Это работает, потому что компилятор всегда генерирует ссылку на символ, такую ​​как _ExampleFunction@12, но когда он достигает компоновщика Microsoft в сочетании со специализированным .lib-файлом, он видит, что name type для этого undecorate, и он должен разрешить этот символ в фактическое имя ExampleFunction.

...