SatishKagathara Ответов: 4

Как передать аргументы указателя из C# в функцию в C++ .dll


Привет,
Я сталкиваюсь с некоторыми проблемами при передаче аргументов из управляемого кода в неуправляемый код. У меня есть библиотека dll C++, и я хочу вызвать несколько ее функций из проекта C# (VS2010). Библиотека dll C++ не является собственной сборкой или COM-компонентом. Поэтому я использую DllImport - P\Invoke.

Детали функции C++
typedef unsigned long UINT32;
typedef UNIT32 ABC_RESULT; // the return code from a ABC function
typedef UINT32 ABC_HOBJECT; // basic object handle
typedef ABC_HOBJECT ABC_HCONTEXT; // context object handle

ABC_RESULT Abc_Context_Create(
ABC_HCONTEXT* phContext  // out
);

phContext - получает дескриптор созданного объекта контекста.

Подпись C# -
public class Abc1api
{
[DllImport("Abc1.dll")]
public static extern UInt32 Abc_Context_Create(UIntPtr phContext);
}

И я называю это так, как показано ниже -
UInt32 result = 0;
UIntPtr hContext = new UIntPtr(sizeof(UInt32));
result = Abc1api.Abc_Context_Create(hContext);


Я могу построить проект, но во время работы я получаю ниже ошибки -
Вызов функции PInvoke 'ABCTool!ABCTool.Abc1api::Abc_Context_Create' разбалансировал стек. Вероятно, это связано с тем, что управляемая подпись PInvoke не совпадает с неуправляемой целевой подписью. Убедитесь, что соглашение о вызове и параметры подписи PInvoke соответствуют целевой неуправляемой подписи.

Я попробовал IntPtr hContext вместо UIntPtr, но результат тот же.

Поэтому я изменил подпись C# , как указано ниже -
public class Abc1api
{
[DllImport("Abc1.dll")]
[return: MarshalAs(UnmanagedType.U4)]
public static extern UInt32 Abc_Context_Create([MarshalAs(UnmanagedType.U4), Out()] IntPtr phContext);
}


И я называю это так, как показано ниже -
UInt32 result = 0;
IntPtr hContext = new IntPtr(sizeof(UInt32));
result = Abc1api.Abc_Context_Create(hContext);

Когда я запускаю его, ошибка - не удается маршалировать "параметр #1": недопустимая комбинация управляемого/неуправляемого типа (Int/UInt должен быть сопряжен с SysInt или SysUInt).

Я попробовал UIntPtr, но тот же результат. Также попробовал выделить IntPtr hContext с помощью Маршала т. е.
IntPtr hkontext = Маршал.AllocHGlobal(Marshal.SizeOf(typeof(UInt32))); но получает ту же ошибку, что и выше.

Я так и не смог выяснить причину этой ошибки. Может ли кто-нибудь помочь мне в определении сигнатуры C# и как я могу передать аргументы, чтобы получить необходимый hContext из неуправляемого кода и использовать его?

Спасибо,
Сатиш.

4 Ответов

Рейтинг:
2

Espen Harlinn

Поскольку вы знаете свой путь вокруг C++, почему бы не решить эту проблему с помощью смешанного режима C++/CLI? В этой статье я продемонстрирую использование ACE с C++ CLI[^], где ACE-это обычная нативная библиотека C++, скомпилированная в DLL. Одна из приятных вещей заключается в том, что вы можете забыть о искажении имен c++, все это заботится о вас.

Я думаю, что это более простое решение :) - выставьте функциональность вашей библиотеки c++ как CLI - вызовите собственную dll из кода cli.

С уважением
Эспен Харлинн


Рейтинг:
2

Franco Rosatti

В dllimport вы должны использовать правильную точку входа (будьте осторожны с mangle), а также использовать CallingConvention:=CallingConvention.Ключевое слово cdecl


Рейтинг:
1

DaveyM69

// int or uint
typedef unsigned long UINT32;
// int or uint
typedef UNIT32 ABC_RESULT; // the return code from a ABC function
// int or uint
typedef UINT32 ABC_HOBJECT; // basic object handle
// int or uint
typedef ABC_HOBJECT ABC_HCONTEXT; // context object handle
// returns int or uint and passes out a pointer (reference) to a int or uint that is the context
ABC_RESULT Abc_Context_Create(ABC_HCONTEXT* phContext  // out);

Поэтому я бы заявил об этом:
public class Abc1api
{
    [DllImport("Abc1.dll")]
    public static extern UInt32 Abc_Context_Create(out UInt32 hContext);
}

или
[DllImport("Abc1.dll")]
public static extern Int32 Abc_Context_Create(out Int32 hContext);


Надеюсь, это поможет!


Рейтинг:
0

Andrew Brock

Во-первых, вам нужно экспортировать функцию с помощью __declspec(dllexport), чтобы она отображалась в экспортируемых функциях DLL (для этого также доступны другие методы, такие как файл .def)

C++ будет "искажать" имена функций, когда он экспортирует их, чтобы экспортировать тип аргументов и возвращаемый тип. Чтобы избежать этого, экспортируемые функции должны быть определены внутри a extern "C" {} блок, который экспортирует их как функцию C без этого искажения.

Чтобы проверить, не искажены ли имена, получите такую программу, как DependancyWalker[^] и откройте в нем библиотеку DLL C++.
То, что вы ищете, находится над правой стороной посередине, которая показывает экспортированные функции.
Вам нужно cehck, чтобы увидеть, содержат ли имена функций типы возвращаемых значений и параметров или имеют перепутанные имена.

typedef unsigned long UINT32;
typedef UNIT32 ABC_RESULT; // the return code from a ABC function
typedef UINT32 ABC_HOBJECT; // basic object handle
typedef ABC_HOBJECT ABC_HCONTEXT; // context object handle

extern "C" {
	__declspec(dllexport) ABC_RESULT Abc_Context_Create(ABC_HCONTEXT * phContext) {
		//The code, you may need to call another function from here if you are wanting to do something only valid in C++
	}
}


SatishKagathara

Спасибо, что ответили. Функция уже экспортирована с помощью __declspec(dllexport) и определена внутри блока extern "C" {} правильно, как вы уже упоминали.

Я проверил экспортированные функции dll C++ с помощью Dependancy Walker. Список экспортированных функций показывает только имя функции.

т.е.
Порядковая Подсказка Точка Входа Функции
6 (0x0006) 5 (0x0005) Abc_Context_Connect 0x0010F550
8 (0x0008) 7 (0x0007) Abc_Context_Create 0x0010EDD0
и т.д...

Дайте мне знать, являются ли они правильными или он должен показывать Аргументы и возвращаемые типы.