How is marshalling performed when C++ code is called from C++/CLI?
Asked Answered
F

6

9

According to this question it's possible to seamlessly combine managed and unmanaged code using C++/CLI. I don't quite get it - shouldn't there be marshalling between managed and unmanaged anyway?

For example, I have InnerLibrary that is compiled as a native C++ .dll with a header published and C++/CLI OuterLibrary that calls code from InnerLibrary. Will there be marshalling? Who will implement it and how costly will it be?

Forsberg answered 16/8, 2010 at 10:48 Comment(0)
W
6

Well, it's a feature that's built into the C++/CLI compiler, called C++ Interop. There's a lot less black magic involved that you might think. The JIT compiler generates the exact same kind of machine code as your C++ compiler generates. All of the .NET value types have a direct equivalent in C++ so no conversion is needed. It does not automatically handle reference types, you have to do that yourself. pin_ptr<>, typically.

All that it really does is inject a bit of code that handles the transition from a managed stack frame to an unmanaged stack frame. That code puts a special "cookie" on the stack, recognized by the garbage collector. Which prevents it from blundering into the unmanaged frames and mis-identify unmanaged pointers as object references. There's not much to that code, takes about 5 nanoseconds in the Release build, give or take.

Wherewith answered 16/8, 2010 at 12:5 Comment(1)
KeyValuePair<string,string> is a value type but has no direct equivalent. Value types are layout-compatible iff every member is layout compatible (an intrinsic type or another layout-compatible value type).Alms
C
3

There doesn't have to be any marshalling because C++/CLI is able to emit unsafe code that makes the call directly. Take a look at some C++/CLI code in Reflector -- it'll look very different from C#.

This is something that C# can't do (at least, not without the unsafe keyword and some pointer hacks), and it's also something that pure-mode C++/CLI can't do (for the same reason as C#).

.NET unsafe code is able to make direct calls to unmanaged functions; it's just that this ability isn't conveniently available except through C++/CLI.

Coffle answered 16/8, 2010 at 10:58 Comment(3)
I don't get it. Say someone wants to call OuterLibrary from his C# code (and OuterLibrary will internally call into InnerLibrary). Will he be able to work without marsshalling?Forsberg
@sharptooth: only if you create accessible public methods that internally call your InnerLibrary. See also my answer, which states that marshaling is not always seemless, not even with C++/CLI.Archaeornis
@sharptool, with C++/CLI, you are effectively writing the marshalling code yourselfCoffle
A
3

Marshaling is the process of bringing unmanaged data or calls to the managed world. It merely does — so to speak — a translation between the two.

Withing C++/CLI you can mix and match. That means, if you use your library directly, using the *.h file and using traditional C++ code, it will be unmanaged and without marshaling. If you access that data using BCL classes or your own managed code, you are adding the marshaling layer by hand, but only if needed. I.e., a LPTSTR will need to be translated into a managed string to be used as one. Using C++/CLI, you can skip this step and stick to traditional C++ code, creating faster and more lenient code, at the expense of not using safe, checked managed code.

Archaeornis answered 16/8, 2010 at 11:7 Comment(0)
P
2

There is marshalling invovled, but you (i.e. the programmer) must do it explicitly.

If your C++CLI OuterLibrary calls has a function that takes a System.String/System::String^, the C++ type system requires that you perform a type conversion before passing it to an InnerLibrary function that takes a const char*. You have to do the conversion yourself - which is the marshalling.

Microsoft ship something called the C++ Support Library, which provides functions that help with C++ <-> C++CLI interaction.

Panzer answered 16/8, 2010 at 11:15 Comment(0)
A
0

It depends on the data types involved.

Intrinsic types such as int, double, and so on (string does not qualify) have the same representation in both native and managed code, no marshaling is required. Arrays of intrinsic types are laid out the same way as well (if we ignore the metadata .NET stores, but that's separate from the array content).

Value types using explicit layout attributes where all members are intrinsic types, are also memory layout-compatible.

Pinning may be required if the data is stored within an object on the managed heap (this is true for all arrays).

Class types, on the other hand, have to be converted/translated back and forth.

Alms answered 16/8, 2010 at 11:9 Comment(2)
If int, double etc are boxed, then their internal representation is different from the native C++ version.Archaeornis
No it isn't. There is a metadata header added, but the representation of the value is unchanged, so the native code can read and write the value directly with no marshaling beyond pinning the value in place.Alms
D
0

There are two points here:

1) Managed/unmanaged code transition: every transition has its fixed cost. When C++/CLI code is compiled in a single assembly, compiler tries to make all the code managed, when it is possible, to minimize such transitions. When external unmanaged Dll is called from C++/CLI code, such optimization is impossible. Therefore, it is a good idea to minimize such transitions, at least in time-critical sections. See more about this here: http://msdn.microsoft.com/en-us/magazine/dd315414.aspx, Performance and the Location of Your Interop Boundary

2) Parameters marshalling. This depends on parameters type. Some parameters don't need to be marshalled, for example, simple types like int. Strings should be marshalled. Some tricks, like pinning pointers, allows to prevent simple type array marshalling.

Driskill answered 16/8, 2010 at 11:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.