Как объявить DLL в user32.dll (он же stdcall?) мода?

#c #windows #winapi #dll #compiler-construction

#c #Windows #winapi #dll #компилятор-конструкция

Вопрос:

У меня есть стороннее приложение, которое позволяет вызывать функции C из DLL-файлов. Приведенный пример этого приложения показывает, что вы можете вызвать MessageBoxW из user32.dll . Это также позволяет вам вызывать функции C из ваших DLL-файлов.

Я создал DLL из файла.h файл, подобный этому:

 _declspec (dllexport) void example(int);
  

и file.c вот так:

 #include <stdio.h>
#include "file.h"

_declspec (dllexport) void example(int s1)
{
    printf("dsa");    
}
  

И скомпилируйте его с помощью компилятора C / C версии 15 из Windows SDK следующим образом:

 cl file.c /DLL /LD
  

И я получаю правильную компиляцию с файлом DLL. В экзаменаторе функций DLL я вижу свою функцию. Я помещаю этот файл в папку System32 и вызываю его из этого стороннего приложения.

Приложение находит файл, но не может найти функцию.

Я думаю, что причина проблемы в том, что я объявляю (или компилирую) свою DLL другим способом / способом / стандартом, который используют библиотеки Windows (например user32.dll ) потому что user32.dll работает нормально.

Я обнаружил, что стороннее приложение использует такие вызывающие функции в DLL:

winapi_abi Используется для вызова системных функций Windows. Они объявляются как stdcall в Windows, но не имеют искаженных имен.

Итак, мой вопрос: как подготовить и скомпилировать DLL-файл в user32.dll мода (stdcall?) значит, он будет работать со сторонним приложением?

Комментарии:

1. Я не понимаю, почему вы даже беспокоитесь обо всем этом дерьме «_declspec (dllexport)». Почему бы просто не удалить его; и скомпилировать и связать его обычным образом? То есть, что заставило вас принять этот бессмысленный вызов?

2. @Pete Как я уже писал, это стороннее приложение, и у меня нет особого выбора в отношении того, как оно вызывает функции, чтобы вы знали…. Я пробовал это с declspec, но это не работает.

3. И имя функции __stdcall по-прежнему искажается при экспорте. Начальный символ подчеркивания и завершающий символ @x, например _example@4. Использовать dumpbin.exe /экспортирует, чтобы увидеть это. Для переименования экспорта в обычный «экспорт» требуется файл .def.

4. @tomaszs и Hans — извините, я не понял причины. спасибо за объяснение.

5. Если pdb доступна, dumpbin может отображать исходное оформленное имя, а также экспортированное имя. Проверьте фактическое экспортируемое имя, а не просто «что-либо в выводе корзины, содержащее слово «пример»». Если фактическое экспортируемое имя _example@4, то я предполагаю, что вы не перестроили чисто.

Ответ №1:

Простой ответ:

 __declspec(dllexport) void __stdcall example(int);
  

И то же самое в реализации, конечно.

Если вы заглянете в windows.h и друзья, вы увидите:

 #define WINUSERAPI __declspec(dllexport)
#define WINAPI __stdcall
  

И затем:

 WINUSERAPI int WINAPI MessageBoxW(HWND,LPCWSTR,LPCWSTR,UINT);
  

Но если вы просто определяете несколько функций, в макросах нет необходимости.

Комментарии:

1. Я сделал это в этом режиме, и все равно это не работает. Есть ли там еще какие-то вещи? или, может быть, библиотеки DLL компилируются другим способом в Windows?

2. На самом деле WINUSERAPI не определен как __declspec(dllexport) потому, что экспортирует оформленные имена. Существует не так много информации о том, что ожидает сторонняя программа, но я предполагаю, что она выполняет a GetProcAddress для имени, которое вы предоставляете, поэтому вам нужно создать свою DLL с разделом ЭКСПОРТА, который дает точное имя, которое вы хотите экспортировать.

3. @Raymond Я думаю, что это может быть проблемой. Я добавил в пример экспорта файлов DEF, но когда я делаю dumbin / EXPORTS file. dll я вижу имя _example@4. Что я делаю не так? Мне нужна только эта одна функция.

4. я добавил решение в свой ответ, это в основном то, что вы написали удаление __stdcall и добавление файла def.

Ответ №2:

Включите файл .def, чтобы отменить оформление имени stdcall. Тогда вы можете избавиться от беспорядка __declspec (dllexport).

Комментарии:

1. Я пробовал этот подход. Я добавил EXPORTS example @1 в DEF и /DEF:file . def в параметрах компиляции и удалил declspec из файла. файлы h и file.c, но не после файла dumpbin / EXPORTS. dll я больше не вижу никакой функции (перед удалением _declspec я видел _example@4). Что я здесь делаю не так? Заранее спасибо.

2. я собрал решение в своем ответе, но вы мне очень помогли, спасибо

Ответ №3:

Вы не указываете никакого соглашения о вызовах в объявлении функции, поэтому компилятор по умолчанию __cdecl будет использовать значение , которое украшает экспортируемое имя. Кроме того, если вы компилируете на C вместо C, также происходит дополнительное оформление. Вам нужно сделать что-то вроде следующего в вашем коде, чтобы обойти все это:

file.h:

 #ifndef fileH
#define fileH

#ifdef _BUILDING_DLL_
#define MYEXPORT __declspec (dllexport)
#else
#define MYEXPORT __declspec (dllimport)
#endif

#ifdef __cplusplus
extern "C" {
#endif

MYEXPORT void __stdcall example(int);

#ifdef __cplusplus
}
#endif

#endif
  

file.c:

 #include <stdio.h>

#define _BUILDING_DLL_
#include "file.h"

void __stdcall example(int s1)
{
   printf("dsa");
}
  

Комментарии:

1. Извините, но я не понимаю, я делаю C DLL, а не C и строю DLL, поэтому BUILDING_DLL является true, а внешняя часть здесь не используется, так что … в чем разница между этим и моим кодом? Извините, если я что-то потерял, вы можете это написать? Когда я использую свой код в dumprep, у меня есть имя _example@4 — я полагаю, это нехорошо…

2. я добавил решение. Это было то, что вы написали добавление файла экспорта и удаление stdcall. Спасибо!

3. Добавление файла .def — это нормально, но удаление __stdcall — это неправильный поступок. Смотрите другие комментарии.

Ответ №4:

Спасибо вам всем. Решение состояло в том, чтобы добавить файл def с экспортом, включить его в компилятор с помощью /DEF:file.def , использовать Remy-версию (без #define _BUILDING … ) и удалить _stdcall .

Комментарии:

1. Если вы удалите stdcall , то получите cdecl . Но вы сказали, что вам нужно stdcall .

2. stdcall это больше, чем просто искажение имен. Если в документах так сказано, у вас должно быть это, и, более того, переименуйте символы, если это необходимо.

3. Цель _BUILDING_DLL_ состоит в том, чтобы разрешить использовать один и тот же файл .h как в DLL, так и в других проектах, которые хотят статически связываться с функциями DLL. Поскольку объявляется только DLL _BUILDING_DLL_ , файл .h будет помечать функции для экспорта, в то время как другие проекты будут помечать их для импорта.