Преобразование LPDWORD в C# для P/Invoke

У меня есть функция C++ (WinAPI) со следующей подписью:

__declspec(dllimport) LPDWORD WINAPI MyFunction(HDET hDet, WORD wStartChan, WORD wNumChans,
    LPDWORD lpdwBuffer, LPWORD lpwRetChans, LPDWORD lpdwDataMask,
    LPDWORD lpdwROIMask, LPCSTR lpszAuth);

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

::MyFunction((HDET)pUConn->GetHDET(), (WORD)lStartChan, (WORD)lNumChans,
                &(m_DataCache[lView].pdwChans[lStartChan]), &wRetChans,
                &(m_DataCache[lView].dwDataMask), &(m_DataCache[lView].dwROIMask),
                pUConn->GetAuthorization());
//m_DataCache[lView].pdwChans is an array of DWORDs. (Declared as DWORD * pdwChans;)

Теперь я могу маршалировать все параметры, кроме четвертого. Это указатель на массив DWORDs. Это заставляет меня задуматься int **. Однако, когда я прохожу ref int[], я возвращаюсь null. Вот моя декларация С#:

[System.Runtime.InteropServices.DllImport("MCBCIO32.dll", EntryPoint = "MIOGetData")]
public static extern IntPtr MIOGetData(int hDet, ushort sChan, ushort nChan,
ref int[] Buffer, ref short rchan, ref int datamask, ref int roimask, string auth);

Я также пробовал IntPtr и простой int[]. Int[] возвращает массив нулей. IntPtr интересно (интересно, но неправильно) - возвращает мне странные значения, ненулевые, но явно неверные. И они меняются каждый раз, когда я делаю очередной прогон, так что я думаю, что блуждаю в случайной памяти. Я распределяю память для буфера и копирую его следующим образом:

/***Snip***/
int arrsize = 100;
int[] buffer = new int[arrsize];

short rchan = -1;
IntPtr p = Marshal.AllocHGlobal(arrsize * sizeof(int));
int datamask = 0, roimask = 0;
MCBWrapper.FUNC(hDet, 0, 1, p, ref rchan, ref datamask, ref roimask, "");
Marshal.Copy(p, buffer, 0, arrsize);
Marshal.FreeHGlobal(p);
p = IntPtr.Zero;
for (int r = 0; r < arrsize; r++)
{
    Console.Write($"{buffer[r]} ");
}

В этом случае буфер маршалируется как IntPtr.
Я пробовал несколько других способов маршалинга, но без особого эффекта. IntPtr кажется наиболее перспективным. Я также пытался использовать ref для IntPtr (с почти идентичным кодом), и это дает мне результаты, аналогичные одному косвенному обращению.

Буду признателен за любые мысли по этому поводу - я не привык работать с маршалингом.

Я всегда считал, что метод dllimport/pinvoke слишком низкого качества для использования в реальных продуктах. Сборщик мусора может перемещать вещи (поэтому С++ не должен удерживать указатели и т. д.). Я советую вам немного изучить C++/cli (не слишком сложно). введение здесь. Вы также можете прочитать о закреплении памяти при взаимодействии с C++.

Pepijn Kramer 14.06.2023 21:37
It's a pointer to an array of DWORDs - нет, это указатель на DWORD, который может означать или не означать массив DWORDs. Перевод C# этого — [MarshalAs(UnmanagedType.LPArray)] int[] Buffer.
GSerg 14.06.2023 21:45

Массивы уже маршалированы как указатель на их первый элемент. Итак, Buffer должен быть int[], без ссылки.

Hans Passant 14.06.2023 21:45
pinvoke - ответ «нет, это не так» на вопрос «легко ли использовать c#?»
Paul Sanders 14.06.2023 22:01

Спасибо всем. В свою защиту, int[] - это первое, что я попробовал, но nChans функционирует как длина копии, а первые 10 или около того копируемых элементов равны 0. Таким образом, это выглядело так, будто массив возвращается без изменений. Фейспалм.

Isaac 14.06.2023 22:17

Обратите внимание, что LPDWORD* в заголовке вопроса неточен, фактический код C++ просто LPDWORD

Ben Voigt 14.06.2023 22:45

@PaulSanders Я думаю, что взаимодействие с C++ непросто ...

Charlieface 14.06.2023 23:58

@Charlieface Я думаю, это неудобная и подверженная ошибкам квадратная

Paul Sanders 15.06.2023 00:00
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
8
53
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это не ** указатель на указатель. Это просто указатель. Итак, вам нужно пройти в буфер. Так что это int[] не ref int[].

Размер буфера выглядит так, как будто он передается как nChan, а используемая длина буфера передается обратно в rchan. Вы указываете это, используя SizeParamIndex.

Вам также необходимо указать LPStr в последнем строковом параметре.

Использование двух других параметров ref неясно, вероятно, они должны быть либо [In] in, либо [Out] out. Также HDET непонятно, какой это размер.

[DllImport("MCBCIO32.dll")]
public static extern IntPtr MIOGetData(
  int hDet,
  ushort sChan,
  ushort nChan,
  [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] int[] Buffer,
  [Out] out short rchan,
  ref int datamask,
  ref int roimask,
  [MarshalAs(UnmanagedType.LPStr)] string auth);

Вы используете это так

int arrsize = 100;
int[] buffer = new int[arrsize];
int datamask = 0, roimask = 0;

var result = MCBWrapper.FUNC(hDet, 0, 1, buffer, out var rchan, ref datamask, ref roimask, "");

for (int r = 0; r < arrsize; r++)
{
    Console.Write($"{buffer[r]} ");
}

Обратите внимание, что если вам нужно использовать Marshal.AllocHGlobal, вы должны обязательно освободить память в finally, чтобы предотвратить утечки.

Другие вопросы по теме