Interop 101 - Part 1
It's funny how often the people within our team (myself included) take certain things for granted. We have provided a great way to bridge the gap between native and managed code with C++/CLI yet I am continually surprised by how little information has been successfully conveyed. I posted slides from the talk I gave last month on this topic, however it lacks the most basic examples. To this effect, I intend to write up a few posts with some simple samples to boil down the basic concepts behind what we have dubbed C++ Interop.
Let's start with "legacy" native Win32 code. The following piece of code defines a simple type that is exported from a dll.
// HelloWorld.h
#pragma once
class __declspec(dllexport) HelloWorld
{
public:
HelloWorld();
~HelloWorld();
void SayThis(wchar_t *phrase);
};
The implementation of the SayThis method just brings up a message box as follows:
void HelloWorld::SayThis(wchar_t *phrase)
{
MessageBox(NULL, phrase, L"Hello World Says", MB_OK);
}
Once this code is compiled into a dll, our goal is to instantiate HelloWorld and invoke its one and only method. A traditional native client would do it by including the header we defined above as follows:
#include "..\interop101\helloworld.h"
int wmain()
{
HelloWorld hw;
hw.SayThis(L"I'm a native client");
return 0;
}
Alright, now that I've reminded what native code looks like, we can talk interop. In this little educational series, I intend to demonstrate 4 major scenarios.
- Client in C++/CLI calls the native code
- Client in C# calls the native code using its built-in interop functionality (a.k.a. P/Invoke)
- Client in C# calls the native code via a COM interface
- Client in C# calls the native code via a C++/CLI wrapper
Today let's look at the first and simplest scenario: using C++/CLI.
#include "..\interop101\helloworld.h"
using namespace System;
int main(array<System::String ^> ^args)
{
HelloWorld hw;
hw.SayThis(L"I'm a managed C++ client");
return 0;
}
Hmmm… Looks virtually identical… This what we refer to when we talk about IJW or "It Just Works". In other words, all we had to do here was throw the /clr switch on the original code and the compiler generated MSIL instead of x86 assembly. If we delve into the MSIL, we see that transitions to native code (there are three in this case, can you spot them?) are automagically handled by the compiler.
.method assembly static int32 main(string[] args) cil managed
{
// Code size 51 (0x33)
.maxstack 2
.locals ([0] int32 V_0,
[1] int32 V_1,
[2] valuetype HelloWorld hw)
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: ldloca.s hw
IL_0004: call valuetype HelloWorld* modopt([mscorlib]System.Runtime.CompilerServices.CallConvThiscall) 'HelloWorld.{ctor}'(valuetype HelloWorld* modopt([mscorlib]System.Runtime.CompilerServices.IsConst) modopt([mscorlib]System.Runtime.CompilerServices.IsConst))
IL_0009: pop
.try
{
IL_000a: ldloca.s hw
IL_000c: ldsflda valuetype '<CppImplementationDetails>'.$ArrayType$$$BY0BJ@$$CB_W modopt([mscorlib]System.Runtime.CompilerServices.IsConst) '?A0x783d98d5.unnamed-global-0'
IL_0011: call void modopt([mscorlib]System.Runtime.CompilerServices.CallConvThiscall) HelloWorld.SayThis(valuetype HelloWorld* modopt([mscorlib]System.Runtime.CompilerServices.IsConst) modopt([mscorlib]System.Runtime.CompilerServices.IsConst),
char*)
IL_0016: ldc.i4.0
IL_0017: stloc.1
IL_0018: leave.s IL_0028
} // end .try
fault
{
IL_001a: ldftn void modopt([mscorlib]System.Runtime.CompilerServices.CallConvThiscall) 'HelloWorld.{dtor}'(valuetype HelloWorld* modopt([mscorlib]System.Runtime.CompilerServices.IsConst) modopt([mscorlib]System.Runtime.CompilerServices.IsConst))
IL_0020: ldloca.s hw
IL_0022: call void ___CxxCallUnwindDtor(method void *(void*),
void*)
IL_0027: endfinally
} // end handler
IL_0028: ldloca.s hw
IL_002a: call void modopt([mscorlib]System.Runtime.CompilerServices.CallConvThiscall) 'HelloWorld.{dtor}'(valuetype HelloWorld* modopt([mscorlib]System.Runtime.CompilerServices.IsConst) modopt([mscorlib]System.Runtime.CompilerServices.IsConst))
IL_002f: ldloc.1
IL_0030: stloc.0
IL_0031: ldloc.0
IL_0032: ret
} // end of method 'Global Functions'::main
Voila. Short and sweet this time. Next example will be far more useful for C# clients :)
Comments
- Anonymous
August 03, 2006
Say I have
typedef struct myS myS;
struct myS {
int i32;
void *ptr;
};
myS s;
s.i32 = 1;
s.ptr = some_ptr_to_who_knows_what;
int myRet = CallMe(&s);
Such a simple non-hello-world would be more useful, because that's what will be used in the real world. You know, pointers.
I can't say the IL is at all useful (on spending column-inches), not without description, and still not useful even then, I would think. Maybe when you get to #4 that'll be a good time? - Anonymous
August 12, 2006
Points taken boogie. I'll try to do better in the next posts. - Anonymous
August 21, 2006
PingBack from http://www.robherbst.com/blog/2006/08/22/ccli-interop-examples/ - Anonymous
February 12, 2007
Yes, How do we declare types and structs in CLR? I do not think it is possible. I am trying to integrate some old .libs (C) into a CLR, is this possible? - Anonymous
July 31, 2007
http://bathroomdesign.greatnow.com/bathroom-vanities bathroom vanities - Anonymous
July 31, 2007
http://bathroomdesign.greatnow.com/bathroom-vanities bathroom vanities - Anonymous
August 02, 2007
http://bathroomdesign.greatnow.com/bathroom-faucets bathroom faucets - Anonymous
August 03, 2007
http://bathroomdesign.greatnow.com/bathroom-sinks bathroom sinks - Anonymous
August 04, 2007
http://bathroomdesign.greatnow.com/bathroom-vanity-cabinet bathroom vanity cabinet - Anonymous
August 04, 2007
http://bathroomdesign.greatnow.com/bathroom-vanity bathroom vanity - Anonymous
May 30, 2009
PingBack from http://outdoorceilingfansite.info/story.php?id=498 - Anonymous
June 18, 2009
PingBack from http://outdoordecoration.info/story.php?id=378 - Anonymous
June 18, 2009
PingBack from http://homelightingconcept.info/story.php?id=434