Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Calling a C# Method from C/C++ (Native Process)

0.00/5 (No votes)
8 Sep 2014 1  
Calling a C# method from C/C++ (native process)

I received a challenge from a friend (lost a bet...) regarding how to load a managed (C#) DLL in a native (C++) process by using the Common Language Runtime (CLR). This is confirmed to work with .NET Framework 4.0.

The trick is to host the CLR in the process and using it to load the managed DLL.

// This tutorial contains C/C++; you have been warned.

1) Making a C# DLL

This is the only part where we'll be using C# - so enjoy it as much as you can! We'll make a dummy DLL that will be later loaded into a native application.

I came up with this sophisticated code (feel free to improvise), but the function that you want to call must return something (preferably int).

using System.Windows.Forms;

namespace dllNamespace
{
    public class dllClass
    {
        public static int ShowMsg(string msg)
        {
            MessageBox.Show(msg);
            return 0;
        }
    }
}

2) Hosting the CLR in a Native Process

First things first, you'll need to include these 2 lines:

#include <metahost.h>

#pragma comment(lib, "mscoree.lib")

We have to call CLRCreateInstance() in order to gain access to ICLRMetaHost. This interface contains various methods that will provide general information about .NET Framework.

From there, it is required to focus on one version of the framework (in this tutorial, I'm working with v4.0.30319) - calling ICLRMetaHost::GetRuntime() will return a pointer to another interface (ICLRRuntimeInfo), which contains...more methods. (this is the upgraded version of ICorRuntimeHost).

The next step is calling ICLRRuntimeInfo::GetInterface which loads the CLR into the current process and allows us to use some runtime pointers.

Finally, start the runtime host (if it's not running already) using ICLRRuntimeHost::Start().

3) Calling a Method from the Managed DLL

This is done via the runtime host that we struggled to load earlier. Using ICLRRuntimeHost::ExecuteInDefaultAppDomain() with a bunch of arguments does the job pretty well.

The method looks like this:

HRESULT ExecuteInDefaultAppDomain (

    [in] LPCWSTR pwzAssemblyPath,  // absoulte path to the managed dll (not relative!)

    [in] LPCWSTR pwzTypeName,      // name of the class for example: dllNamespace.dllClass

    [in] LPCWSTR pwzMethodName,    // name of the method 

    [in] LPCWSTR pwzArgument,      // argument(s)

    [out] DWORD *pReturnValue      // this is what the method returns

);

Source Code

This is the whole source code:

#include <metahost.h>
#pragma comment(lib, "mscoree.lib")

int main()
{
    ICLRMetaHost* metaHost = NULL;
    ICLRRuntimeInfo* runtimeInfo = NULL;
    ICLRRuntimeHost* runtimeHost = NULL;

    if (CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&metaHost) == S_OK)
        if (metaHost->GetRuntime(L"v4.0.30319", 
        IID_ICLRRuntimeInfo, (LPVOID*)&runtimeInfo) == S_OK)
            if (runtimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, 
               (LPVOID*)&runtimeHost) == S_OK)
                if (runtimeHost->Start() == S_OK)
	        {		
		    DWORD pReturnValue;
		    runtimeHost->ExecuteInDefaultAppDomain(L"C:\\random.dll", 
            L"dllNamespace.dllClass", L"ShowMsg", L"It works!!", &pReturnValue);
	
		    runtimeInfo->Release();
		    metaHost->Release();
		    runtimeHost->Release();
                }
    return 0;
}

Short advice: Always check that each HRESULT returned is equivalent to S_OK.

P.S.: Due to some problems with my compiler, I couldn't test this code properly - last time, it worked pretty well... hope it still does so.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here