преобразование байтового массива из нативного метода в управляемую структуру - PullRequest
1 голос
/ 27 сентября 2010

У меня есть приложение ac # .net 2.0 CF, которое взаимодействует с собственной DLL-библиотекой, реализующей такую ​​функцию:

struct NATIVE_METHOD_REPLY
{
    int other_irrelevant_data;
    int data_size;
    void* data;
}

// reply_buffer will contain an array of NATIVE_METHOD_REPLY structures
// and their data.
//
// returns an error code
int Foo(NATIVE_METHOD_REPLY* reply_buffer, int reply_size);

Я реализовал ее в C # следующим образом:

[StructLayout(LayoutKind.Sequential)]
internal struct NATIVE_METHOD_REPLY
{
    public Int32 OtherIrrelevantData;
    public Int16 DataSize;
    public IntPtr DataPtr;
}

[DllImport("my_lib.dll", SetLastError = true)]
internal static extern Int32 Foo(byte[] replyBuffer, Int32 replySize);

public byte[] void Bar()
{
    // data returned to the user. May be an arbitrary size.
    byte[] result_buffer = new byte[256];

    // data sent to Foo() 
    byte[] reply_buffer = 
        new byte[Marshal.SizeOf(typeof(NativeMethods.NATIVE_METHOD_REPLY)) + 
            result_buffer.Length];

    NativeMethods.Foo(reply_buffer, reply_buffer.Length);

    // is there a better way of doing this?

    NativeMethods.NATIVE_METHOD_REPLY reply;
    GCHandle pinned_reply = GCHandle.Alloc(reply_buffer, 
        GCHandleType.Pinned);
    try
    {
        reply = (NativeMethods.NATIVE_METHOD_REPLY)Marshal.PtrToStructure(
            pinned_reply.AddrOfPinnedObject(), 
            typeof(NativeMethods.NATIVE_METHOD_REPLY));

        Marshal.Copy(reply.DataPtr, result_buffer, 0, reply.DataSize);
    }
    finally
    {
        pinned_reply.Free();
    }

    // bonus point*: is this okay to do after the Free() call?
    int test = reply.OtherIrrelevantData;

    return result_buffer;
}

Хотя это работает правильно, я хотел бы знать, является ли это наиболее эффективным / наиболее правильным способом реализации этой функции.

Существует ли какой-либо метод преобразования массива управляемых байтов в управляемую структуру, который не включает промежуточный собственный дескриптор и копию?Например, в C ++ я бы просто сделал это:

NATIVE_METHOD_REPLY* reply = reinterpret_cast< NATIVE_METHOD_REPLY* >( reply.DataPtr );

* Для бонусного балла можно ли использовать данные в структуре после освобождения собственного дескриптора?

Спасибо, PaulH


Редактировать: Обновленное решение

[DllImport("my_lib.dll", SetLastError = true)]
internal static extern Int32 Foo(IntPtr replyBuffer, Int32 replySize);

public byte[] void Bar()
{
    byte[] result_buffer = new byte[256];

    int reply_buffer_len = Marshal.SizeOf(typeof(NativeMethods.NATIVE_METHOD_REPLY)) + result_buffer.Length;
    IntPtr reply_buffer = Marshal.AllocCoTaskMem(reply_buffer_len);
    NativeMethods.NATIVE_METHOD_REPLY reply;

    try        
    {
        NativeMethods.Foo(reply_buffer, reply_buffer_len);

        reply = (NativeMethods.NATIVE_METHOD_REPLY)Marshal.PtrToStructure(
            reply_buffer, 
            typeof(NativeMethods.NATIVE_METHOD_REPLY));

        Marshal.Copy(reply.DataPtr, result_buffer, 0, reply.DataSize);
    }
    finally
    {
        Marshal.FreeCoTaskMem(reply_buffer);
    }

    return result_buffer;
}

Ответы [ 2 ]

1 голос
/ 27 сентября 2010

Структура имеет фиксированный размер. Нет смысла передавать массив, просто передайте структуру:

[DllImport("my_lib.dll", SetLastError = true)]
internal static extern Int32 Foo(out NATIVE_METHOD_REPLY replyBuffer, Int32 replySize);

У вас есть проблема с управлением памятью. Кому принадлежит указатель?


Хорошо, структура на самом деле имеет переменный размер, и указатель указывает на массив. Вам не нужно подходить. Просто выделите кусок неуправляемой памяти заранее, вместо того, чтобы позволить маршаллеру P / Invoke скопировать данные в управляемый массив. Что на самом деле является жестким требованием, поскольку сборщик мусора может перемещать массив, лишая законной силы указатель. Вызовите Marshal.CoTaskMemAlloc (), чтобы зарезервировать память, позже вам придется ее освободить. И измените первый аргумент функции на IntPtr (not out).

Вы также обнаружите, что маршалинг структуры намного проще, нет необходимости закреплять память. Не забудьте Marshal.FreeCoTaskMem (), когда закончите.

0 голосов
/ 27 сентября 2010

В C # под полной структурой вы можете упорядочить массив напрямую.См. Маршалинг по умолчанию для массивов .Я не знаю, каковы ограничения на Compact Framework.

...