Как маршалировать структуру С++ в С#

Ниже приведена структура C++. Его реализация выполняется в C++ DLL:

typedef char CusDate[sizeof("2023-02-02")];

typedef enum cycletype {
    NOES=0,
    LET=1, 
    WARN=2,
    GRACE=3,
    EXP=4,
} MYCYCLETYPE;

typedef struct Info {
    MYCYCLETYPE midcycle;
    int  type;
    int porotype;
    long sways;
    CusDate sedate;            
    long days;
    CusDate elate;
    long edays;
    CusDate edate;
    const char* feature;
    const char* ver;
    const char* hid;
}SINFO, *SINFO;

int Initialize(SINFO lp)
{
    lp->mycycle = NOES;
    ......
    .....
    lp->hid = "XYZ";
}

Ниже приведен код C#:

[DllImport(@"abc.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int Initialize(ref SINFO lp);

В С# main():

SINFO mydata;
Initialize(ref mydata);

Теперь я не могу получить доступ к данным структуры, заполненным кодом С++ в С#. Как получить доступ к данным структуры С++ в С#?

Вы не показали, как SINFO определяется на стороне С#.

wohlstad 14.02.2023 18:08

@wohlstad Разве ОП не спрашивает, как это сделать?

Adrian Mole 14.02.2023 18:14

Теперь я не могу получить доступ к данным структуры, заполненным кодом C++ в C# — мне любопытно, как вы заполнили эти char * элементы из C++. Даже если бы вы могли получить доступ к этой структуре на C#, я не удивлюсь, если эти члены окажутся мусором на стороне C#.

PaulMcKenzie 14.02.2023 18:18

@AdrianMole Я не уверен. Они упоминают, что «не смогли получить доступ к данным». Я думал, что это проблема времени выполнения, и поэтому код прошел компиляцию, и SINFO должен быть определен. Я может неправильно понял.

wohlstad 14.02.2023 18:18

Не могли бы вы уточнить, что вы подразумеваете под «невозможно получить доступ ...»? Это проблема времени выполнения или отсутствие кода? Пожалуйста, отредактируйте свой вопрос и добавьте всю необходимую информацию, включая сообщения об ошибках (если применимо).

wohlstad 14.02.2023 18:21

@wohlstad, как маршалировать члены структуры и получить доступ к правильному значению членов структуры в C#?

Indrasen 15.02.2023 10:39
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
6
75
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий
typedef struct Info {
    ...
} SINFO, *SINFO;

Приведенное выше объявление недопустимо для C/C++, так как один и тот же идентификатор (SINFO) не может относиться к двум разным типам (Info и Info*). Возможно, вы имели в виду что-то вроде этого:

typedef struct Info {
    ...
} SINFO, *PSINFO;
          ^

Или, более C++-стиль (поскольку приведенный выше стиль происходит от C):

struct SINFO {
    ...
};
typedef SINFO* PSINFO;
// or: using PSINFO = SINFO*;

В любом случае, Initialize() может принимать либо ссылку SINFO&, либо указатель PSINFO в качестве своего параметра, любой из которых будет совместим с ref SINFO на стороне C#:

int Initialize(SINFO& lp)
int Initialize(PSINFO lp)

Где код C# будет выглядеть примерно так:

public enum MYCYCLETYPE {
    NOES = 0,
    LET = 1, 
    WARN = 2,
    GRACE = 3,
    EXP = 4,
};

[StructLayout(LayoutKind.Sequential)]  
public struct SINFO {
    public MYCYCLETYPE midcycle;
    public int type;
    public int porotype;
    public long sways; // <-- or int, depending on the C++ compiler!
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=11)]
    public byte[] sedate = new byte[11];
    // alternatively:
    // [MarshalAs(UnmanagedType.ByValArray, SizeConst=11, ArraySubType=UnmanagedType.LPStr)]
    // public String sedate;
    public long days; // <-- or int, depending on the C++ compiler!
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=11)]
    public byte[] elate = new byte[11];
    // alternatively:
    // [MarshalAs(UnmanagedType.ByValArray, SizeConst=11, ArraySubType=UnmanagedType.LPStr)]
    // public String elate;
    public long edays; // <-- or int, depending on the C++ compiler!
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=11)]
    public byte[] edate = new byte[11];
    // alternatively:
    // [MarshalAs(UnmanagedType.ByValArray, SizeConst=11, ArraySubType=UnmanagedType.LPStr)]
    // public String edate;
    public IntPtr feature; // <-- use Marshal.PtrToStringAnsi() to read this
    // alternatively:
    // [MarshalAs(UnmanagedType.LPStr)]
    // public String feature;
    public IntPtr ver; // <-- use Marshal.PtrToStringAnsi() to read this
    // alternatively:
    // [MarshalAs(UnmanagedType.LPStr)]
    // public String ver;
    public IntPtr hid; // <-- use Marshal.PtrToStringAnsi() to read this
    // alternatively:
    // [MarshalAs(UnmanagedType.LPStr)]
    // public String hid;
};

[DllImport("abc.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int Initialize(ref SINFO lp);

Кроме того, примечание: вы объявили Initialize() как возвращающее int, но показанный вами код C++ на самом деле не return возвращает значение, что является неопределенным поведением.

В коде C++ int Initialize(SINFO lp) { lp->mycycle = NOES; ...... ..... <здесь присваиваются значения в каждом элементе структуры> lp->hid = "XYZ"; вернуть 1; }

Indrasen 15.02.2023 07:00

Я объявил вышеуказанную структуру и вызвал Initialize() в C#. Теперь, как маршалировать каждый элемент структуры и получать доступ к данным в C#?

Indrasen 15.02.2023 07:08

@lebeausoftware - значение, присвоенное const char *, изменяется в С++. Теперь, когда мы маршалируем IntPtr, как мы определяем длину строки в С#.

Indrasen 16.02.2023 04:07

@Indrasen так же, как и в C++, - подсчитывая символы, пока не дойдете до нулевого ограничителя. Пусть Marshal.PtrToStringAnsi() сделает это за вас.

Remy Lebeau 16.02.2023 08:10

СИНФО ЛП; int result = Initialize(ref SINFO lp); строковая функция = Marshal.PtrToStringAnsi(lp.feature); Console.WriteLine(функция); --> Ничего не печатать Console.WriteLine(lp.midcycle); --> Не печатается значение, назначенное Initialize() Console.WriteLine(lp.type); --> Он не печатает значение, назначенное Initialize() Я делаю что-то неправильно здесь?

Indrasen 16.02.2023 15:06

Убедились ли вы, что размер, выравнивание и заполнение SINFO в C# соответствуют тому же размеру, выравниванию и заполнению SINFO в C++? Если они не совпадают точно, вы не получите правильных результатов. Использует ли код C++ выравнивание по 4 или 8 байтам для структуры? Каков размер cycletype и long в C++? Они могут быть 1 или 4 и 4 или 8 байт соответственно, в зависимости от настроек компилятора. Какое значение возвращает sizeof() C++ для SINFO и offsetof() для каждого члена SINFO?

Remy Lebeau 16.02.2023 16:41

Я проверил выравнивание и размер каждого члена структуры как в С++, так и в С# на своем конце. Мне кажется хорошо. В настоящее время я компилирую код С++ в 64-битной версии и аналогичным образом компилирую код С# только в 64-битной версии.

Indrasen 16.02.2023 18:26

Не могли бы вы привести пример маршалирования элементов структуры по сравнению с приведенной выше структурой в С#?

Indrasen 16.02.2023 18:29

«Я проверил выравнивание и размер каждого члена структуры как в С++, так и в С#», но проверяли ли вы также смещения байтов каждого члена в структуре? В худшем случае вам может понадобиться использовать [FieldOffset] на стороне C#. Если все в порядке, то предоставленный код должен работать. Также убедитесь, что адрес памяти, который вы назначаете feature/ver/hid на стороне C++, является тем же числовым значением, которое IntPtr получает на стороне C#.

Remy Lebeau 16.02.2023 20:02

«Не могли бы вы привести пример для маршалирования членов структуры по сравнению с приведенной выше структурой в С#?» - У меня уже есть.

Remy Lebeau 16.02.2023 20:03

строка myfeature = Marshal.PtrToStringAnsi(lp.feature); Console.WriteLine(myfeature);--> Prints 133760539264 Он должен напечатать какое-то строковое значение, но печатает какой-то мусор

Indrasen 18.02.2023 20:28

@Indrasen, учитывая предоставленный вами код C++, является правильным кодом C# (если вы не переключитесь на MarshalAs, который я показал). Итак, то, что вы описываете, может произойти только в том случае, если lp.feature НЕ получает правильный адрес памяти от С++ (я сказал вам проверить это). Это означает, что ваша запись SINFO в C# НЕ точно совпадает с SINFO в C++. Есть что-то, что еще не работает. Вам нужно найти и исправить это. Вы утверждаете, что проверили выравнивание/смещение, но я не думаю, что это действительно так (и вы не предоставили числа, которые я просил вас), иначе это работало бы нормально.

Remy Lebeau 18.02.2023 20:33

Я проверил выравнивание. Кажется, что в С++ DLL значения для всех членов структуры не установлены. т.е. из 12 элементов структуры значения устанавливаются только для 5 элементов структуры.

Indrasen 19.02.2023 06:38

@Indrasen Вы не показали полный код C++ и не предоставили фактические числа, которые я просил вас (несколько раз), поэтому я не могу вам помочь, пока вы это не сделаете. То, что вы описываете, не имеет смысла, учитывая доступный до сих пор код, поэтому чего-то еще не хватает.

Remy Lebeau 19.02.2023 09:21

Другие вопросы по теме