views:

208

answers:

2

I'm trying to understand my options for calling a C# library implementation from unmanaged C++.

My top level module is an unmanaged C++ COM/ATL dll. I would like to integrate functionality of an existing managed C# dll. I have, and can recompile the source for both libraries.

I understand from reading articles like this overview on MSDN and this SO question that it might be possible to create a "mixed-mode" dll which allows the native C++ code to call into the C# library.

I have a couple of questions about this approach:

  1. How do I go about setting this up? Can I simply change some properties on the existing COM/ATL project to allow use of the C# modules?
  2. How will these mixed-mode calls differ in performance from COM interop calls? Is there a common string format that may be used to prevent conversion or deep copies between the modules?
  3. If this dll is created mixed-mode, can it still be interfaced/used in the same way by its COM clients, or do they need to be mixed mode aware?
  4. Will inclusion of the CLR impose substantial overhead when loading this COM object?

I'm new to Windows development, so please comment if anything in the question statement needs clarification or correction.

Thanks in advance.

A: 

I can't say much about the details like e.g. the string issues, since I never actively used this approach.

But you can easily consume any COM interface from any C# code by simply letting a VS wizard create a proxy for you, there is no performance overhead to it except the one that you always have when invoking COM and .NET.

The other direction, you just have to set your C# assemblies' ComVisibleAttribute to true (in VS it's a simple check box in the project properties), and then the compiler will automatically create COM interfaces for you. Again, there's no additional performance penalty.

HTH!

Thomas Weller
"there's no additional performance penalty" - additional compared to what?
Pavel Minaev
Compared to "there is no performance overhead to it except the one that you always have when invoking COM and .NET", like I said some lines before.Tip: Reading helps...
Thomas Weller
Still doesn't parse. "There's no additional penalty" applies to calling .NET via COM (since you mention generating proxies etc; this isn't the "direct native/managed C++/CLI" approach). And now you're essentially saying that it means "there's no additional penalty when calling .NET via COM, except for the penalty that is there". It's certainly a true statement, just not very informative :)
Pavel Minaev
You have a performance penalty for initializing the general .NET stuff (CLR, JIT-Compiler etc.), and you have a similar performance penalty for initializing and using COM (reading the registry entries, late binding, marshalling etc.). All in all, this is quite considerable. But other than that, you will have no issues. Was that clear enough now ;-) ?
Thomas Weller
+3  A: 

How do I go about setting this up? Can I simply change some properties on the existing COM/ATL project to allow use of the C# modules?

If you fully control that project, so changing such settings isn't an issue, then sure. All you need is to enable /clr for this project (In project properties, open the "General" page, and look for "Common Language Runtime" support). Now you can use managed handles (^) and other C++/CLI bits in your project as needed. All existing code written in plain C++ should just keep working (it will be compiled to MSIL now, inasmuch as possible, but its semantics will remain unchanged).

How will these mixed-mode calls differ in performance from COM interop calls? Is there a common string format that may be used to prevent conversion or deep copies between the modules?

A mixed-mode call will be faster, because it uses faster calling conventions, and doesn't do any marshaling the way COM interop does (you either use types that are inherently compatible, or do your own explicit conversions).

There's no common string format - the problem is that System::String both allocates and owns its buffer, and also requires it to be immutable; so you can't create a buffer yourself and then wrap it as String, or create a String and then use it as a buffer to output text to.

If this dll is created mixed-mode, can it still be interfaced/used in the same way by its COM clients, or do they need to be mixed mode aware?

It can be interfaced the same, but if it's entered via an native entry point, it will try to load the CLR into the process, unless one is already loaded. If the calling client had already loaded CLR prior to the call (or the client was itself called from managed code), then you'll get the CLR that is already loaded, which may be different from the CLR that your code requires (e.g. client may have loaded 1.1, and your code needs 2.0).

Will inclusion of the CLR impose substantial overhead when loading this COM object?

It depends on what you define by overhead. Code size? Runtime penalties? Memory footprint?

In any case, loading the CLR means that you get all the GC and JIT machinery. Those aren't cheap. That said, if you need to call managed code ultimately anyways, there's no way around this - you will have to load CLR into some process to do this. The penalties aren't going to differ between COM Interop and mixed-mode C++/CLI assemblies.

Pavel Minaev
Thanks for all the great information. I wish I could give more than one up-vote.
Adam
Very biased opinion. COM calls are not slower. CLR loading overhead is substantial.
wqw
Explain how a COM call will not be slower, if it's using a calling convention that mandates pushing all arguments onto the stack (no in-registry passing), while direct managed<->native C++/CLI calls will use "thiscall". Furthermore, when using `/clr`, most code will be compiled to MSIL in the first place - both `ref` and non-`ref` classes - so there will be a single native->managed transition on DLL entry point, and from there it's all managed, with no transition penalties. I have stated that CLR loading overhead is substantial, but how do you avoid it with COM interop (or otherwise)?
Pavel Minaev
thiscall? But his methods have no params or 10+ params :-)) So your speed comparison is biased on a 1-2 params methods...
wqw
An instance method that has "no params" still has 1 param - `this`.
Pavel Minaev