Учитывая следующий класс c ++ в foo.dll
class a{
private:
int _answer;
public:
a(int answer) { _answer = answer; }
__declspec(dllexport) int GetAnswer() { return _answer; }
}
Я хотел бы pInvoke GetAnswer из C #. Для этого я использую следующий метод:
[DllImport("foo.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint= "something")]
public static extern int GetAnswer(IntPtr thisA);
И я передаю в IntPtr, который указывает на (что я получил откуда-то, это не важно). CallingConvention = CallingConvention.ThisCall
удостоверяется, что он обрабатывается правильно
Что здорово в этом вопросе, так это то, что я знаю, что пока прав, потому что это уже отлично работает! Используя Depends.exe, я вижу, что «GetAnswer» экспортируется как? GetAnswer @ a @@ UAEHXZ (или что-то близкое — суть в том, что это имя искажено). Когда я вставляю искаженное имя в «что-то» для EntryPoint, все отлично работает! Мне потребовалось около дня, прежде чем я понял, что я должен использовать Depends.exe, так что я собираюсь оставить это здесь, чтобы помочь любому, у кого есть подобная проблема.
Мой РЕАЛЬНЫЙ вопрос: Есть ли способ отключить искажение имени в C ++ на GetAnswer, чтобы мне не нужно было вводить искаженное имя в качестве моей точки входа. Кажется, что искажение имени там может сломаться, потому что мое понимание искажения имени заключается в том, что оно может измениться при изменении компилятора. Также неприятно использовать Depends.exe для каждого метода экземпляра, который я хочу pInvoke.
Изменить: Забыл добавить то, что я пробовал:
Кажется, я не могу поставить extern «C» в объявлении функции, хотя я могу вставить его в определение. Это, похоже, не помогает (что очевидно, когда вы об этом думаете)
Единственное другое решение, о котором я могу подумать, — это функция в стиле c, которая оборачивает метод экземпляра и принимает экземпляр a в качестве параметра. Затем отключите искажение имени в этой оболочке и вызовите его. Я бы предпочел придерживаться решения, которое у меня уже есть. Я уже сказал моим коллегам, что pInvoke — это здорово. Я буду выглядеть как идиот, если мне придется помещать специальные функции в нашу библиотеку c ++ только для того, чтобы заставить работать pInvoke.
Вы не можете отключить искажение для метода класса C ++, но вы вполне можете экспортировать функцию под именем по вашему выбору, используя /EXPORT
или .def файл.
Однако весь ваш подход хрупок, потому что вы полагаетесь на детали реализации, а именно this
передается как неявный параметр. Более того, экспорт отдельных методов класса — это рецепт боли.
Наиболее разумные стратегии предоставления класса C ++ языкам .net:
Вариант 2 предпочтительнее, на мой взгляд.
Вы можете использовать комментарий / компоновщик #pragma для передачи /EXPORT
переключитесь на компоновщик, который должен позволить вам переименовать экспортируемый символ:
#pragma comment(linker, "/EXPORT:GetAnswer=?GetAnswer@a@@UAEHXZ")
К сожалению, это не решает проблему поиска искаженного имени с использованием зависимости или другого инструмента.
От автора вопроса: Решение, с которым я действительно пошел
Я закончил тем, что пошел с функцией стиля c, которая оборачивает метод экземпляра и берет экземпляр в качестве параметра. Таким образом, если класс когда-либо наследуется, будет вызван правильный виртуальный метод.
Я сознательно решил не использовать C ++ / CLI, потому что это еще один проект для управления. Если бы мне нужно было использовать все методы в классе, я бы подумал об этом, но мне действительно нужен только один метод, который сериализует данные класса.
Вам не нужно отключать искаженное имя, которое на самом деле содержит много информации о том, как объявляется сама функция, оно в основном представляет всю сигнатуру функции после того, как имя функции де-искалечено. Я понимаю, что вы уже нашли слово, и другой ответ был помечен как правильный ответ. Ниже я пишу, как мы можем сделать так, как вы хотите.
[DllImport("foo.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint = "#OrdinalNumber")]
public static extern int GetAnswer(IntPtr thisA);
Если вы замените «#OrdinalNumber» на реальный порядковый номер GetAnsweer, например «# 1», он будет работать так, как вам нужно.
Вы можете просто считать, что свойство EntryPoint совпадает с именем функции, которое мы передаем в GetProcAddress, где вы можете передать либо имя функции, либо порядковый номер функции.
Ваш подход к вызову нестатических функций-членов класса C ++ действительно верен, и thiscall используется правильно, и именно это соглашение о вызовах thiscall вступает в игру в C # P / Invoke. Проблема с этим подходом заключается в том, что вам нужно будет просмотреть PE-информацию DLL, Export Information Information и узнать порядковый номер для каждой функции, которую вы хотите вызывать. Если у вас есть большое количество функций C ++ для вызова, вы можете хочу автоматизировать такой процесс.