Экспорт DLL c++: украшенные / искаженные имена

создал базовую DLL C++ и экспортировал имена с помощью файла определения модуля (MyDLL.DEF.) После компиляции я проверяю экспортированные имена функций, используя dumpbin.exe Я ожидаю увидеть:

SomeFunction

но вместо этого я вижу:

SomeFunction = SomeFunction@@@23mangledstuff#@@@@

почему?

экспортируемая функция кажется недекорированной (особенно по сравнению с не использованием файла модуля Def), но что происходит с другими вещами?

если я использую dumpbin.exe против DLL из любого коммерческого применение, вы получаете чистый:

SomeFunction

и больше ничего...

Я также попытался удалить определение модуля и экспортировать имена, используя стиль экспорта "C", а именно:

extern "C" void __declspec(dllexport) SomeFunction();

(просто используя "extern "C" не создал экспортированную функцию)

однако это все равно создает тот же результат, а именно:

SomeFunction = SomeFunction@@@23mangledstuff#@@@@

Я также попробовал #define dllexport __declspec(dllexport) option и создал LIB без проблем. Однако, я не хочу предоставьте файл LIB людям, использующим DLL в своем приложении C#.

это обычная ванильная C++ DLL (неуправляемый код), скомпилированная с C++ ничего, кроме простого заголовка и кода. Без модуля Def я получаю искаженные экспортированные функции (я могу создать статическую библиотеку и использовать LIB без проблем. Я пытаюсь этого избежать). Если я использую extern "C" __declspec(dllexport) или определение модуля я получаю то, что кажется недекорированным именем функции... единственная проблема заключается в том, что за ним следует " = " и что выглядит как украшенная версия функции. Я хочу избавиться от материала после " = " - или, по крайней мере, понять, почему он там.

в его нынешнем виде я уверен, что могу вызвать функцию из C# с помощью P/Invoke... Я просто хочу избежать этого мусора в конце"=".

Я открыт для предложений о том, как изменить параметры проекта/компилятора, но я просто использовал стандартный шаблон DLL Visual Studio - ничего особенного.

10 ответов


вы можете получить то, что вы хотите, отключив генерацию отладочной информации. Проект + Свойства, Компоновщик, Отладка, Генерация Отладочной Информации = Нет.

естественно, вы хотите сделать это только для сборки выпуска. Где опция уже установлена таким образом.


вместо использования .Def-файл просто вставить pragma comment такой

#pragma comment(linker, "/EXPORT:SomeFunction=_SomeFunction@@@23mangledstuff#@@@@")

Edit: или еще проще: внутри тела функции используйте

#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)

. . . если у вас возникли проблемы с поиском имени украшенной функции. Эта последняя ПРАГМА может быть дополнительно уменьшена с помощью простого определения макроса.


вы должны объявить функции как extern "C" Если вы не хотите, чтобы их имена исковеркали.


из опыта, будьте осторожны, если вы используете __stdcall в вашей сигнатуре функции. С __stdcall, имя останется в какой-то степени искаженным (вы узнаете достаточно быстро). По-видимому, есть два уровня искалечивания, один extern "C" имеет дело с на уровне C++, но он не имеет дело с другим уровнем искажения имени, вызванным __stdcall. Дополнительное повреждение, по-видимому, имеет отношение к перегрузке, но я не уверен в этом.


извините за ответ на старый поток, но то, что было отмечено как ответ, не сработало для меня.

как указывали несколько людей, внешнее украшение " C " важно. Изменение параметра" Project / Properties / Linker / Debugging / Generate debug info " не имело абсолютно никакого значения для искаженных имен, генерируемых для меня в режиме отладки или выпуска сборки.

Setup: VS2005 компиляция проекта библиотеки классов Visual C++. Я проверял скомпилированный .выход dll с помощью инструмента Microsoft Dependency Walker.

вот пример рецепта, который работал для меня...

в проект.h:

#define DllExport extern "C" __declspec( dllexport )

DllExport bool API_Init();
DllExport bool API_Shutdown();

в проект.cpp:

#include "project.h"

bool API_Init()
{
  return true;
}

bool API_Shutdown()
{
  return true;
}

затем вызывается из управляемого кода C#, 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();
    }
}

выполнение вышеизложенного предотвратило искаженные имена как в режиме отладки, так и в режиме выпуска, независимо от настройки Generate debug info. Удача.


даже без искажения 32-битные и 64-битные сборки экспортируют имя по-разному, даже с extern "C". Проверьте это с помощью DEPENDS.ИСПОЛНЯЕМЫЙ.

Это может означать большие проблемы для любого клиента, который делает LoadLibrary + GetProcAdress для доступа к вашей функции.

Итак, поверх всех остальных используйте файл определения модуля следующим образом:

LIBRARY MYDLL
EXPORTS
myFunction=myFunction

Да, это немного больно поддерживать, но затем, сколько экспортированных функций вы пишете день?

кроме того, я обычно меняю макросы, как показано ниже, так как мои функции экспорта DLL не классы C++, и я хочу, чтобы они вызывались большинством сред программирования:

#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 пару лет назад, я не знаю, правильно ли она переваривает ее сейчас : -)


Я знаю, сколько раз я пытался форсировать имена функций с помощью кода и #pragma. И я всегда заканчиваю точно так же, используя файл определения модуля (*.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);
> _SetCallback@4

__declspec(dllexport) void SetCallback(LPCALLBACK function);
> ?SetCallback@@YAXP6AXHPADPAX@Z@Z

__declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);
> ?SetCallback@@YGXP6GXHPADPAX@Z@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:SetCallback=_SetCallback@4")
extern "C" __declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);

// doesn't work on other cases / creates alias
#pragma comment(linker, "/EXPORT:SetCallback=?SetCallback@@YAXP6AXHPADPAX@Z@Z")
__declspec(dllexport) void SetCallback(LPCALLBACK function);

// doesn't work on other cases / creates alias
#pragma comment(linker, "/EXPORT:SetCallback=?SetCallback@@YGXP6GXHPADPAX@Z@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 минут, чтобы проверить все случаи.


SomeFunction@@@23mangledstuff#@@@ @ искажается, чтобы дать типы и класс функции C++. Простой экспорт-это функции, вызываемые из C, т. е. написанные на C или объявленные extern " C " в коде C++. Если вы хотите простой интерфейс, вы должны сделать функции, которые вы экспортируете, использовать только типы C и сделать их не функциями-членами в глобальном пространстве имен.


в основном, когда вы используете функции в C++, части их имен теперь включают их подпись и тому подобное, чтобы облегчить языковые функции, такие как перегрузка.

Если вы пишете DLL с помощью _ _ declspec (dllexport), то он также должен создать lib. Ссылка на этот lib, и вы будете автоматически связаны и функции, зарегистрированные CRT во время запуска (если вы не забыли изменить все ваши импорт на экспорт). Вам не нужно знать о name mangling, если вы используете эта система.


в случае, если это не было ясно из сотен строк вафель на тему искалеченного экспорта. Вот мой стоит 2С :)

после создания проекта под названием Win32Project2 с помощью VS 2012 и выбора экспорта всех символов в Мастере. У вас должно быть 2 файла с именем Win32Project2.cpp и Win32project2.h

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

В Win32Project2.ч у вас будет следующий:

#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);

в unmangle изменить последние две строчки, чтобы модификатор extern "с" деклараций:

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;
}

в unmangle изменить эти:

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

чтобы импортировать функцию DLL выше в проект C# (в этом случае базовое приложение c# 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();
        }
      }
    }