Союзы в C # - неправильно выровнены или перекрыты необъектным полем - PullRequest
2 голосов
/ 12 января 2011

Я выполняю маршалинг через PInvoke на собственный Cll, который ожидает следующий вызов.

private static extern int externalMethod(IntPtr Data, [MarshalAs(UnmanagedType.U4)] ref int dataLength);

Параметр dataLength - это длина структуры, передаваемой через параметр данных IntPtr.Выдает исключение, если два не совпадают.Внешний метод использует объединение C, объединяющее четыре типа.

Мне удалось воссоздать объединения в C # с помощью FieldOffsetAttribute.Затем я вычисляю длину объединения C # и вызываю метод следующим образом:

int len = Marshal.SizeOf(data);
IntPtr ptr = Marshal.AllocCoTaskMem(len);
externalMethod(ptr, len);

Однако я получаю ошибку System.TypeLoadException : ... Could not load type because it contains an object field at offset 0 that is incorrectly aligned or overlapped by a non-object field. со следующим кодом.Я полагаю, что это может быть одна из строк или целочисленный массив (переменная B7)?Как мне изменить это, чтобы оно работало - нужно ли разбивать массив целых чисел на несколько переменных?

[StructLayoutAttribute(LayoutKind.Explicit)]
public struct Union{
    [FieldOffset(0)]
    public A a;

    [FieldOffset(0)]
    public B b;

    [FieldOffset(0)]
    public C c;

    [FieldOffset(0)]
    public D d;
}

[StructLayout(LayoutKind.Sequential)]
public struct A
{
    public int A1;
    public int A2;
    public int A3;
    [MarshalAs(UnmanagedType.LPTStr, SizeConst = 17)]
    public string A4;
    [MarshalAs(UnmanagedType.LPTStr, SizeConst = 4)]
    public string A5;
}

[StructLayout(LayoutKind.Sequential)]
public struct B
{
    public int B1;
    [MarshalAs(UnmanagedType.LPTStr, SizeConst = 2)]
    public string B2;
    [MarshalAs(UnmanagedType.LPTStr, SizeConst = 4)]
    public string B3;
    [MarshalAs(UnmanagedType.LPTStr, SizeConst = 6)]
    public string B4;
    public int B5;
    public int B6;
    [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U4, SizeConst = 255)]
    public int[] B7;
}

[StructLayout(LayoutKind.Sequential)]
public struct C
{
    public int C1;
    public int C2;
    public int C3;
    public int C4;
    [MarshalAs(UnmanagedType.LPTStr, SizeConst = 32)]
    public string C5;
    public float C6;
    public float C7;
    public float C8;
    public float C9;
    public float C10;
    public float C11;
    public float C12;
    public float C13;
    public float C14;
}

[StructLayout(LayoutKind.Sequential)]
public struct D
{
    public int D1;
    [MarshalAs(UnmanagedType.LPTStr, SizeConst = 36)]
    public string D2;
}

Ответы [ 2 ]

4 голосов
/ 12 января 2011

Просто используйте структуры A / B / C / D напрямую и пропустите объединение. В ваших внешних вызовах просто замените правильную структуру в объявлении метода.

extern void UnionMethodExpectingA( A a );

Если неуправляемые методы фактически принимают объединение и ведут себя по-разному в зависимости от переданного типа, вы можете объявить разные методы extern, которые в конечном итоге вызовут одну и ту же неуправляемую точку входа.

[DllImport( "unmanaged.dll", EntryPoint="ScaryMethod" )]
extern void ScaryMethodExpectingA( A a );

[DllImport( "unmanaged.dll", EntryPoint="ScaryMethod" )]
extern void ScaryMethodExpectingB( B b );

Обновлено для параметра "length". Логика все еще применяется. Просто создайте метод-оболочку и сделайте то же самое.

void CallScaryMethodExpectingA( A a )
{
  ScaryMethodExpectingA( a, Marshal.SizeOf( a ) );
} 
3 голосов
/ 12 января 2011

Трудно ответить на этот вопрос, не зная, чего вы пытаетесь достичь.Явно выложенная структура - очень плохой выбор для любого обычного варианта использования;это имеет смысл, только если вы используете данные в собственных вызовах (pinvoke), и в этих случаях вам определенно не нужно использовать управляемый класс string.Атрибут [MarshalAs] вступает в силу только во время вызовов, а не постоянно каждый раз, когда поле считывается или записывается вашим управляемым кодом.Он не позволяет вам перекрывать строковый указатель с int, потому что это позволит вам установить указатель на бессмысленное значение, а затем доступ к строке приведет к краху CLR.То же самое верно для массивов, поэтому вы также не можете использовать char[].

Если вы являетесь автором нативного кода, который вам нужно вызывать, тогда я настоятельно рекомендую написать четыре отдельных метода вместоодин, который принимает четыре совершенно разные структуры данных.

Если вы не можете изменить собственный код, то вы всегда можете объявить свои четыре структуры A, B, C и D какВы делаете сейчас и просто используете их напрямую, без объединения.Просто объявите четыре разных объявления pinvoke для одной и той же встроенной функции (используйте свойство EntryPoint в атрибуте [DllImport]).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...