#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)
потому, что экспортирует оформленные имена. Существует не так много информации о том, что ожидает сторонняя программа, но я предполагаю, что она выполняет aGetProcAddress
для имени, которое вы предоставляете, поэтому вам нужно создать свою 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 будет помечать функции для экспорта, в то время как другие проекты будут помечать их для импорта.