Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>I will insist on type-safe principles: I know there's already <code>Marshal.GetFunctionPointerForDelegate</code> but that requires function pointer type cast in C++/CLI and hides how marshalling unmanaged->managed works (debugging is much harder and I don't like not understanding what's happening behing the scene). Just noticed the approach is similar to <a href="https://stackoverflow.com/questions/4982404/c-cli-class-wrapper-for-c-library-callbacks">this</a> but doesn't need a managed native class (less overhead). Please, tell me if you know how to further simplify it (mantaining type safety and marshaling control) and reduce overhead.</p> <p>The following is the C++/CLI <code>Wrapper.h</code> header:</p> <pre><code>#include &lt;gcroot.h&gt; using namespace System; using namespace System::Runtime::InteropServices; namespace LibraryWrapper { // Declare the cdecl function that will be used void cdecl_allocate_buffer(void *opaque, void **buffer); public ref class Library { public: // The BufferAllocator delegate declaration, available to any clr language // [In, Out] attributes needed (?) to pass the pointer as reference delegate void BufferAllocator([In, Out] IntPtr% buffer); internal: // The stored delegate ref to be used later BufferAllocator ^_allocate_buffer; private: // Native handle of the ref Library class, castable to void * gcroot&lt;Library^&gt; *_native_handle; // C library context lib_context_base *_lib_context_base; public: Library(); ~Library(); // The clr callback setter equivalent to the C counterpart, don't need // the context because in CLR we have closures void SetBufferAllocateCallback(BufferAllocator ^allocateBuffer); }; } </code></pre> <p>Follows C++/CLi <code>Wrapper.cpp</code> defines:</p> <pre><code>#include "wrapper.h" namespace LibraryWrapper { Library::Library() { // Construct the native handle _native_handle = new gcroot&lt;Library^&gt;(); // Initialize the library base context _lib_context_base = new_lib_context_base(); // Null the _allocate_buffer delegate instance _allocate_buffer = nullptr; } Library::~Library() { free_lib_context_base(_lib_context_base); delete _native_handle; } void Library::SetBufferAllocateCallback(BufferAllocator ^allocateBuffer) { _allocate_buffer = allocateBuffer; // Call the C lib callback setter. Use _native_handle pointer as the opaque data set_allocate_buffer_callback(_lib_context_base, cdecl_allocate_buffer, _native_handle); } void cdecl_allocate_buffer(void *opaque, void **buffer) { // Cast the opaque pointer to the hnative_handle ref (for readability) gcroot&lt;Library^&gt; &amp; native_handle = *((gcroot&lt;Library^&gt;*)opaque); // Prepare a IntPtr wrapper to the buffer pointer IntPtr buffer_cli(*buffer); // Call the _allocate_buffer delegate in the library wrapper ref native_handle-&gt;_allocate_buffer(buffer_cli); // Set the buffer pointer to the value obtained calling the delegate *buffer = buffer_cli.ToPointer(); } } </code></pre> <p>Can be used in this way (C#):</p> <pre><code>// Allocate a ~10mb buffer in unmanaged memory. Will be deallocated // automatically when buffer go out of scope IntPtr _buffer = Marshal.AllocHGlobal(10000000); // Init the library wrapper Library library = new Library(); // Set the callback wrapper with an anonymous method library.SetBufferAllocateCallback(delegate(ref IntPtr buffer) { // Because we have closure, I can use the _buffer variable in the outer scope buffer = _buffer; }); </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload