С++ DLL Export: оформленные/измененные имена
Создал базовую С++ DLL и экспортировал имена, используя файл определения модуля (MyDLL.def).
После компиляции я проверяю имена экспортируемых функций, используя dumpbin.exe
Я ожидаю увидеть:
SomeFunction
но я вижу это вместо:
SomeFunction = [email protected]@@23mangledstuff#@@@@
Почему?
Экспортированная функция выглядит неэкранированной (особенно по сравнению с не использующей файл Defence модуля), но что с другими вещами?
Если я использую dumpbin.exe
для DLL из любого коммерческого приложения, вы получите чистый:
SomeFunction
и ничего больше...
Я также попытался удалить определение модуля и экспортировать имена, используя стиль экспорта "С", а именно:
extern "C" void __declspec(dllexport) SomeFunction();
(Просто использование "extern" C "не создало экспортированную функцию)
Однако это все равно создает тот же результат, а именно:
SomeFunction = [email protected]@@23mangledstuff#@@@@
Я также попробовал параметр #define dllexport __declspec(dllexport)
и создал LIB без проблем. Однако я не хочу предоставлять LIB файл людям, использующим DLL в своем приложении С#.
Это простая ванильная С++ DLL (неуправляемый код), скомпилированный с С++ ничего, кроме простого заголовка и кода. Без модуля Def Я получаю искаженные экспортированные функции (я могу создать статическую библиотеку и использовать LIB без проблем. Я пытаюсь это избежать). Если я использую extern "C" __declspec(dllexport)
ИЛИ определение модуля, я получаю то, что кажется непривязанным именем функции... единственная проблема заключается в том, что за ним следует "=" и что выглядит как украшенная версия функции. Я хочу избавиться от материала после "=" - или, по крайней мере, понять, почему он есть.
Как бы то ни было, я вполне уверен, что могу вызвать функцию из С# с помощью P/Invoke... Я просто хочу избежать этого мусора в конце "=" .
Я открыт для предложений о том, как изменить параметры проекта/компилятора, но я просто использовал стандартный шаблон библиотеки Visual Studio - ничего особенного.
Ответы
Ответ 1
Вы можете получить то, что хотите, отключив генерацию отладочной информации. Project + Свойства, компоновщик, отладка, генерация информации отладки = нет.
Естественно, вы хотите сделать это только для сборки Release. Если опция уже установлена таким образом.
Ответ 2
Вместо использования файла .def просто вставьте pragma comment
, как этот
#pragma comment(linker, "/EXPORT:[email protected]@@23mangledstuff#@@@@")
Изменить: Или еще проще: внутри тела функции используйте
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
., если у вас есть проблемы с поиском имени украшенной функции. Эта последняя прагма может быть дополнительно уменьшена с помощью простого определения макроса.
Ответ 3
Вы должны объявить функции как extern "C"
, если вы не хотите, чтобы их имена искажались.
Ответ 4
Из опыта, будьте осторожны, если вы используете __stdcall в своей сигнатуре функции. С __stdcall имя будет в некоторой степени искажено (вы узнаете достаточно быстро). По-видимому, есть два уровня mangling, один из которых extern "C" имеет дело на уровне С++, но он не имеет отношения к другому уровню манипуляции имени, вызванному __stdcall. Дополнительное искажение, по-видимому, имеет отношение к перегрузке, но я не уверен в этом.
Ответ 5
Извините за ответ на старый поток, но то, что было отмечено как ответ, не сработало для меня.
Как отмечают многие люди, внешнее украшение "С" важно. Изменение настройки "Project/Properties/Linker/Debugging/Generating debug info" не имело никакого значения для меняющихся имен, созданных для меня в режиме Debug или Release build.
Настройка: VS2005 компилирует проект библиотеки классов Visual С++. Я проверял скомпилированный выход .dll с помощью инструмента Microsoft Dependency Walker.
Вот пример рецепта, который работал у меня...
В project.h:
#define DllExport extern "C" __declspec( dllexport )
DllExport bool API_Init();
DllExport bool API_Shutdown();
В файле project.cpp:
#include "project.h"
bool API_Init()
{
return true;
}
bool API_Shutdown()
{
return true;
}
Затем, будучи вызванным из управляемого кода С#, class.cs:
using System.Runtime.Interopservices;
namespace Foo
{
public class Project
{
[DllImport("project.dll")]
public static extern bool API_Init();
[DllImport("project.dll")]
public static extern bool API_Shutdown();
}
}
Выполнение вышеизложенного предотвратило искаженные имена как в режиме отладки, так и в режиме Release, независимо от параметра "Создать информацию об отладке". Удачи.
Ответ 6
Даже без манипуляции, 32-битное и 64-битное имя сборки экспортируется по-разному, даже с extern "C". Проверьте это с помощью DEPENDS.EXE.
Это может означать BIG-проблему для любого клиента, который выполняет LoadLibrary + GetProcAdress для доступа к вашей функции.
Таким образом, поверх всех остальных используется файл определения модуля следующим образом:
LIBRARY MYDLL
EXPORTS
myFunction=myFunction
Yeap, это немного боль для поддержания, но тогда сколько экспортируемых функций вы пишете день?
Кроме того, я обычно меняю макросы, как показано ниже, поскольку мои DLL файлы экспортируют функции не С++-классов, и я хочу, чтобы их вызывали большинство программных сред:
#ifdef WTS_EXPORTS
#define WTS_API(ReturnType) extern "C" __declspec(dllexport) ReturnType WINAPI
#else
#define WTS_API(ReturnType) extern "C" __declspec(dllimport) ReturnType WINAPI
#endif
WTS_API(int) fnWTS(void);
Последняя строка, используемая для путаницы VisualAssistX пару лет назад, я не знаю, правильно ли она ее переваривает: -)
Ответ 7
Я знаю, сколько раз я пытался использовать имена функций с помощью кода и # pragma's.
И я всегда заканчиваю то же самое, используя файл определения модуля (*.def) в конце.
И вот причина:
//---------------------------------------------------------------------------------------------------
// Test cases built using VC2010 - Win32 - Debug / Release << doesn't matter
//---------------------------------------------------------------------------------------------------
// SET: Project > Properties > Linker > Debugging > Generate Debug Info = Yes (/DEBUG)
// || (or, also doesn't matter)
// SET: Project > Properties > Linker > Debugging > Generate Debug Info = No + delete PDB file!
extern "C" __declspec(dllexport) void SetCallback(LPCALLBACK function);
> SetCallback
extern "C" __declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);
> [email protected]
__declspec(dllexport) void SetCallback(LPCALLBACK function);
> [email protected]@[email protected]@Z
__declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);
> [email protected]@[email protected]@Z
//---------------------------------------------------------------------------------------------------
// this also big is nonsense cause as soon you change your calling convention or add / remove
// extern "C" code won't link anymore.
// doesn't work on other cases
#pragma comment(linker, "/EXPORT:SetCallback")
extern "C" __declspec(dllexport) void SetCallback(LPCALLBACK function);
// doesn't work on other cases
#pragma comment(linker, "/EXPORT:SetCallback=SetCallback")
extern "C" __declspec(dllexport) void SetCallback(LPCALLBACK function);
// doesn't work on other cases / creates alias
#pragma comment(linker, "/EXPORT:[email protected]")
extern "C" __declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);
// doesn't work on other cases / creates alias
#pragma comment(linker, "/EXPORT:[email protected]@[email protected]@Z")
__declspec(dllexport) void SetCallback(LPCALLBACK function);
// doesn't work on other cases / creates alias
#pragma comment(linker, "/EXPORT:[email protected]@[email protected]@Z")
__declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);
//---------------------------------------------------------------------------------------------------
// So far only repetable case is using Module-Definition File (*.def) in all possible cases:
EXPORTS
SetCallback
extern "C" __declspec(dllexport) void SetCallback(LPCALLBACK function);
> SetCallback
extern "C" __declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);
> SetCallback
__declspec(dllexport) void SetCallback(LPCALLBACK function);
> SetCallback
__declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);
> SetCallback
// And by far this is most acceptable as it will reproduce exactly same exported function name
// using most common compilers. Header is dictating calling convention so not much trouble for
// other sw/ppl trying to build Interop or similar.
Интересно, почему никто этого не делал, мне потребовалось всего 10 минут, чтобы проверить все случаи.
Ответ 8
SomeFunction @@@23mangledstuff # @@@@искажен, чтобы дать типы и класс функции С++. Простой экспорт - это функции, которые вызываются из C, т.е. записаны на C или иначе объявлены extern "C" в коде С++. Если вам нужен простой интерфейс, вы должны сделать, чтобы функции, которые вы экспортируете, использовали только типы C и делали их non member в глобальном пространстве имен.
Ответ 9
В основном, когда вы используете функции на С++, части их имен теперь включают их подпись и т.д., чтобы облегчить языковые функции, такие как перегрузка.
Если вы пишете DLL, используя __declspec (dllexport), тогда он также должен создать lib. Ссылка на эту библиотеку, и вы будете автоматически связаны и функции, зарегистрированные в CRT во время запуска (если вы помните, чтобы изменить все свои импортные товары для экспорта). Если вы используете эту систему, вам не нужно знать о наименовании.
Ответ 10
В случае, если из сотен строк вафли не видно из-за искаженного экспорта. Вот мой 2c стоит:)
После создания проекта под названием Win32Project2 с использованием VS 2012 и выбора экспорта всех символов в мастере. У вас должно быть 2 файла с именем Win32Project2.cpp и Win32project2.h
Оба из них будут ссылаться на пример экспортируемой переменной и пример экспортируемой функции.
В Win32Project2.h у вас будет следующее:
#ifdef WIN32PROJECT2_EXPORTS
#define WIN32PROJECT2_API __declspec(dllexport)
#else
#define WIN32PROJECT2_API __declspec(dllimport)
#endif
extern WIN32PROJECT2_API int nWin32Project2;
WIN32PROJECT2_API int fnWin32Project2(void);
Чтобы развязать последние две строки с внешними объявлениями C, выполните следующие действия:
extern "C" WIN32PROJECT2_API int nWin32Project2;
extern "C" WIN32PROJECT2_API int fnWin32Project2(void);
В Win32Project2.cpp у вас также будут следующие определения по умолчанию:
// This is an example of an exported variable
WIN32PROJECT2_API int nWin32Project2=0;
// This is an example of an exported function.
WIN32PROJECT2_API int fnWin32Project2(void)
{
return 42;
}
Чтобы отменить ИЗМЕНЕНИЕ ЭТОГО:
// This is an example of an exported variable
extern "C" WIN32PROJECT2_API int nWin32Project2=0;
// This is an example of an exported function.
extern "C" WIN32PROJECT2_API int fnWin32Project2(void)
{
return 42;
}
По существу, вы должны использовать префикс extern "C" перед декларациями, чтобы заставить компоновщик создавать невыпущенные C-подобные имена.
Если вы предпочитаете использовать искаженные имена для этого бита дополнительной обфускации (в случае, если информация о манипуляции кому-то полезна кому-то), используйте "dumpbin/exports Win32Project2.dll" из командной строки VC для поиска фактических имен ссылок. Он будет иметь форму "? FnWind32Project2 @[param bytes] @[другая информация]. Существуют также другие инструменты просмотра DLL, если запущенная командная оболочка VC не плавает на вашей лодке.
Точно, почему MS не по умолчанию для этого соглашения является загадкой. Фактическая информация о манипуляции означает что-то (например, размер параметра в байтах и т.д.), Что может быть полезно для проверки и отладки, но в остальном является guff.
Чтобы импортировать DLL-функцию выше в проект С# (в этом случае базовое приложение Windows С# с формой на нем, содержащей кнопку "button1" ), вот пример кода:
using System.Runtime.InteropServices;
namespace AudioRecApp
{
public partial class Form1 : Form
{
[ DllImport("c:\\Projects\test\Debug\Win32Projects2.dll")]
public static extern int fnWin32Project2();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
int value;
value = fnWin32Project2();
}
}
}