C # вызов C ++ DLL, передавая аргумент указатель-указатель

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безопасно передаются , и утечки памяти не должно быть. Я хотел бы, чтобы это помогло.

Автор: Yongwei Wu Размещён: 06.12.2013 01:18

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
Вопросы из категории :
32x32