C # вызов C ++ DLL, передавая аргумент указатель-указатель
18226 просмотра
3 ответа
Не могли бы вы, ребята, помочь мне решить следующую проблему? У меня есть функция C ++ DLL, и она будет вызываться другим приложением C #. Одна из необходимых мне функций заключается в следующем:
struct DataStruct
{
unsigned char* data;
int len;
};
DLLAPI int API_ReadFile(const wchar_t* filename, DataStruct** outData);
Я написал следующий код на C #:
class CS_DataStruct
{
public byte[] data;
public int len;
}
[DllImport("ReadFile.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
private static extern int API_ReadFile([MarshalAs(UnmanagedType.LPWStr)]string filename, ref CS_DataStruct data);
К сожалению, приведенный выше код не работает ... Я думаю, что из-за функции C ++ принимает указатель на указатель DataStruct, в то время как я только что передал ссылку на CS_DataStruct в.
Могу ли я узнать, как я могу передать указатель на указатель на функцию C ++? Если это невозможно, есть ли обходной путь? (API C ++ исправлен, поэтому изменение API на указатель невозможно)
Редактировать: Память DataStruct будет выделяться функцией c ++. До этого я понятия не имел, насколько большим должен быть массив данных. (Спасибо за комментарии ниже)
Автор: LennonLam Источник Размещён: 13.11.2019 11:49Ответы (3)
7 плюса
Я использовал следующую тестовую реализацию:
int API_ReadFile(const wchar_t* filename, DataStruct** outData)
{
*outData = new DataStruct();
(*outData)->data = (unsigned char*)_strdup("hello");
(*outData)->len = 5;
return 0;
}
void API_Free(DataStruct** pp)
{
free((*pp)->data);
delete *pp;
*pp = NULL;
}
Код C # для доступа к этим функциям выглядит следующим образом:
[StructLayout(LayoutKind.Sequential)]
struct DataStruct
{
public IntPtr data;
public int len;
};
[DllImport("ReadFile.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
unsafe private static extern int API_ReadFile([MarshalAs(UnmanagedType.LPWStr)]string filename, DataStruct** outData);
[DllImport("ReadFile.dll", CallingConvention = CallingConvention.Cdecl)]
unsafe private static extern void API_Free(DataStruct** handle);
unsafe static int ReadFile(string filename, out byte[] buffer)
{
DataStruct* outData;
int result = API_ReadFile(filename, &outData);
buffer = new byte[outData->len];
Marshal.Copy((IntPtr)outData->data, buffer, 0, outData->len);
API_Free(&outData);
return result;
}
static void Main(string[] args)
{
byte[] buffer;
ReadFile("test.txt", out buffer);
foreach (byte ch in buffer)
{
Console.Write("{0} ", ch);
}
Console.Write("\n");
}
Данные теперь buffer
безопасно передаются , и утечки памяти не должно быть. Я хотел бы, чтобы это помогло.
2 плюса
Нет необходимости использовать unsafe
для передачи указателя на массив из DLL. Вот пример (см. Параметр «результаты»). Ключ должен использовать ref
атрибут. Также показано, как передать несколько других типов данных.
Как определено в C ++ / C:
#ifdef __cplusplus
extern "C" {
#endif
#ifdef BUILDING_DLL
#define DLLCALL __declspec(dllexport)
#else
#define DLLCALL __declspec(dllimport)
#endif
static const int DataLength = 10;
static const int StrLen = 16;
static const int MaxResults = 30;
enum Status { on = 0, off = 1 };
struct Result {
char name[StrLen]; //!< Up to StrLen-1 char null-terminated name
float location;
Status status;
};
/**
* Analyze Data
* @param data [in] array of doubles
* @param dataLength [in] number of floats in data
* @param weight [in]
* @param status [in] enum with data status
* @param results [out] array of MaxResults (pre-allocated) DLLResult structs.
* Up to MaxResults results will be returned.
* @param nResults [out] the actual number of results being returned.
*/
void DLLCALL __stdcall analyzeData(
const double *data, int dataLength, float weight, Status status, Result **results, int *nResults);
#ifdef __cplusplus
}
#endif
Как используется в C #:
private const int DataLength = 10;
private const int StrLen = 16;
private const int MaxThreatPeaks = 30;
public enum Status { on = 0, off = 1 };
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Result
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = StrLen)] public string name; //!< Up to StrLen-1 char null-terminated name
public float location;
public Status status;
}
[DllImport("dllname.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "analyzeData@32")] // "@32" is only used in the 32-bit version.
public static extern void analyzeData(
double[] data,
int dataLength,
float weight,
Status status,
[MarshalAs(UnmanagedType.LPArray, SizeConst = MaxResults)] ref Result[] results,
out int nResults
);
Без этой extern "C"
части компилятор C ++ будет манипулировать именем экспорта зависимым от компилятора способом. Я заметил, что имя функции EntryPoint / Exported совпадает с именем функции точно в 64-битной DLL, но при добавлении в 32-битную DLL к нему добавляется '32' (число может варьироваться). Запустите, dumpbin /exports dllname.dll
чтобы найти экспортированное имя наверняка. В некоторых случаях вам также может понадобиться использовать параметр DLLImport ExactSpelling = true
. Обратите внимание, что эта функция объявлена __stdcall
. Если бы это не было указано, это было бы __cdecl
и вам нужно CallingConvention.Cdecl
.
Вот как это может быть использовано в C #:
Status status = Status.on;
double[] data = { -0.034, -0.05, -0.039, -0.034, -0.057, -0.084, -0.105, -0.146, -0.174, -0.167};
Result[] results = new Result[MaxResults];
int nResults = -1; // just to see that it changes (input value is ignored)
analyzeData(data, DataLength, 1.0f, status, ref results, out nResults);
Автор: jtbr
Размещён: 05.08.2016 04:31
0 плюса
Если вы вызываете собственный код, убедитесь, что ваши структуры выровнены в памяти. CLR не гарантирует выравнивание, если вы не нажмете на него.
Пытаться
[StructLayout(LayoutKind.Explicit)]
struct DataStruct
{
string data;
int len;
};
Более подробная информация: http://www.developerfusion.com/article/84519/mastering-structs-in-c/
Автор: Random Размещён: 06.12.2013 08:39Вопросы из категории :
- c# Преобразовать десятичную в двойную?
- c# Как рассчитать чей-то возраст в C #?
- c# Как вы сортируете словарь по значению?
- c# В чем разница между int и Integer в Java и C #?
- c# Как создать новый экземпляр объекта из Типа
- c# Datatable против Dataset
- c++ What are the barriers to understanding pointers and what can be done to overcome them?
- c++ Какой самый простой способ для анализа файла INI в C ++?
- c++ Когда вы должны использовать «друг» в C ++?
- c++ Как вы очищаете переменную stringstream?
- c++ В C ++ конструктор и деструктор могут быть встроенными функциями?
- c++ Что такое виртуальный базовый класс в C ++?
- dll Что такое DLL-файлы и как они работают?
- dll Встраивание DLL в скомпилированный исполняемый файл
- dll Как переименовать DLL, но все же разрешить EXE найти ее?
- dll Как установить информацию о версии для существующего .exe, .dll?
- dll круговые зависимости между dll с визуальной студией
- dll Использование Windows DLL из Linux