C # - вызов доб. Функция DLL, содержащая параметр Delphi «вариант записи» - PullRequest
0 голосов
/ 30 мая 2010

Во внешней (созданной Delphi) DLL у меня есть следующая функция, которую мне нужно вызвать из приложения C #.

function ReadMsg(handle: longword; var Msg: TRxMsg): longword; stdcall; external 'MyDll.dll' name 'ReadMsg';

Тип "TRxMsg" - это вариант записи, определяемый следующим образом:

TRxMsg = record
    case TypeMsg: byte of
        1: (accept, mask: longword);
        2: (SN: string[6]);
        3: (rx_rate, tx_rate: word);
        4: (rx_status, tx_status, ctl0, ctl1, rflg: byte);
end;

Чтобы вызвать функцию из C #, я объявил вспомогательную структуру «my9Bytes», содержащую массив байтов, и определил, что ее следует маршалировать как массив длиной 9 байтов (что в точности соответствует размеру записи Delphi).

private struct my9Bytes {
    [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 9)]
    public byte[] data;
}

Затем я объявил импортированную функцию «ReadMsg», используя структуру «my9bytes».

[DllImport("MyDll.dll")]
private static extern uint ReadMsg(uint handle, ref my9Bytes myMsg);

Я могу без проблем вызвать функцию ... Затем мне нужно создать структуру, соответствующую исходной записи варианта "TRxMsg", и преобразовать мой вспомогательный массив "myMsg" в эту структуру.

Я не знаю никакого C # эквивалента вариантного массива Delphi, поэтому я использовал наследование и создал следующие классы.

public abstract class TRxMsg {
    public byte typeMsg;
}
public class TRxMsgAcceptMask:TRxMsg {
    public uint accept, mask;
    //...
}
public class TRxMsgSN:TRxMsg {
    public string SN;
    //...
}
public class TRxMsgMRate:TRxMsg {
    public ushort rx_rate, tx_rate;
    //...
}
public class TRxMsgStatus:TRxMsg {
    public byte rx_status, tx_status, ctl0, ctl1, rflg;
    //...
}

Наконец, я создаю соответствующий объект и инициализирую его значениями, преобразованными вручную из массива "myMsg" (для этого я использовал BitConverter).

Это работает нормально, это решение кажется мне слишком сложным, и что должно быть возможно сделать это как-то более напрямую, без вспомогательных структур "my9bytes" или наследования и ручного преобразования отдельных значений. Поэтому я хотел бы попросить вас предложить наилучший способ сделать это.

Большое спасибо!

Ответы [ 3 ]

1 голос
/ 30 мая 2010

Обычно вы обрабатываете это с помощью атрибута [StructLayout (LayoutKind.Explicit)] в объявлении структуры. С отдельными декларациями структуры для членов объединения, в данном случае. Что бы почти сработало бы, если бы не член SN. CLR не позволит вам наложить строку или член массива, он предоставит неограниченный доступ к куче мусора.

Это дурак функции, здесь уместно ручное маршалинг.

1 голос
/ 30 мая 2010

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

Вы можете расположить целевые типы (классы или структуры) с помощью явного макета:

http://en.csharp -online.net / Common_Type_System% E2% 80% 94Explicit_Layout_Example

И затем скопировать полученные данные на них, используя Marshal.PtrToStructure:

http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx

0 голосов
/ 30 мая 2010

Вы пробовали класс System.Runtime.InteropServices.Marshal?Он предоставляет множество методов Readxxx для доступа к неуправляемой памяти.

...