#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