вызов неуправляемой функции char возвращает char * - PullRequest
3 голосов
/ 27 апреля 2009

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

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

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

SAMPLE_STRUCT sampleFunction( SAMPLE_STRUCT ss );

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

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.

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

Ответы [ 6 ]

5 голосов
/ 27 апреля 2009

Хитрость в преобразовании члена массива 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.

2 голосов
/ 30 апреля 2009

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

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

Определение в C #:

[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;
}
2 голосов
/ 27 апреля 2009

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

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

public fixed char text[20];

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

0 голосов
/ 27 апреля 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;
}

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

0 голосов
/ 27 апреля 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;
}

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

Спасибо!

0 голосов
/ 27 апреля 2009

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

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