Ниже приведена структура 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);
Теперь я не могу получить доступ к данным структуры, заполненным кодом С++ в С#. Как получить доступ к данным структуры С++ в С#?
@wohlstad Разве ОП не спрашивает, как это сделать?
Теперь я не могу получить доступ к данным структуры, заполненным кодом C++ в C# — мне любопытно, как вы заполнили эти char *
элементы из C++. Даже если бы вы могли получить доступ к этой структуре на C#, я не удивлюсь, если эти члены окажутся мусором на стороне C#.
@AdrianMole Я не уверен. Они упоминают, что «не смогли получить доступ к данным». Я думал, что это проблема времени выполнения, и поэтому код прошел компиляцию, и SINFO должен быть определен. Я может неправильно понял.
Не могли бы вы уточнить, что вы подразумеваете под «невозможно получить доступ ...»? Это проблема времени выполнения или отсутствие кода? Пожалуйста, отредактируйте свой вопрос и добавьте всю необходимую информацию, включая сообщения об ошибках (если применимо).
@wohlstad, как маршалировать члены структуры и получить доступ к правильному значению членов структуры в C#?
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; }
Я объявил вышеуказанную структуру и вызвал Initialize() в C#. Теперь, как маршалировать каждый элемент структуры и получать доступ к данным в C#?
@lebeausoftware - значение, присвоенное const char *, изменяется в С++. Теперь, когда мы маршалируем IntPtr, как мы определяем длину строки в С#.
@Indrasen так же, как и в C++, - подсчитывая символы, пока не дойдете до нулевого ограничителя. Пусть Marshal.PtrToStringAnsi()
сделает это за вас.
СИНФО ЛП; int result = Initialize(ref SINFO lp); строковая функция = Marshal.PtrToStringAnsi(lp.feature); Console.WriteLine(функция); --> Ничего не печатать Console.WriteLine(lp.midcycle); --> Не печатается значение, назначенное Initialize() Console.WriteLine(lp.type); --> Он не печатает значение, назначенное Initialize() Я делаю что-то неправильно здесь?
Убедились ли вы, что размер, выравнивание и заполнение SINFO
в C# соответствуют тому же размеру, выравниванию и заполнению SINFO
в C++? Если они не совпадают точно, вы не получите правильных результатов. Использует ли код C++ выравнивание по 4 или 8 байтам для структуры? Каков размер cycletype
и long
в C++? Они могут быть 1 или 4 и 4 или 8 байт соответственно, в зависимости от настроек компилятора. Какое значение возвращает sizeof()
C++ для SINFO
и offsetof()
для каждого члена SINFO
?
Я проверил выравнивание и размер каждого члена структуры как в С++, так и в С# на своем конце. Мне кажется хорошо. В настоящее время я компилирую код С++ в 64-битной версии и аналогичным образом компилирую код С# только в 64-битной версии.
Не могли бы вы привести пример маршалирования элементов структуры по сравнению с приведенной выше структурой в С#?
«Я проверил выравнивание и размер каждого члена структуры как в С++, так и в С#», но проверяли ли вы также смещения байтов каждого члена в структуре? В худшем случае вам может понадобиться использовать [FieldOffset] на стороне C#. Если все в порядке, то предоставленный код должен работать. Также убедитесь, что адрес памяти, который вы назначаете feature
/ver
/hid
на стороне C++, является тем же числовым значением, которое IntPtr
получает на стороне C#.
«Не могли бы вы привести пример для маршалирования членов структуры по сравнению с приведенной выше структурой в С#?» - У меня уже есть.
строка myfeature = Marshal.PtrToStringAnsi(lp.feature); Console.WriteLine(myfeature);--> Prints 133760539264 Он должен напечатать какое-то строковое значение, но печатает какой-то мусор
@Indrasen, учитывая предоставленный вами код C++, является правильным кодом C# (если вы не переключитесь на MarshalAs
, который я показал). Итак, то, что вы описываете, может произойти только в том случае, если lp.feature
НЕ получает правильный адрес памяти от С++ (я сказал вам проверить это). Это означает, что ваша запись SINFO
в C# НЕ точно совпадает с SINFO
в C++. Есть что-то, что еще не работает. Вам нужно найти и исправить это. Вы утверждаете, что проверили выравнивание/смещение, но я не думаю, что это действительно так (и вы не предоставили числа, которые я просил вас), иначе это работало бы нормально.
Я проверил выравнивание. Кажется, что в С++ DLL значения для всех членов структуры не установлены. т.е. из 12 элементов структуры значения устанавливаются только для 5 элементов структуры.
@Indrasen Вы не показали полный код C++ и не предоставили фактические числа, которые я просил вас (несколько раз), поэтому я не могу вам помочь, пока вы это не сделаете. То, что вы описываете, не имеет смысла, учитывая доступный до сих пор код, поэтому чего-то еще не хватает.
Вы не показали, как
SINFO
определяется на стороне С#.