|
Introduction
As .NET is penetrating the development environment day by day, we are having unmanaged and managed code running parallel. These days, managed to unmanaged calls have become very popular. But unmanaged to managed calls are still tedious. So my aim was to make such calls as simple as possible.
Background
Unmanaged to managed calls using Regasm are very common. I have tried a straightforward call from unmanaged C++ code to a managed C# code. The main funda which I worked on are:
- Each type of .NET Framework application requires a piece of code called a Runtime host to start it. The runtime host loads the runtime into a process, creates the application domains within the process, and loads and executes user code within those application domains. This section explains how to write a runtime host that performs several fundamental tasks.
- Operating systems and runtime environments typically provide some form of isolation between applications. This isolation is necessary to ensure that code running in one application cannot adversely affect other, unrelated applications.
Application domains provide a more secure and versatile unit of processing that the common language runtime can use to provide isolation between applications. Application domains are typically created by runtime hosts, which are responsible for bootstrapping the common language runtime before an application is run.
- Unmanaged hosting code is used to configure the common language runtime, load it in the process, and transition the program into managed code. The managed portion of the hosting code creates the application domains in which the user code will run and dispatches user requests to the created application domains.
In my project, I have made an attempt to create a CLRHost to call methods of a managed DLL or EXE. I have done the steps described in the following section.
Using the code
Suppose we have a .NET DLL with a method declaration like this: using System;
namespace ManagedDll
{
public class ManagedClass
{
public ManagedClass()
{
}
public int Add(int i, int j)
{
return(i+j);
}
}
}
Now, the C++ code which directly calls this DLL without using Regasm would be:
#include "stdafx.h"
#include <atlbase.h>
#include <mscoree.h>
#include <comutil.h>
#import "C:\\WINNT\\Microsoft.NET\\Framework\\"
"v1.1.4322\\Mscorlib.tlb" raw_interfaces_only
using namespace mscorlib;
int CallManagedFunction(char*, char*, BSTR, int,
VARIANT *, VARIANT *);
int main(int argc, char* argv[])
{
VARIANT varArgs[2] ;
varArgs[0].vt = VT_INT;
varArgs[0].intVal = 1;
varArgs[1].vt = VT_INT;
varArgs[1].intVal = 2;
VARIANT varRet;
varRet.vt = VT_INT;
//Calling manageddll.dll Add() method.
int iRet = CallManagedFunction("ManagedDll",
"ManagedDll.ManagedClass",L"Add",
2,varArgs,&varRet);
if(!iRet)
printf("\nSum = %d\n",varRet.intVal);
return 0;
}
int CallManagedFunction(char* szAsseblyName,
char* szClassNameWithNamespace,BSTR szMethodName,
int iNoOfParams, VARIANT * pvArgs, VARIANT * pvRet)
{
CComPtr<ICorRuntimeHost> pRuntimeHost;
CComPtr<_AppDomain> pDefAppDomain;
try
{
//Retrieve a pointer to the ICorRuntimeHost interface
HRESULT hr = CorBindToRuntimeEx(
NULL, //Specify the version
//of the runtime that will be loaded.
L"wks", //Indicate whether the server
// or workstation build should be loaded.
//Control whether concurrent
//or non-concurrent garbage collection
//Control whether assemblies are loaded as domain-neutral.
STARTUP_LOADER_SAFEMODE | STARTUP_CONCURRENT_GC,
CLSID_CorRuntimeHost,
IID_ICorRuntimeHost,
//Obtain an interface pointer to ICorRuntimeHost
(void**)&pRuntimeHost
);
if (FAILED(hr)) return hr;
//Start the CLR
hr = pRuntimeHost->Start();
CComPtr<IUnknown> pUnknown;
//Retrieve the IUnknown default AppDomain
hr = pRuntimeHost->GetDefaultDomain(&pUnknown);
if (FAILED(hr)) return hr;
hr = pUnknown->QueryInterface(&pDefAppDomain.p);
if (FAILED(hr)) return hr;
CComPtr<_ObjectHandle> pObjectHandle;
_bstr_t _bstrAssemblyName(szAsseblyName);
_bstr_t _bstrszClassNameWithNamespace(szClassNameWithNamespace);
//Creates an instance of the Assembly
hr = pDefAppDomain->CreateInstance(
_bstrAssemblyName,
_bstrszClassNameWithNamespace,
&pObjectHandle
);
if (FAILED(hr)) return hr;
CComVariant VntUnwrapped;
hr = pObjectHandle->Unwrap(&VntUnwrapped);
if (FAILED(hr)) return hr;
if (VntUnwrapped.vt != VT_DISPATCH)
return E_FAIL;
CComPtr<IDispatch> pDisp;
pDisp = VntUnwrapped.pdispVal;
DISPID dispid;
DISPPARAMS dispparamsArgs = {NULL, NULL, 0, 0};
dispparamsArgs.cArgs = iNoOfParams;
dispparamsArgs.rgvarg = pvArgs;
hr = pDisp->GetIDsOfNames (
IID_NULL,
&szMethodName,
1,
LOCALE_SYSTEM_DEFAULT,
&dispid
);
if (FAILED(hr)) return hr;
//Invoke the method on the Dispatch Interface
hr = pDisp->Invoke (
dispid,
IID_NULL,
LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD,
&dispparamsArgs,
pvRet,
NULL,
NULL
);
if (FAILED(hr)) return hr;
pRuntimeHost->Stop();
return ERROR_SUCCESS;
}
catch(_com_error e)
{
//Exception handling.
}
}
We need to be careful about some settings like:
- The managed DLL should be in the right path, i.e., any mapped path, or inside the debug/release folder of the calling C++ project.
- In the C++ project, we need to include the path C:\PROGRAM FILES\MICROSOFT VISUAL STUDIO .NET 2003\SDK\V1.1\BIN, and C:\PROGRAM FILES\MICROSOFT VISUAL STUDIO .NET 2003\SDK\V1.1\LIB for MSCOREE.H and MSCOREE.LIB.
MSCOREE.H contains the definition of the interfaces used in this program.
Points of Interest
So it is done. Unmanaged to managed call becomes very simple, we just need to pass the namespace, class name and the method name with the arguments to be passed. If you want more fundas, click here.
| You must Sign In to use this message board. |
|
| | Msgs 1 to 24 of 24 (Total in Forum: 24) (Refresh) | FirstPrevNext |
|
|
 |
|
|
Thanks first for a really helpful piece of code!
I found one detail when doing some mem leak profiling:
I am neither a c++ nor COM expert - but could it be some release() calls are missing in the sample?
I think I fixed some leaks with releasing pUnknown.release() and the like.
Thought I'd post that here for others to find, just in case.
Again: thanks for the code!
tb
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Great article. I'm curious how you'd use a managed Dll in different directories than the ones you specify. Intuitively I thought you could modify the parameters passed to 'CallManagedFunction' to something like:
int iRet = CallManagedFunction("C:\\Test\\ManagedDll", "ManagedDll.ManagedClass",L"Add", 2,varArgs,&varRet);
But this throws a file load exception:
First-chance exception at 0x76d2b09e in Test1.exe: Microsoft C++ exception: EEFileLoadException at memory location 0x0012f4ac..
Anybody have any hints as to how to resolve this?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Mr Chakrabarty Rajib This article really helps me a lot in implementing 'calling c# dll from c++'
Bravo - admcse
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I've tried to compile and execute this example with MS Windows Server 2003 and MS Visual Studio 2005 SP1 and it fails in execution
if (VntUnwrapped.vt != VT_DISPATCH) return E_FAIL;
returning always E_FAIL ( VntUnwrapped.vt value is VT_UNKNOWN ).
What am I doing wrong?
Thanks a lot.
Jesús Soriano
-- modified at 6:40 Monday 12th February, 2007
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Make your class COM visible. You don't need to register the assembly. This should work:
[System.Runtime.InteropServices.ComVisible(true)] public class Class1 { public Class1() { }
public int Init() { return 0; } }
Hristo
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
Hi, if I understand, you do the inverse rispect my problr (write in title). Do you know the passage (or manual) for use a C lib in C# application (for my is first time with .Net and windows programming)?
Sorry for my english
Thank's
Stefano
|
| Sign In·View Thread·PermaLink | 1.00/5 (1 vote) |
|
|
|
 |
|
|
Great arcticle. However I have few more question about it:
1. It is impossible to debug the created .NET runtime. How a debugging should be enabled for the run module? 2. If an exception is rised from the call, hr == 0x80020009 (Exception occurred). Do you know how to get the exception object?
Thank you.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Common guys…
This gentleman is suggesting something – take it (and say thanks for sharing) or …. BUT why are you shouting at him?
Many years ago (probably start of 1990 or there about) someone asked me what do I think about programming in Windows (I am an old-hat DOS programmer) and my response was:
1) Frankly my dear… who cares what I think of Windows… As long as Bill Gates thinks is what goes… 2) You either join the flow, or be left behind – I personally was happy with MSVC-6 and I didn’t really feel the urge to have this .NET, but if I ignore it, I will e left behind.
Finally, in my book – the majority of us old cowboys will have a much higher investment in existing Unmanaged C++, and the “Natural” think for us will be trying to call C# from that, very unlikely the other way around…
Anyway, thanks for sharing
Cheers Alex
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Excellent! You have demonstrated with alacrity exactly why I should avoid .NET for the foreseeable future.
What a grandiose pile of unnecessary, ridiculous and resource-sucking crap. Kind of like .COM and DirectX, for which I have already had to devise simplifying routines regularly (and quite unfortunately).
If only the base OS and API designers could have been promoted!
Whence come these retards who devise the crap they spew these days? It's been going on for years -- and almost no one calls them on it. Instead, people jump onto their bandwagon of insidious development-resource-sucking. (Not only user resources, but especially OUR OWN development resources).
It would be quite simple for the company that controls this industry to move it into a direction of simple design and control; instead they make it as complex as possible, and we retardly fall in line.
Am I too old to understand their 'direction'? No! But I am old enough to see through their crapola.
Shalom, Severian
|
| Sign In·View Thread·PermaLink | 1.00/5 (1 vote) |
|
|
|
 |
|
|
*plonk*
I should probably ignore the troll, but just a few words for those who are irritated by his claims.
Whether .NET is a good thing or not - this article is definitely NOT suited to help you to make that decision for yourself. Why? Because it doesn't contain a single line of managed code, i.e. of what commonly is called ".NET"!
Instead, the article focuses on a rare use case where the programmer needs to integrate unmanaged and managed code, wants to forego the automatisms and tools provided by the .NET framework, and where performance isn't a major issue. Within these confines, this code does its job just fine - thanks to Chakrabarty for working this out and sharing it with us!
The reason why this code looks like COM to you is because it, well, basically *is* COM. The code accesses the CLR from outside through its COM-like interfaces to perform its job.
To integrate managed and unmanaged code, Microsoft provides several mechanisms which are usually preferred. For example, you can automatically create a COM wrapper for your managed code (the "regasm approach") and then use it from your existing managed code just like any other COM component. Or you could use Managed C++ (now C++/CLI) and freely mix managed and unmanaged code in the same executable and even in the same source file. Or you could pass delegate function pointers from the managed code into the unmanaged code and use them to call back into the managed world. Or...
So there are many ways to interface between managed and unmanaged code, and most of them are much more elegant than the one discussed here. (Which does not mean that I want to dis the article - it's just that you should know your options before deciding to use this approach. Again, kudos to Chakrabarty for illustrating another interesting interfacing option.)
Personally, I think that .NET is an extremely interesting and ambitious attempt to raise the abstraction level in software engineering and help everybody to improve their productivity.
http://www.clausbrod.de/Blog/BlogOnSoftware
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Claus Brod wrote: Personally, I think that .NET is an extremely interesting and ambitious attempt to raise the abstraction level in software engineering and help everybody to improve their productivity.
Got to agree with that. The use of a language like C# and the .NET has improved on my productivity. Moving from C++ towards C# has been the best descission the company has made.
I find articles like this interesting as anyone in a similar situation to myself will need to communicate between languages.
Ant.
I'm hard, yet soft. I'm coloured, yet clear. I'm fruity and sweet. I'm jelly, what am I? Muse on it further, I shall return! - David Walliams (Little Britain)
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hey
Did you do any performance measures? I would be interested in knowing what is the penalty that is incured.
On another way to call C# from C++, why not have a layer in managed C++ that will interface C# and pure C++?
The pure C++ can called static methods written in MC++. The static method will then call the C# side.
Nuno
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
Hi Nuno!
I wanted to ask exactly the same question about performance.
We are using the approach explained here: .... It works quite fine, but it's very slow during startup , and it's not very robust to errors like DLL not found .
Do you have any performance experience for the method you mentioned?
CU, Andromeda Shun
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
This is really interesting. I think, this can be extended to Visual Basic 6 as well. If any one has tried it out, please post here. I will also try it out and post my response.
Kannan
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
You are hosting the runtime and loading everything for a method call. Jesus, I know it can be done, but come on.
Andrei
|
| Sign In·View Thread·PermaLink | 2.33/5 (3 votes) |
|
|
|
 |
|
|
I agree. This is interesting from an academic standpoint, but very unpractical in the real world. If anyone is using this approach in their product, please let me know so I can avoid it all together. Managed C++ layer between .NET and native c++ would be a much more elegant solution.
|
| Sign In·View Thread·PermaLink | 1.50/5 (2 votes) |
|
|
|
 |
|
|
Hi Andrei,
IMHO doing it the COM-way could not be faster, the .NET runtime must be loaded to execute managed code.
Greats
Pityu
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
|
I'm not talking about executing managed code. I'm talking about exposing managed objects as COM+ objects, in which case all the overhead involved would be creating that COM+ object.
No hosting the runtime, no app-domain creation ... pretty darn fast compared to the solution in the article.
Andrei
|
| Sign In·View Thread·PermaLink | 1.25/5 (4 votes) |
|
|
|
 |
|
|
Great article Rajib. Thanks for the simple solution to a complex problem.
Your example shows the calling of a managed routine that takes 2 integers and returns an integer. But if the managed routine
(a) takes string as parameter (b) takes StringBuilder as parameter, and changes the incoming string (c) takes ref int as parameter, and changes the incoming int
then a little bit of marshalling is needed I guess? If your article shows how to achieve these combinations, it will be an absolutely 5 out of 5 article any day! What you have done should be enough for somebody willing to continue the RnD to achieve these, but discussing these in the article will simply take the article to another level.
Koushik Biswas
-- modified at 21:52 Wednesday 11th January, 2006
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
|
Hi,
Thank you for such a nice comment.
I have tried with "string" and "ref int" as input parameter but it works fine. In case of "string" you have to set VARIANT.vt = VT_BSTR and use _com_util::ConvertStringToBSTR() while setting string value to VARIANT.bstrVal.I guess it will also work with "StringBuilder" as parameter.
Thanks, rajib
|
| Sign In·View Thread·PermaLink | 5.00/5 (2 votes) |
|
|
|
 |
|
|
Interesting article. Gives some insight in the boot strapping process and the relationship between the managed/native worlds. Some more details would be nice - Such as the Unwrap call.
Steve
|
| Sign In·View Thread·PermaLink | 2.50/5 (3 votes) |
|
|
|
 |
|
|
 |
|
|
General News Question Answer Joke Rant Admin
|