Как передать структуру как параметр функции из программы на C# в программу на C и вернуть структуру обратно?
Как с помощью вызова extern функции библиотеки на чистом Си из языка C# передать в неё struct со значениями и получить в возвращаемом значении тот же struct?
Функция и структура в C#:
public struct Structure
{
public int a;
public char c;
public string s;
public double d;
public float f;
}
[DllImport("lib")]
public static extern Structure entry (Structure s);
Вызов в коде C#:
var s = entry(
new Structure
{
a = 1,
c = 'c',
f = 2,
d = 3,
s = "abc"
}
);
выдаёт исключение при обращении к методу entry:
Exception has occurred: CLR/System.AccessViolationException An unhandled exception of type 'System.AccessViolationException' occurred in System.Private.CoreLib.dll: 'Attempted to read or write protected memory. This is often an indication that other memory is corrupt.'
Код библиотеки lib на Си:
struct Structure
{
int a;
char c;
float f;
double d;
char * s;
};
struct Structure entry(struct Structure x);
struct Structure entry(struct Structure x)
{
struct Structure r = x;
return r;
}
void main()
{
}
Команда сборки библиотеки:
gcc -o lib -s -shared -Ofast lib.c -m64
Подкладываю файл lib в /bin/Debug/net5.0/ и запускаю метод C#.
ОБНОВЛЕНО 20.09.2021
Код библиотеки lib.c:
#include <uchar.h>
struct Structure
{
int a;
char c;
float f;
double d;
int * m;
char16_t * s;
};
struct Structure entry(struct Structure x);
struct Structure entry(struct Structure x)
{
struct Structure r = x;
short unsigned int t[6] = {1,2,3,4,5,6};
int t1[2] = {1,0};
r.s=(short unsigned int *)t;
r.m = (int*)t1;
return r;
}
void main()
{
}
Код C#:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct Structure
{
public int a;
public char c;
public double d;
public float f;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public int[] m;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
public string s;
}
[DllImport("lib")]
public static extern Structure entry(Structure s);
Код вызова C#:
var s = entry(new Structure { a = 1, c = 'c', f = 2, d = 3, m = new int[] { 5, 7 }, s = "UTF-16" });
Некорректный результат:
s.m[0] = 5; s.m[1] = случайный результат; s.s = случайный результат.
Ответы (1 шт):
После комментариев коллег публикую полный ответ. Код проверялся в Linux x64 и .NET 5. Упрощённая команда сборки библиотеки:
gcc -o lib -s -shared lib.c
ВАРИАНТ 1
Код lib.c
#include <string.h>
struct Structure
{
int a;
char c;
double d;
float f;
int m[2];
char s[6];
};
void entry(struct Structure* x);
void entry(struct Structure* x)
{
memcpy(x->m, (int[2]){1, 2}, sizeof x->m);
strncpy(x->s,"abcde\0",6);
}
void main()
{
}
Код объявления C#
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Structure
{
public int a;
public char c;
public double d;
public float f;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I4, SizeConst = 2)]
public int[] m;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
public string s;
}
[DllImport("lib")]
public static extern void entry([MarshalAs(UnmanagedType.Struct), In, Out] ref Structure s);
Код вызова C#
var parameter = new Structure { a = 1, c = 'c', f = 2, d = 3, m = new int[] { 5, 7 } , s = "UTF-1\0" };
entry(ref parameter);
ВАРИАНТ 2
Код lib.c
#include <uchar.h>
#include <string.h>
struct Structure
{
int a;
char c;
double d;
float f;
int m[2];
char16_t s[6];
};
struct Structure entry(struct Structure x);
struct Structure entry(struct Structure x)
{
char16_t string[6] = u"abcdef";
memcpy(x.s, string, sizeof(string));
return x;
}
void main()
{
}
Код объявлений C#
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct Structure
{
public int a;
public char c;
public double d;
public float f;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I4, SizeConst = 2)]
public int[] m;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
public string s;
}
[DllImport("lib")]
[return: MarshalAs(UnmanagedType.Struct)]
public static extern Structure entry([MarshalAs(UnmanagedType.Struct), In] Structure s);
Код вызова C#
Structure structure = entry(new Structure { a = 1, c = 'c', f = 2, d = 3, m = new int[2] { 5, 7 }, s = "UTF-16" });
Интересно, что строку меньшей длины из программы на Си возвращать можно, а большей, чем SizeConst, не получится - будет обрезана.