Skip to content

Instantly share code, notes, and snippets.

@alepez
Created November 5, 2020 11:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alepez/30a7e8ae0b64fa5956acc0f5dd28bf66 to your computer and use it in GitHub Desktop.
Save alepez/30a7e8ae0b64fa5956acc0f5dd28bf66 to your computer and use it in GitHub Desktop.

Unmanaged C code:

typedef void(read_fun_t)(void* user_data, const char* data, int data_len);

int native_consume_all(native_handler handler, void* user_data, read_fun_t* read_fun);

Managed C++ code:

public delegate void ConsumeCallback(String^ str);

public
ref class Example
{
public:
  void ConsumeAll(ConsumeCallback^ callback);
}
/* Some unmanaged code is needed so native_consume_all can
 * call ConsumeCallback delegate */
#pragma unmanaged

typedef void(*NativeConsumeCallbackBridgeDelegate)(const char*, int);

void ConsumeAllHelper(void* p, const char* data, int data_len)
{
    /* Here `p` is a marshalled pointer to ConsumeCallbackBridgeDelegate */
    auto callback = static_cast<NativeConsumeCallbackBridgeDelegate>(p);
    callback(data, data_len);
}

#pragma managed

public delegate void ConsumeCallbackBridgeDelegate(const char*, int);

/* This class is needed as a bridge between the native function ConsumeAllHelper
 * which get a pointer to char, and ConsumeCallback delegate, which handles
 * managed String */
ref class ConsumeCallbackBridge {
public:
    ConsumeCallback^ callback;
    void Call(const char* nativeStr, int nativeStrLen) {
        /* Marshal works with null terminated strings, so here we are creating
         * a temporary null terminated string */
        std::string tmpStr(nativeStr, nativeStr + nativeStrLen);
        System::String^ managedStr = msclr::interop::marshal_as<System::String^>(tmpStr.c_str());
        callback->Invoke(managedStr);
    }
};

void Example::ConsumeAll(ConsumeCallback^ callback)
{
    /* To have the delegate ConsumeCallback being called by
     * native_consume_all we have to play with pointers.
     * ConsumeCallbackBridge is also needed to convert pointer to
     * char to a managed String. */
    ConsumeCallbackBridge^ bridge = gcnew ConsumeCallbackBridge();
    bridge->callback = callback;
    ConsumeCallbackBridgeDelegate^ del = gcnew ConsumeCallbackBridgeDelegate(bridge, &ConsumeCallbackBridge::Call);
    IntPtr ip = Marshal::GetFunctionPointerForDelegate(del);

    /* rfid_manager_consume_all accept a pointer to anything that will be
     * passed to the callback as the first parameter. Here we pass the
     * delegate as a raw pointer, so it can be cast back to a function
     * pointer in ConsumeAllHelper */
    native_consume_all(handler, ip.ToPointer(), ConsumeAllHelper);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment