вызов неуправляемой функции char возвращает char *

У меня есть функция в неуправляемом коде C/C++ (dll), которая возвращает структуру, содержащую массив символов. Я создал структуру C# для получения этого возвращаемого значения при вызове функции. И после вызова этой функции я получаю «System.Runtime.InteropServices.MarshalDirectiveException»

Это объявление C:

typedef struct T_SAMPLE_STRUCT {
int num;
char text[20];
} SAMPLE_STRUCT;

SAMPLE_STRUCT sampleFunction( SAMPLE_STRUCT ss );

Это объявление С#:

struct SAMPLE_STRUCT
{
    public int num;
    public string text;
}

class Dllwrapper
{
    [DllImport("samplecdll.dll")]
    public static extern SAMPLE_STRUCT sampleFunction(SAMPLE_STRUCT ss);

}

Я использую 1-байтовый ASCII.

У кого-нибудь есть подсказка или решение о том, как это сделать?


person Mita    schedule 27.04.2009    source источник
comment
Я не думаю, что у вас есть доступ к исходному коду С++, чтобы упростить вызов с С#?   -  person Tim Robinson    schedule 27.04.2009
comment
Фактический текст ошибки поможет.   -  person C. Ross    schedule 27.04.2009


Ответы (6)


Хитрость преобразования члена массива C заключается в использовании MarshalAs(UnmanagedType.ByValTStr). Это можно использовать, чтобы указать среде CLR маршалировать массив как встроенный элемент, а не как обычный невстроенный массив. Попробуйте следующую подпись.

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet=System.Runtime.InteropServices.CharSet.Ansi)]
public struct T_SAMPLE_STRUCT {

    /// int
    public int num;

    /// char[20]
    [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst=20)]
    public string text;
}

public partial class NativeMethods {

    /// Return Type: SAMPLE_STRUCT->T_SAMPLE_STRUCT
    ///ss: SAMPLE_STRUCT->T_SAMPLE_STRUCT
    [System.Runtime.InteropServices.DllImportAttribute("<Unknown>", EntryPoint="sampleFunction")]
public static extern  T_SAMPLE_STRUCT sampleFunction(T_SAMPLE_STRUCT ss) ;

}

Эта подпись предоставляется помощником по взаимодействию PInovke (ссылка), доступным на CodePlex. Он может автоматически переводить большинство подписей PInvoke из собственного кода в C# или VB.Net.

person JaredPar    schedule 27.04.2009

Это непростая структура для P/Invoke для маршалинга: легче маршалировать структуры, содержащие char* вместо char[] (хотя тогда у вас остается проблема с выделением char* из неуправляемого кода и последующим освобождением его из управляемый код).

Предполагая, что вы придерживаетесь текущего дизайна, один из вариантов - объявить массив строк как:

public fixed char text[20];

К сожалению, вы должны затем добавить ключевое слово unsafe к любому коду, который обращается к этому массиву.

person Tim Robinson    schedule 27.04.2009

Определение структуры в C:

#pragma pack(push, 1)
typedef struct T_SAMPLE_STRUCT {
  int num;
  char text[20];
};
#pragma pack(pop)

Определение на С#:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct T_SAMPLE_STRUCT
{
  [MarshalAs(UnmanagedType.I4)]
  public int num;

  [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
  public string text;
}
person abatishchev    schedule 30.04.2009

Мне удалось сделать это, разделив функцию на:

void receiveStruct( SAMPLE_STRUCT ss )
void returnStruct(SAMPLE_STRUCT &ss)

Я изменил определение структуры, как сказал мне JaredPar:

[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct T_SAMPLE_STRUCT
{
    /// int
    public int num;

    /// char[20]
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 20)]
    public string text;
}

И теперь это работает.

Спасибо!

person Mita    schedule 27.04.2009

Интересно отметить, что, поскольку у вас уже есть ответ, основная проблема здесь заключается в том, что структура просто должна быть правильного размера. Поскольку управляемые типы не имеют встроенных массивов, вам просто нужно заполнить пространство, которое в противном случае потребовалось бы. Когда вы пишете на C++/CLI, вы часто будете видеть StructLayoutAttribute с явным параметром Size. Это заставляет среду выполнения выделять правильный объем памяти для типа, что позволяет преобразовать его в нативную сторону. Отсюда следует, что они также должны работать:

[StructLayout(LayoutKind.Sequential, Size=24)]
public struct T_SAMPLE_STRUCT
{    
    public int num;
    // to get the string here, you'd need to get a pointer
    public char firstChar;     
}

// or

[StructLayout(LayoutKind.Sequential)]
public struct T_SAMPLE_STRUCT
{    
    public int num;
    public byte c0;
    public byte c1;
    public byte c2;
    public byte c3;
    public byte c4;
    public byte c5;
    public byte c6;
    public byte c7;
    public byte c8;
    public byte c9;
    public byte c10;
    public byte c11;
    public byte c12;
    public byte c13;
    public byte c14;
    public byte c15;
    public byte c16;
    public byte c17;
    public byte c18;
    public byte c19;
}

Конечно, их намного сложнее использовать из управляемого кода (вам потребуется копировать память или использовать указатели), но они иллюстрируют концепцию преобразовываемого типа, которая в основном представляет собой то, как типы передаются между собственным и управляемым кодом.

person codekaizen    schedule 27.04.2009

Сначала вам нужно поместить атрибут StructLayout[Sequential] в свою структуру, и я думаю, что это сработает.

[ StructLayout( LayoutKind.Sequential, CharSet=CharSet.Ansi )]
struct SAMPLE_STRUCT
{    
    public int num;
    [ MarshalAs( UnmanagedType.ByValArray, SizeConst=20 )] 
    public char[] text;
} 
person Ahmed Said    schedule 27.04.2009