Search code examples
c#c++.netunmanagedopenframeworks

Retrieve C# .NET image from C++ unmanaged application


I'm using openFrameworks to render a video stream. OpenFrameworks uses the ofImage class, which stores the images as a vector of ofPixel (which stores an unsigned char vector).

I am using a C# .NET dll to retrieve each frame. This library uses Bitmap to return the retrieved frames. So far I know C# .NET works under the Common Language Runtime.

My question is, what .NET tools or functionalities can I use in order to comunicate native C++ code and managed C#?


Solution

  • In order to retrieve the image I used this tutorial. It thoroughly explains how to consume a C# managed library from unmanaged code.

    The process I followed is documented here.

    Here's an overview

    • create a C++/CLI project (CLR Class Library in Visual Studio's New Project dialog)
    • add a reference to the C# dll create native classes and/or functions that expose the necessary C# dll functionality to the native world;
    • export them using the usual means (__declspec(dllexport) or .def file)
    • add the .lib produced by the C++/CLI project as linker input in your native project(s)

    About the image information, it can be referred to as a byte array. It has to be pinned in the managed code using a GCHandle. Here is some code to illustrate this concept (Disclaimer, this answer was provided by another author in the original msdn forum post):

    #include <unordered_map>
    
    using namespace System;
    using namespace System::Runtime::InteropServices;
    
    std::unordered_map<void *, void *> dataHandles;
    
    void *getData() {
        array<Byte>^ arr = gcnew array<Byte>(2);
        arr[0] = 'C';
        arr[1] = '#';
    
        GCHandle handle = GCHandle::Alloc(arr, GCHandleType::Pinned);
        void *data = (void *)handle.AddrOfPinnedObject();
        dataHandles.insert(std::make_pair(data, (void *)GCHandle::ToIntPtr(handle)));
        return data;
    }
    
    void releaseData(void *data) {
        auto i = dataHandles.find(data);
    
        if (i != dataHandles.end())
            GCHandle::FromIntPtr((IntPtr)i->second).Free();
    }
    
    #pragma unmanaged
    
    void native() {
        char *data = (char *)getData();
    
        for (int i = 0; i < 2; i++)
            printf("%c ", data[i]);
    
        releaseData(data);
    }
    
    #pragma managed
    
    int main(array<System::String ^> ^args) {
        native();
        return 0;
    }
    

    More about specific Bitmap to unsigned char * conversion here