C ++ DLL Export: украшенные / искаженные имена - PullRequest
26 голосов
/ 10 мая 2010

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

SomeFunction

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

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

Почему?

Экспортируемая функция выглядит неукрашенной (особенно по сравнению с тем, что не используется файл Module Def), но что с остальными?

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

SomeFunction

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

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

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

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

Однако это все равно создает тот же вывод, а именно:

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

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

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

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

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

Ответы [ 10 ]

40 голосов
/ 10 мая 2010

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

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

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

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

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

16 голосов
/ 10 мая 2010

Вы можете получить то, что хотите, отключив генерацию отладочной информации. Project + Properties, компоновщик, отладка, генерация отладочной информации = №

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

12 голосов
/ 10 мая 2010

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

11 голосов
/ 15 января 2011

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

6 голосов
/ 07 июля 2011

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

Как отмечают многие люди, внешняя отделка "C" важна. Изменение параметра «Проект / Свойства / Компоновщик / Отладка / Создать отладочную информацию» не имело никакого значения для искаженных имен, сгенерированных для меня в режиме отладки или выпуска.

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

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

В project.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();
    }
}

Выполнение вышеизложенного предотвратило искажение имен как в режиме отладки, так и в режиме выпуска независимо от параметра «Создать информацию отладки». Удачи.

5 голосов
/ 23 октября 2012

Даже без искажения, 32-битные и 64-битные сборки экспортируют имена по-разному, даже с внешним «C». Проверьте это с помощью DEPENDS.EXE.

Это может означать БОЛЬШУЮ проблему для любого клиента, который использует 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 пару лет назад, я не знаю, правильно ли она сейчас ее переваривает: -)

4 голосов
/ 16 декабря 2014

Я знаю, сколько раз я пытался форсировать имена функций, используя код и # 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);
> _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 минут, чтобы проверить все случаи.

0 голосов
/ 24 июля 2014

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

После создания проекта с именем 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] @ [other info]. Существуют также другие инструменты просмотра DLL, если запуск командной оболочки VC не поддерживает вашу лодку.

Точно, почему MS по умолчанию не придерживается этого соглашения, остается загадкой. Фактическая информация искажения означает что-то (например, размер параметра в байтах и ​​более), что может быть полезно для проверки и отладки, но в остальном бесполезно.

Чтобы импортировать указанную выше функцию DLL в проект C # (в данном случае базовое приложение Windows на C # с формой, содержащей кнопку «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();
        }
      }
    }
0 голосов
/ 10 мая 2010

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

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

0 голосов
/ 10 мая 2010

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

...