Маршалинские строки, указатели в C # (Написание обертки для C DLL в C # с P/Invoke)

В настоящее время я пытаюсь написать оболочку для USB-камеры, которая использует неуправляемую C DLL для интерфейса. Прилагаемое программное обеспечение написано на C ++ с использованием Qt, и я пытаюсь интегрировать эту камеру в приложение C#, которое я уже написал (приложение уже использует другую камеру, которая поставляется с интерфейсом C #). Я прочитал кучу об неуправляемом/управляемом интерфейсе, но я все еще зацикливаюсь на этой конкретной DLL.

У меня возникли проблемы с P/Invoking DLL. Я сделал кучу чтения о том, как справиться с управляемым/неуправляемым кодом, и кажется, что, поскольку у меня есть только функции/параметры (и ничего больше), я не могу написать обертку C ++/CLI, и я вынужден придерживаться P/Invoke. Если это неверно, то помощь с альтернативным методом будет оценена, если будет более эффективной.

Во всяком случае, в настоящее время у меня возникают проблемы с вызовом некоторых (чтение: все) функций. Для примера давайте возьмем эту функцию (взятую из dijSDK.h, заголовочный файл, который будет использоваться при использовании этого интерфейса в неуправляемом C ++ в Qt):

/** \brief Find cameras currently connected to the host
  *
  * \param[out]      pGuidList   List of unique identifiers of connected cameras; memory is allocated by user
  * \param[in,out]   pNumGuids   Pointer to the number of elements in pGuidList to limit the search
  * \n                           Pointer to the number of cameras found
  * \param[in]       mask        optional mask to limit the results in pGuidList
  *
  * \note
  * -    the function lists all supported cameras that are connected to the host having a driver installed
  * -    the cameras are identified by a string in a defined style:
  * \n  [Name of the camera library resp. camera class]:[camera name]:[Serial number of the camera]
  * -    the optional parameter mask may be used to limit the results returned in pGuidList; 
  * \n   mask is in the same style as the results in pGuidList 
  *      therefore the results can be limited to certain camera classes, camera types,
  *      and cameras with a given serial number */

DIJSDK_EXPORT error_t DijSDK_FindCameras(DijSDK_CamGuid* pGuidList, unsigned int* pNumGuids, const DijSDK_CamGuid mask = NULL);

DIJSDK_EXPORT определяется следующим образом:

#ifndef DIJSDK_EXPORT
 #define DIJSDK_EXPORT externC DLLEXPORT
#endif

И два typedefs, используемые в вышеуказанной функции:

/// Return value of all DijSDK functions
/// \note All return values of type error_t can be casted to the enum DijSDK_EErrorCodeList, see header errorlistinstall.h
typedef int error_t;

// unique DijSDK types
/// Globally unique identifier for all supported cameras. It is used to establish a relation between physical and logical cameras.
typedef char   DijSDK_CamGuid[64];

Таким образом, глядя на функцию, пользователь переходит в выделенный массив строк и количество выделенных строк в этом массиве, а функция должна возвращать связку строк и количество возвращаемых строк. Я не знаю, как реализовать это на C #. Я пробовал разные DllImport объявления функции, такие как передача pGuidList как ...

  • StringBuilder

Если переменная, которую я передал, была инициализирована с помощью var = new StringBuilder (64) , а uint - всего 1.

    <Литий> StringBuilder []

Где переменная, которую я передал, была инициализирована с помощью var = new StringBuilder [length] , где length был uint , переданный функции.

  • IntPtr Where the variable I passed in was initialized with Marshal.AllocCoTaskMem(64 * length * Marshal.SystemDefaultCharSize), where length is the same uint as above

И затем передается в pNumGuids как ref uint . Я не передаю аргумент для маски , поскольку он является необязательным, и мне не нужно его использовать.

Я постоянно получаю PInvokeStackImbalance , поскольку подпись управляемого импорта и неуправляемая DLL не совпадают, но я не могу понять, что такое правильная подпись.

У меня есть вопросы обо всех функциях, с которыми я имею дело, но я буду иметь дело с ними по одному. Первой функцией, которую я использую, является функция «init», которая принимает аргумент указателя функции и аргумент указателя void для обратного вызова и связанные с ним данные, но я могу назвать это без аргументов, если мне не нужно использовать обратный вызов и это P/Invoking просто отлично. Функция в этом вопросе - это тот, который мне нужно позвонить, чтобы получить список подключенных устройств. Я думаю, что если я смогу заставить это работать, я смогу выяснить большинство остальных функций.

SDK и документация, с которой я работаю, не являются общедоступными в Интернете из компании, которая ее поставляет. Если кто-нибудь захочет, чтобы он увидел весь код/​​документацию, я могу опубликовать его на файловом сервере и опубликовать ссылку на него.

Любая помощь приветствуется! Если есть что-то, что я забыл, не забудьте меня обмануть. = P

Solution: After the help, I managed to get it working:

DllImport:

[DllImport(DLL,CallingConvention=CallingConvention.Cdecl)]
        public static extern error_t DijSDK_FindCameras([Out] DijSDK_CamGuid[] pCamGuid, ref uint pNumGuids, string mask);

Структура, используемая для DijSDK_CamGuid:

[StructLayout(LayoutKind.Sequential,Size=64)]
public struct DijSDK_CamGuid
{
    [MarshalAs(UnmanagedType.ByValArray,SizeConst=64)]
    public char[] id;
}

И функция findCameras() :

private void findCameras()
{
     uint cameralistlen = 5;
     DijSDK_CamGuid[] cameralist = new DijSDK_CamGuid[cameralistlen];
     result = Jenoptik.DijSDK_FindCameras(cameralist, ref cameralistlen, null);
}
1
nl ja de

2 ответы

Вам нужно иметь дело с идентификатором в управляемом коде как строку? Если нет, возможно, что-то подобное будет работать, рассматривая байты как непрозрачный массив байтов:

[StructLayout(LayoutKind.Sequential)]
public struct DijSDK_CamGuid
{
  [MarshalAs(UnmanagedType.ByValAray,SizeConst=64)]
  public byte[] id;
}

Затем объявите свою подпись p/invoke для использования параметра типа:

DijSDK_CamGuid* 
1
добавлено
Да, возможно, есть некоторые MarshalAs mumbjo-jumbo, которые будут работать со строкой, так как это было самым быстрым, о котором я мог думать. Вы также можете получить вдохновение отсюда: stackoverflow.com/questions/10320502/…
добавлено автор jlew, источник
В самом деле? DijSDK_FindCameras похоже, что он возвращает список DijSDK_CamGuids, а не один.
добавлено автор jlew, источник
пока я получаю данные в управляемом коде, я думаю, что смогу справиться. Я бы хотел это строка, но я могу преобразовать массивы char/byte в строки, так что это может сработать. Я сделаю это. Благодаря!
добавлено автор Shamtam, источник
Таким образом, это не работает, поскольку я не могу взять указатель на struct (указатель управляемого типа). Я помню, как читал о прохождении массивов структур, поэтому я рассмотрю это.
добавлено автор Shamtam, источник
Я пытаюсь просто передать один DijSDK_CamGuid (вместо массива или указатель на один), а также передать в uint со значением «1» и все равно получить неуравновешенный стек. Я потерял = _ =
добавлено автор Shamtam, источник
Это значит, что для устранения неполадок я пытался передать один. В любом случае это не сработало, но я думаю, что чужой ответ может прояснить ситуацию (с помощью атрибута CallingConvention).
добавлено автор Shamtam, источник

Попробуйте объявить так:

    [DllImport("whatever.dll", CallingConvention = CallingConvention.Cdecl)]
    extern public static int DijSDK_FindCameras(byte[] pGuidList, ref int pNumGuids, byte[] mask);

и используя его вот так:

    byte[] CamGuids = new byte[64 * 32];   //Up to 32 cameras
    int GuidsCount = CamGuids.Length/64;
    int result = DijSDK_FindCameras(CamGuids, ref GuidsCount, null);
1
добавлено
Я добавил [Out] в декларацию, но я думаю, что «CallingConvention» сделал это!
добавлено автор Shamtam, источник
Теперь команда запустилась, но массив байтов был инициализирован 0s (а счетчик также был 0). Возможно, он работает, но мне нужно убедиться, что камера правильно общается.
добавлено автор Shamtam, источник
Камера работает нормально в прилагаемом программном обеспечении, функция просто изменяет оба входа в нули/нуль.
добавлено автор Shamtam, источник
ПОНЯЛ. Обновление вопроса с кодом, который работал для меня.
добавлено автор Shamtam, источник
Microsoft Stack Jobs
Microsoft Stack Jobs
1 788 участник(ов)

Work & freelance only Microsoft Stack. Feed https://t.me/Microsoftstackjobsfeed Чат про F#: @Fsharp_chat Чат про C#: @CSharpChat Чат про Xamarin: @xamarin_russia Чат общения:@dotnettalks

Microsoft Developer Community Chat
Microsoft Developer Community Chat
584 участник(ов)

Чат для разработчиков и системных администраторов Microsoft Developer Community. __________ Новостной канал: @msdevru __________ Баним за: оскорбления, мат, рекламу, флуд, флейм, спам, NSFW контент, а также большое количество оффтоп тем. @banofbot