InvertedAcceleration
InvertedAcceleration

Reputation: 11003

Pain free way to call a managed c# function (with no return value) from unmanaged c++?

I have been tasked with maintaining a legacy unmanaged c++ system. I do not have access to the source of the entire system but I do have the source to a number of extension dlls that, when included in the same directory as the core system, will be loaded instead of the built in defaults.

I have used the extensions in the past for small changes without problems. My issue now, however, is that I'm being asked to overhaul one of the extension dlls with a substantial amount of extra functionality. Creating this extra functionality in C# is going to be significantly faster (time-to-develop) and more maintainable (our team is primarily composed of C# devs).

The extension dll only has two functions that get called by the core system. The two functions take a bool, int, uint, RECT, Point, CString and return void. Some of the parameters they accept are const.

I'm really keen to find a solid way to bridge these extension functions to C# (.NET 4). So far I've put considerable effort into researching COM Visible, Regasm, c++ mixed mode and interop wrapping libraries. I've also lost a considerable amount of time on proof of concept projects during this research and so far I do not have a working 'bridge'.

What is the most pain free method to get this up and running?


I'm under considerably more pressure on this project than normal - I'm literally starting the C# now and assuming I will get this working somehow.

Really appreciate help and feedback.


Here is the .h and .def files:

modeldll.h

    #ifndef INC_MODELDLL_H
    #define INC_MODELDLL_H

    #ifdef MODELDLL_EXPORTS
    #define MODELDLL_API __declspec(dllexport)
    #else
    #define MODELDLL_API __declspec(dllimport)
    #endif

    typedef int (*model_updatemodel_t)(const bool update_model, const HWND hwnd, const RECT rect, const POINT next_point, const CString title);
    MODELDLL_API int UpdateModel(const bool update_model, const HWND hwnd, const RECT rect, const POINT next_point, const CString title);

    typedef int (*model_updatemodelpoint_t)(const bool update_model, const HWND hwnd, const RECT rect, UINT update, const POINT next_point);
    MODELDLL_API int UpdateModelPoint(const bool update_model, const HWND hwnd, const RECT rect, UINT update, const POINT next_point);

    typedef void (*model_process_message_t)(const char *message, const void *param);
    MODELDLL_API void ProcessMessage(const char *message, const void *param);

    #endif // INC_MODELDLL_H

modeldll.def:

    LIBRARY model.dll
    EXPORTS
    ProcessMessage    @1
    UpdateModel       @2
    UpdateModelPoint  @3

Upvotes: 3

Views: 459

Answers (3)

Sergey Teplyakov
Sergey Teplyakov

Reputation: 11657

I've investigated this topic couple of years ago: I want to use log4net and Npgsql libraries from native code that compiles even withour /clr key.

The main idea behind this technique described by Paul DiLascia in his two remarkable articles:

Managed Code in Visual Studio 2005 Use Our ManWrap Library to Get the Best of .NET in Native C++ Code

For example, here some code snippets that uses log4net library from native code (this code resides in simple non-managed dll, but you should compile this with /clr, but it's not nessary to compile with /clr key code that would use this dll from native code):

// Log4NetWrapper.h

#pragma once

using namespace System;
//facade for log4net library (you may create something like this too)
ref class Log4NetWrapper
{
public:
    //I intentionally remove additional methods, because
    //I'm trying to show the main principle
    static void ReportDebugMessage(char* msg);
private:
    static property log4net::ILog^ Logger
    {
        log4net::ILog^ get();
    }
    static Object^ syncObject_ = gcnew Object();
    static String^ loggerName_ = "";
};

//C-interface that could be accessed from native code
extern "C" 
{
    _declspec(dllexport) void ReportDebugMessage(char* msg) 
    {
        Log4NetWrapper::ReportDebugMessage(msg);
    }

}

// This is the main DLL file.

#include "stdafx.h"

#include "Log4NetWrapper.h"

void Log4NetWrapper::ReportDebugMessage(char* msg) 
{
    String^ data = gcnew String(msg);
    Logger->Debug(data);
}
log4net::ILog^ Log4NetWrapper::Logger::get()
{
    if ( loggerName_ == nullptr )
        return log4net::LogManager::GetLogger("");
    return log4net::LogManager::GetLogger(loggerName_);
}

Compile this code as native dll, than add this dll to your native project, add something like this to that project:

#pragma once

#pragma comment(lib, "Log4NetWrapper")

extern "C"
{
    _declspec(dllimport) void ReportDebugMessage(char* msg); 
}

And use ReportDebugMessage to access managed code from native code.

Upvotes: 1

Eli Iser
Eli Iser

Reputation: 2834

As Preet suggested, this can be done using a Managed C++ wrapper that will do the bridging you need.

This article explains the entire process: http://www.codeproject.com/KB/mcpp/unmanaged_to_managed.aspx

I've actually done these sort of things quite a lot several years ago, and all direction work pretty well - C# calling C++ code, C++ code calling C# (via a Managed C++ proxy) and C# passing delegate to a C++ code treating them as function pointers and calling them. I've had some example projects for this, I'll try and find them.

Upvotes: 1

Preet Sangha
Preet Sangha

Reputation: 65506

Without further information there's not much to go on but I would suggest that you have the following.

Extension Dll (unmanaged code) -> Managed C++ (wrapper) -> C# dll

The first jump is explained in this example:

http://www.codeproject.com/KB/mcpp/cpptomancpp.aspx

You can then load the c# assembly from the managed c++ fairly easily using

using namespace YourNameSpace; // the namespace of the c# routines

In time you may be able to merge the first two.

Upvotes: 1

Related Questions