Структура маршала от C до C #

1

EDIT: я упростил свой пример... В реальном коде я присваивал значения strMyStringx без правильного использования wcscpy_s, поэтому вместо назначения значений я просто передавал указатель, который был вне области видимости к тому времени, когда значения были маршалированы в управляемый код...

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

Моя пользовательская структура:

typedef struct _MY_STRUCT_STRING {
    LPWSTR strMyString1;
    LPWSTR strMyString2;
    LPWSTR strMyString3;
}MY_STRUCT_STRING, *PMY_STRUCT_STRING;

Моя функция C возвращает массив указателей на эту структуру:

bool bEnumerateString(OUT LONG &i_arr_size, OUT PMY_STRUCT_STRING* &pArrStringStruct)
{
// [...] function simplified to demonstrate building a pointer to an array of struct*
long i_arr_size = 3

PMY_STRUCT_STRING *ptr_arr_string = (PMY_STRUCT_STRING *)malloc(sizeof(PMY_STRUCT_STRING)* i_arr_size);

for (int i = 0; i < i_arr_size; i++) {
    ptr_arr_string[i] = (PMY_STRUCT_STRING)malloc(sizeof(MY_STRUCT_STRING));
    ptr_arr_string[i]->strMyString1 = L"String 1"; // This would work. In the real code I was assigning values from another array and mistakenly passed the pointer rather than doing wcscpy_s
    ptr_arr_string[i]->strMyString2 = L"String 2";
    ptr_arr_string[i]->strMyString3 = L"String 3";
}

pArrStringStruct = ptr_arr_string;

return true;
}

С#:

    //Import the DLL with my function
    [DllImport("My.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "bEnumerateString")]
    internal static extern bool bEnumerateString(out long count, out IntPtr pArrStringStruct);

     // Define the C# equivalent of the C struct
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    internal struct MY_STRUCT_STRING
    {
        [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 8)]
        public string strMyString1;
        [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 8)]
        public string strMyString1;
        [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 8)]
        public string strMyString1;
    }

   [...]

    // Code to marshal (try... catch etc removed for succinctness)
   IntPtr pArrStruct = IntPtr.Zero;
   long lCount = 0;

   bool bResult = false;
   bResult = bEnumerateString(out lCount, out pArrStruct);

   if (!bResult)
   {
    // Marshal to deref pointer
       IntPtr[] pArrStructList = new IntPtr[lCount];
       for (ulong i = 0; i < (ulong)lCount; i++)
       {
            pArrStructList[i] = Marshal.ReadIntPtr(pArrStruct, Marshal.SizeOf(typeof(IntPtr)) * (int)i);
       }


    // Marshal pointers to struct
       var lstMyStringStrct = new List<MY_STRUCT_STRING>(pArrStructList.Length);

       foreach (IntPtr ptr in pArrStructList)
       {         
            lstMyStringStrct.Add((MY_STRUCT_STRING)Marshal.PtrToStructure(ptr, typeof(MY_STRUCT_STRING)));
       }

    // Enumerate struct
       foreach (MY_STRUCT_STRING myStr in lstMyStringStrct)
       {
           // All of these outputs are garbage
           Console.WriteLine("strMyString1: " + myStr.strMyString1);
           Console.WriteLine("strMyString2: " + myStr.strMyString2);
           Console.WriteLine("strMyString3: " + myStr.strMyString3);
        }

    }
  • 0
    LPWSTR - это wchar_t [], а не wchar_t *. Удалите атрибут [MarshalAs]. И обратите внимание на утечку памяти, никто не звонит free () для вас. Вы можете продвинуться вперед только с помощью CoTaskMemAlloc () в своем родном коде вместо malloc () или выставив функцию, которая вызывает free ().
  • 0
    @HansPassant - удаление атрибута MarshalAs заставляет Marshal.PtrToStructure генерировать исключение AccessViolation. Поможет ли использование CoTaskMemAlloc ()? Мне не нужно освобождать память, потому что я пытаюсь устранить неполадки с более узкой областью действия, хотя поможет ли освобождение памяти при маршалинге?
Теги:
marshalling

1 ответ

2
Лучший ответ

Я вижу одну проблему. Структура C++ использует LPWSTR (указатели), тогда как вы - код С#, ожидающий массивы char с фиксированным размером.

Измените свои строки:

[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 8)]
public string strMyString1;

который будет использоваться, когда структура C++ определена как:

char strMyString1[8];

чтобы:

[MarshalAsAttribute(UnmanagedType.LPWStr)]
public string strMyString1;
  • 0
    Это то, что я впервые подумал, однако использование этого атрибута вызывает исключение ExecutionEngineException с Marshal.PtrToStructure
  • 0
    Оказывается, у меня были некоторые неэффективные указатели копирования, вместо того, чтобы делать wcscpy_s, который заставлял это работать в консольном приложении Win32 (так как указатель все еще находился в области видимости), но завершался ошибкой, когда он находился внутри DLL и маршалировался в управляемый код.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню