Pinvoke перевод структуры с C ++ - PullRequest
1 голос
/ 28 октября 2011

Ниже приведен фрагмент кода C ++, который проверен на работоспособность:

typedef struct
{
  PVOID  buffer;
  UINT32 length;
} DATA_BUFFER;

typedef struct 
{
  DATA_BUFFER TxBuf [1];
  DATA_BUFFER RxBuf [1];
} JVM_COMM_BUFFER;

UINT32 SendAndRecv(
  IN    JHI_HANDLE        handle,
  IN    CHAR*             AppId,
  INOUT JVM_COMM_BUFFER* pComm
);

Вот моя попытка перенести это на C #:

    [StructLayout(LayoutKind.Sequential)]
    public struct DATA_BUFFER
    {
        public byte[] buffer;
        public uint length;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct JVM_COMM_BUFFER
    {
        public DATA_BUFFER TxBuf;
        public DATA_BUFFER RxBuf;
    }

    [DllImport("jhi.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
    public static extern UInt32 SendAndRecv(IntPtr handle, string AppId, ref JVM_COMM_BUFFER pComm);

В C # нет исключения, генерируемого при сортировке, но результаты не совпадают для версий C ++ и C #. Есть идеи о том, что мне не хватает?

Ответы [ 2 ]

0 голосов
/ 31 октября 2011

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

0 голосов
/ 28 октября 2011

Проблема, вероятно, в вашей первой структуре.Определение C ++ включает указатель на буфер данных, но маршаллер не может напрямую преобразовать это в .NET byte[] (или, идя в другом направлении, он, вероятно, преобразует byte[] в недопустимый указатель, таким образом, ваши недопустимые параметрыошибка).Вместо этого вы можете сделать это вручную в два этапа:

[StructLayout(LayoutKind.Sequential)]
public struct DATA_BUFFER
{
    public IntPtr buffer;
    public uint length;
}

и затем прочитать буфер вручную, используя Marshal (это просто быстрый пример из моей памяти API Marshal):

var txBufBytes = new byte[pComm.TxBuf.length];
Marshal.Copy(pComm.TxBuff.buffer, 0, pComm.TxBuf.length);

Кроме того, как @svick упомянул в комментариях, вам, вероятно, потребуется установить CharSet в CharSet.Ansi, если ваш нативный код принимает не-юникод / ​​широкие символы.

Наконец, определение C ++ второй структуры, похоже, определяет одноэлементные массивы, которые, в свою очередь, могут фактически быть указателями, а не иметь структуры в памяти.В этом случае вам, вероятно, придется заменить определение, чтобы использовать IntPtr s для взаимодействия, а затем использовать Marshal.PtrToStructure для получения фактических структур.

Вы должны по крайней мере сравнить, что размеры структурто же самое, сравнивая результаты sizeof(...) в C ++ с Marshal.SizeOf(...) в C # для определений структуры.

Учитывая то, что вы сказали в комментариях, вы сможете использовать модификацию DATA_BUFFERЯ описал выше, и оригинальное определение структуры, которое вы использовали для JVM_COMM_BUFFER

[StructLayout(LayoutKind.Sequential)]
struct JVM_COMM_BUFFER
{
    public DATA_BUFFER TxBuf;
    public DATA_BUFFER RxBuf;
}

, объедините это с небольшим изменением вашего DllImport

[DllImport("jhi.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern UInt32 SendAndRecv(IntPtr handle, string AppId, [In, Out] ref JVM_COMM_BUFFER pComm);

CharSet.Ansi важнодля гарантии того, что ваша строка .NET будет правильно распределена в строку ANSI (при условии, что ваша нативная функция C не ожидает тип строки wchar_t).Атрибуты [In, Out] могут не требоваться, но могут указывать маршаллеру, как правильно управлять этим параметром.

Если JVM_COMM_BUFFER действительно INOUT, и вы предварительно заполняете его даннымиперед вызовом функции может потребоваться убедиться, что все данные верны.Вызываемая функция может иметь документацию о том, какие значения она ожидает от своих параметров.Однако приведенные здесь определения должны корректно распределяться на основе предоставленных вами определений C ++.

...