Вопрос:

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

c# c++ dll

18226 просмотра

3 ответа

80 Репутация автора

Не могли бы вы, ребята, помочь мне решить следующую проблему? У меня есть функция 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 Источник Размещён: 06.12.2013 08:34

Ответы (3)


0 плюса

1875 Репутация автора

Если вы вызываете собственный код, убедитесь, что ваши структуры выровнены в памяти. 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

7 плюса

2226 Репутация автора

Решение

Я использовал следующую тестовую реализацию:

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 плюса

661 Репутация автора

Нет необходимости использовать 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
Вопросы из категории :
32x32