10769

passing a HANDLE variable to an unmanaged .dll in C++/CLI

Question:

I am trying to wrap an unmanaged c++ dll that talks to a video capture card in c++/CLI so i can reference the functions from a c# project that i have. I am having trouble getting the 1st wrapped call to work as I am new to c++/cli syntax. here is what i have.

here is the function declataion i am trying to wrap.

__declspec(dllimport) BOOL AZ_DeviceCreate(HANDLE& hLiveEvent, DWORD* hEncoderEvent, DWORD* pdwEncoderAddress, HANDLE& hAudioEvent, DWORD& dwAudioAddress);

here is my c++/cli .h file

namespace CaptureLibrary { public ref class CaptureCard { public: HANDLE m_hLiveEvent; DWORD *m_hEncoderEvent; HANDLE m_hAudioEvent; public: CaptureCard(); bool CreateDevice(); void DisposeDevice(); }; }

and my .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(); } }

when i compile this with the required headers, i get this error:

<blockquote>

error C2664: 'AZ_DeviceCreate' : cannot convert parameter 1 from 'HANDLE' to 'HANDLE &'

</blockquote>

Can anyone help me as I know this is a stupid syntax thing that I am doing wrong.

Thanks in advance.

Answer1:

I mean this constructively: you're off on the wrong foot. Your goal with C++/CLI here is to wrap the unmanaged library in a manner that won't seem foreign in .NET, but your CaptureCard class doesn't do that.

<ul><li>Don't expose fields, expose properties (I assume they should be get-only for CaptureCard's members)</li> <li>Don't expose raw pointer types (e.g. HANDLE), expose IntPtr</li> <li>Don't expose raw C-arrays (e.g. DWORD*), expose array<T>^, ReadOnlyCollection<T>^, or IEnumerable<T>^ (but don't expose array<T>^s intended to be read-only via properties, only via methods + Array::Copy)</li> <li>Don't only expose a DisposeDevice method, also make the class actually implement IDisposable so the device can be closed with a using statement rather than forcing use of try..finally</li> <li>As the class controls unmanaged resources, it <strong>needs</strong> a finalizer</li> </ul>

<em><strong>.h</strong></em>:

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; }; }

<em><strong>.cpp</strong></em>:

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 = &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; } } } <hr />

Suggestions for further improvement:

<ul><li>Get rid of CreateDevice and DisposeDevice altogether. This code has a very C-ish mentality; .NET users would expect a constructed object to have a meaningful value without calling a separate initialization function, so assuming AZ_DeviceCreate is not expected to fail regularly then CreateDevice's logic should go straight in the class' constructor and an exception should be thrown upon failure</li> <li>If calling AZ_DeviceClose multiple times is harmless then get rid of m_bOpened altogether</li> </ul>

Answer2:

The problem here is that you are trying to pass m_hLiveHandle as a reference (i.e. HANDLE &), but this would require that m_hLiveHandle could be pointed to by a native pointer (i.e. it would be guaranteed not to move in memory). However, m_hLiveHandle is a member of a <em>ref</em> class (CaptureCard) which means instances of it are stored on the managed heap. This in turn means that the instance of CaptureCard can be moved in memory (by a garbage collection action). So, if you want to use m_hLiveHandle as a pointer parameter or reference parameter, you'd have to use pin_ptr to tell the garbage collector not to move this object during the duration of the call to the native method. Read up here for more: <a href="http://msdn.microsoft.com/en-us/library/1dz8byfh(v=vs.80).aspx" rel="nofollow">http://msdn.microsoft.com/en-us/library/1dz8byfh(v=vs.80).aspx</a>

Recommend

  • Find method in std::wstring
  • Cannot understand how to add new object to std::list
  • cannot convert parameter 5 from 'SIZE_T *' to 'size_t *' — why?
  • CALLBACK macro (QT)
  • How to create two column output from a single column
  • Symfony2 - Doctrine - no changeset in post update
  • Display validation errors inside
  • Rest Services conventions
  • Group list of tuples by item
  • How to calculate the camera position from Vuforia GL matrix?
  • NUnit 3.0 TestCase const custom object arguments
  • Default parameter as generic type
  • Convert SQLite database to XML
  • WPF Visiblity Binding to Boolean Expression with multiple Variables
  • Does Mobilefirst provide a provision to access web services directly?
  • Conversion from string “a” to type 'Boolean' is not valid
  • custom UITableViewCell with image for highlighting
  • Groovy: Unexpected token “:”
  • Content-Length header not returned from Pylons response
  • Meteor: Do Something On Email Verification Confirmation
  • PHPUnit_Framework_TestCase class is not available. Fix… - Makegood , Eclipse
  • java.lang.NoClassDefFoundError: com.parse.Parse$Configuration$Builder on below Lollipop versions
  • Read text file and split every line in MSBuild
  • Where to put my custom functions in Wordpress?
  • How to apply VCL Styles to DLL-based forms in Inno Setup?
  • Warning: Can't call setState (or forceUpdate) on an unmounted component
  • R: gsub and capture
  • RestKit - RKRequestDelegate does not exist
  • Arrays break string types in Julia
  • Codeigniter doesn't let me update entry, because some fields must be unique
  • WPF Applying a trigger on binding failure
  • Benchmarking RAM performance - UWP and C#
  • Hits per day in Google Big Query
  • FormattedException instead of throw new Exception(string.Format(…)) in .NET
  • Qt: Run a script BEFORE make
  • Linking SubReports Without LinkChild/LinkMaster
  • Can't mass-assign protected attributes when import data from csv file
  • XCode 8, some methods disappeared ? ex: layoutAttributesClass() -> AnyClass
  • Does armcc optimizes non-volatile variables with -O0?
  • Net Present Value in Excel for Grouped Recurring CF