передача переменной-дескриптора в неуправляемую .dll в C / CLI

#pointers #c -cli #unmanaged #wrapper #handle

#указатели #c -cli #неуправляемый #оболочка #дескриптор

Вопрос:

Я пытаюсь обернуть неуправляемую c dll, которая взаимодействует с картой видеозахвата в c / CLI, чтобы я мог ссылаться на функции из проекта c #, который у меня есть. У меня возникли проблемы с запуском первого завершенного вызова, поскольку я новичок в синтаксисе c / cli. вот что у меня есть.

вот объявление функции, которое я пытаюсь обернуть.

 __declspec(dllimport) BOOL AZ_DeviceCreate(HANDLEamp; hLiveEvent, DWORD* hEncoderEvent, DWORD* pdwEncoderAddress, HANDLEamp; hAudioEvent, DWORDamp; dwAudioAddress);
  

вот мой файл c / cli .h

 namespace CaptureLibrary 
{
    public ref class CaptureCard
    {
    public:
        HANDLE m_hLiveEvent;
        DWORD *m_hEncoderEvent;
        HANDLE m_hAudioEvent;

    public:
        CaptureCard();
        bool CreateDevice();
        void DisposeDevice();
    };
}
  

и мой .cpp

 namespace CaptureLibrary
{
    CaptureCard::CaptureCard()
    {
        m_hLiveEvent = INVALID_HANDLE_VALUE;

        m_hEncoderEvent = new DWORD[MAX_VIDEO_CHANNEL];
        for (BYTE i=0;i<MAX_VIDEO_CHANNEL;i  )
        {
            m_hEncoderEvent[i] = (DWORD)INVALID_HANDLE_VALUE;
        }

        m_hAudioEvent = INVALID_HANDLE_VALUE;
    }

    bool CaptureCard::CreateDevice()
    {
        DWORD dwEncoderBuff[MAX_VIDEO_CHANNEL];
        DWORD dwACaptureBuffer = 0;

        if(AZ_DeviceCreate(m_hLiveEvent, m_hEncoderEvent, dwEncoderBuff, m_hAudioEvent, dwACaptureBuffer)==FALSE)
        {
            return false;
        }

        return true;
    }

    void CaptureCard::DisposeDevice()
    {
        AZ_DeviceClose();
    }
}
  

когда я компилирую это с требуемыми заголовками, я получаю эту ошибку:

ошибка C2664: 'AZ_DeviceCreate' : не удается преобразовать параметр 1 из 'HANDLE' в 'HANDLE amp;'

Кто-нибудь может мне помочь, поскольку я знаю, что это глупая синтаксическая вещь, которую я делаю неправильно.

Заранее спасибо.

Ответ №1:

Я имею в виду конструктивно: вы встали не с той ноги. Ваша цель с помощью C / CLI здесь — обернуть неуправляемую библиотеку способом, который не будет казаться чужеродным в .NET, но ваш CaptureCard класс этого не делает.

  • Не предоставляйте поля, предоставляйте свойства (я предполагаю, что они должны быть доступны только для CaptureCard членов)
  • Не предоставляйте необработанные типы указателей (например, HANDLE ), предоставляйте IntPtr
  • Не предоставляйте необработанные C-массивы (например, DWORD* ), не предоставляйте array<T>^ , ReadOnlyCollection<T>^ или IEnumerable<T>^ (но не предоставляйте array<T>^ массивы, предназначенные только для чтения, через свойства, только через методы Array::Copy )
  • Не только предоставляйте DisposeDevice метод, но и фактически реализуйте класс, IDisposable чтобы устройство можно было закрыть с помощью using инструкции, а не принудительно использовать try..finally
  • Поскольку класс управляет неуправляемыми ресурсами, ему нужен завершитель

.h:

 namespace CaptureLibrary 
{
    public ref class CaptureCard sealed
    {
    public:
        CaptureCard();
        ~CaptureCard();
        !CaptureCard();

        property IntPtr LiveEvent { IntPtr get(); }
        property IEnumerable<DWORD>^ EncoderEvent { IEnumerable<DWORD>^ get(); }
        property IntPtr AudioEvent { IntPtr get(); }

        bool CreateDevice();
        void DisposeDevice();

    private:
        bool m_bOpened;
        IntPtr m_hLiveEvent;
        array<DWORD>^ m_hEncoderEvent;
        IntPtr m_hAudioEvent;
    };
}
  

.cpp:

 namespace CaptureLibrary
{
    CaptureCard::CaptureCard()
      : m_hLiveEvent(INVALID_HANDLE_VALUE),
        m_hEncoderEvent(gcnew array<DWORD>(MAX_VIDEO_CHANNEL)),
        m_hAudioEvent(INVALID_HANDLE_VALUE)
    {
        for (int i = 0, i_max = m_hEncoderEvent->Length; i != i_max;   i)
            m_hEncoderEvent[i] = reinterpret_cast<DWORD>(INVALID_HANDLE_VALUE);
    }

    CaptureCard::~CaptureCard()
    {
        this->!CaptureCard();
    }

    CaptureCard::!CaptureCard()
    {
        DisposeDevice();
    }

    IntPtr CaptureCard::LiveEvent::get()
    {
        return m_hLiveEvent;
    }

    IEnumerable<DWORD>^ CaptureCard::EncoderEvent::get()
    {
        return m_hEncoderEvent;
    }

    IntPtr CaptureCard::AudioEvent::get()
    {
        return m_hAudioEvent;
    }

    bool CaptureCard::CreateDevice()
    {
        DisposeDevice();

        DWORD dwAudioAddress = 0u;
        DWORD dwEncoderAddress[MAX_VIDEO_CHANNEL];

        HANDLE hLiveEvent = m_hLiveEvent.ToPointer();
        HANDLE hAudioEvent = m_hAudioEvent.ToPointer();
        {
            pin_ptr<DWORD> hEncoderEvent = amp;m_hEncoderEvent[0];
            m_bOpened = AZ_DeviceCreate(hLiveEvent, hEncoderEvent, dwEncoderAddress, hAudioEvent, dwAudioAddress) == TRUE;
        }
        m_hLiveEvent = IntPtr(hLiveEvent);
        m_hAudioEvent = IntPtr(hAudioEvent);

        return m_bOpened;
    }

    void CaptureCard::DisposeDevice()
    {
        if (m_bOpened)
        {
            AZ_DeviceClose();
            m_bOpened = false;
        }
    }
}
  

Предложения по дальнейшему улучшению:

  • Полностью избавьтесь от CreateDevice и DisposeDevice . Этот код очень похож на менталитет C; Пользователи .NET ожидают, что созданный объект будет иметь значимое значение без вызова отдельной функции инициализации, поэтому, предполагая, что AZ_DeviceCreate не ожидается регулярного сбоя, логика CreateDevice должна идти прямо в конструкторе класса, и при сбое должно быть сгенерировано исключение
  • Если вызов AZ_DeviceClose несколько раз безвреден, тогда избавьтесь от m_bOpened полностью

Комментарии:

1. Вау, это огромная помощь! Спасибо за вашу помощь. Это, так сказать, действительно помогло мне преодолеть трудности. Еще раз, спасибо!

Ответ №2:

Проблема здесь в том, что вы пытаетесь передать m_hLiveHandle в качестве ссылки (т. Е. HANDLE amp; ), но для этого потребовалось бы, чтобы на m_hLiveHandle мог указывать собственный указатель (т. Е. он гарантированно не перемещался бы в памяти). Однако m_hLiveHandle является членом класса ref (CaptureCard), что означает, что его экземпляры хранятся в управляемой куче. Это, в свою очередь, означает, что экземпляр CaptureCard может быть перемещен в памяти (действием сборки мусора). Итак, если вы хотите использовать m_hLiveHandle в качестве параметра указателя или параметра ссылки, вам придется использовать pin_ptr, чтобы сообщить сборщику мусора не перемещать этот объект во время выполнения вызова собственного метода. Подробнее читайте здесь: http://msdn.microsoft.com/en-us/library/1dz8byfh (v = против 80).aspx