Подтвердить что ты не робот

С++ 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 - ничего особенного.

4b9b3361

Ответ 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();
        }
      }
    }