Interop C # для C ++ struct - PullRequest
       15

Interop C # для C ++ struct

3 голосов
/ 24 марта 2011

Я пытаюсь вызвать какой-то устаревший код на C, используя взаимодействие в C #.Я пока не слишком знаком с тем, как interop работает на C #, но мне приходится работать с некоторыми запутанными структурами.Я получил часть его работы, но адрес портится, когда я пытаюсь передать структуру в слой C.

Я пытаюсь передать структуру в код C, он что-то с этим сделает, и мне нужночтобы получить результат обратно

У меня есть эти структуры в C #

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct RETURNBUFFER
    public IntPtr records; //this is the struct RECORD
    public IntPtr infoA; // this is the struct INFO
    public IntPtr infoB; 
    public int number;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct INFO
{
    public IntPtr doc; //this is a handle in C code
    public int cpFirst; 
    public int cpLim; 
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct RECORD
{
    public int size;
}

Записи на самом деле являются указателями на другую структуру STATS, определенную в C #, например,

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct STATS 
{
    public int size;
    public int a;
    public int b;
    public int c;
    public int d;
    public int e;
    public int f;
    public int g;
}

в слое C # я создаю структуру, подобную следующей:

        RETURNBUFFER returnBuffer = new RETURNBUFFER();
        returnBuffer.infoA = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(INFO)));
        returnBuffer.infoB = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(INFO)));
        returnBuffer.records = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(STATS)));

Когда я запускаю свой код, я смог извлечь только первый элемент в returnBuffer, который является returnBuffer.records, все остальные элементы, включаяЗначение int в returnBuffer испорчено.

Я пытаюсь отладить его и посмотреть значение адреса, я обнаружил, что когда код кодирует из C # -> C, адрес смещается

IЯ не уверен, почему адрес отключен,

Вот пример того, что произошло в 64-битной среде

C#
&ReturnBuffer
0x00000000046f05f8
&ReturnBuffer.records
0x00000000046f05f8
&ReturnBuffer.infoA
0x00000000046f0600
&ReturnBuffer.infoB
0x00000000046f0608
&ReturnBuffer.number
0x00000000046f0610

в C, скажем, функция, которую я вызываю, принимает параметр RETURNBUFFER *pReturnBuffer,

я получаю эти адреса,

pReturnBuffer
0x00000000046F05F8 
&pReturnBuffer->records
0x00000000046F05F8 
&pReturnBuffer->infoA
0x00000000046F0600
&pReturnBuffer->infoB
0x00000000046F0610    **This address is off by 8**
&pReturnBuffer->number
0x00000000046F0620 **this is off by 16**

Так что в результате, когда код возвращается к функции C #,

я могу правильно построить returnBuffer.records, но неНевозможно создать ни infoA, ни infoB, ни получить правильное значение для returnBuffer.number

, но я не уверен, что мне здесь не хватает.

=====================================================

Я отредактировал свой код с помощью,Fun Mun Pieng

[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
public struct CRB
{
    [FieldOffset(0)]
    public IntPtr pcstat;//CSTAT
    [FieldOffset(8)]
    public IntPtr caProc;//NLCA
    [FieldOffset(24)] 
    public IntPtr caSent;//NLCA
    [FieldOffset(40)]
    public int cnlch;
}

Теперь адрес совпадает, когда он переходит к C # -> C ++ -> C # Однако я все еще получаю некоторые данные мусора обратно.

Я провел некоторое исследование ивот ошибочное поведение, которое я обнаружил.

в коде C # я делаю вызов следующим образом

IntPtr text = Marshal.StringToCoTaskMemUni ("Я здесь");

legacyFunction(текст, ref returnBuffer)

здесь, когда я вызываю функцию GetHashCode, я получаю следующие значения

returnBuffer.records.GetHashCode()  473881344
returnBuffer.infoA.GetHashCode()  473898944
returnBuffer.infoB.GetHashCode()  473898784

text.GetHashCode() 468770816

по возвращении из функции, эти значения хеш-значений изменяются,

returnBuffer.records.GetHashCode()  543431240
returnBuffer.infoA.GetHashCode()  473799988
returnBuffer.infoB.GetHashCode()  473799988

text.GetHashCode() 473799988

Теперь я могу сделать это, Marshal.PtrToStringUni (checkReturnBuffer.infoA) и я получаю «Я здесь»

C # теперь думает, что и infoA, и infoB такие же, как текст.

==================================================== 2-е редактирование

Структура c ++ на самом деле

typedef struct RETURNBUFFER
{
    RECORD *precord;
    INFO infoA;      
    INFO    infoB;       
    UINT    number;
 } CRB;

Спасибо за ответ, это действительно была моя проблема.

Я был как-то под впечатлением,для каждой структуры / класса / объекта в C ++ я должен сделать эквивалентный IntPtr в C #

Последний вопрос, пока я здесь, поэтому мне не нужно переопределять все структуры в новом вопросе,

для IntPtr в структуре INFO.в C ++ он имеет тип HANDLE

Правильно ли я здесь, чтобы определить его как IntPtr?Это всего лишь дескриптор, но это не * дескрипторная мысль, или я должен просто позволить, чтобы это было значение uint?

Вот что я прочитал с сайта msdn "Помните, любая функция API, которая возвращает или принимаетдескриптор действительно работает с непрозрачным указателем. Ваш код должен маршалировать дескрипторы в Windows как значения System.IntPtr "

Если я определил его как IntPtr,

Как мне выделить для него память?Будет ли корректно приведенное ниже?

returnBuffer.infoA.doc = Marshal.AllocCoTaskMem(System.IntPtr.Size);

для демаршализации

Marshal.PtrToStructure (returnBuffer.infoA.doc, typeof (IntPtr));

- это правильный подход?

Спасибо большое

Ответы [ 3 ]

2 голосов
/ 24 марта 2011

Это может быть вызвано тем, что:

  1. ваш C ++ скомпилирован с выравниванием 16 байтов
  2. ваш тип для infoA отличается в C ++ и C #, или размер типов различается

возможные решения включают в себя:

[FieldOffset(24)] public IntPtr infoB;

OR

сравнение IntPtr.Size с sizeof(infoB) на C ++


Редактировать : Кажется, что infoA составляет 16 байтов, но ваше объявление INFO не 16 байтов. Весьма вероятно, что ваши объявления на C # и C ++ отличаются. Было бы хорошо, если бы вы включили в вопрос свои объявления C ++.

Между тем, я могу только догадываться, что лучшим совпадением будет:

public struct RETURNBUFFER
{
    public RECORD records; //this is the struct RECORD
    public INFO infoA; // this is the struct INFO
    public INFO infoB; 
    public int number;
}
2 голосов
/ 24 марта 2011

Ваше предположение, что RETURNBUFFER содержит структурные указатели, должно быть неверным. Только так infoA и infoB могут занимать 16 байтов. Структура INFO определенно имеет длину 16 байт, я не вижу тип infoB. Таким образом:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct RETURNBUFFER
    public IntPtr records;
    public INFO infoA;
    public DUNNO infoB; 
    public int number;
}

Обновите ваш вопрос объявлениями C, если у вас все еще есть проблемы. Это должно быть легко увидеть из них.

0 голосов
/ 24 марта 2011

Постарайтесь убедиться, что структура win32 и структура c # соответствуют битам (битам).Этого можно добиться, используя точный тип c # для типа win32.

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