6

I'm in the process of wrapping a pure unmanaged VC++ 9 project in C++/CLI in order to use it plainly from a .NET app. I know how to write the wrappers, and that unmanaged code can be executed from .NET, but what I can't quite wrap my head around:

  1. The unmanaged lib is a very complex C++ library and uses a lot of inlining and other features, so I cannot compile this into the /clr-marked managed DLL. I need to compile this into a seperate DLL using the normal VC++ compiler.

  2. How do I export symbols from this unmanaged code so that it can be used from the C++/CLI project? Do I mark every class I need visible as extern? Is it that simple or are there some more complexities?

  3. How do I access the exported symbols from the C++/CLI project? Do I simply include the header files of the unmanaged source code and will the C++ linker take the actual code from the unmanaged DLL? Or do I have to hand write a seperate set of "extern" classes in a new header file that points to the classes in the DLL?

  4. When my C++/CLI project creates the unmanaged classes, will the unmanaged code run perfectly fine in the normal VC9 runtime or will it be forced to run within .NET? causing more compatibility issues?

  5. The C++ project creates lots of instances and has its own custom-implemented garbage collector, all written in plain C++, it is a DirectX sound renderer and manages lots of DirectX objects. Will all this work normally or would such Win32 functionality be affected in any way?

Community
  • 1
  • 1
Robin Rodricks
  • 110,798
  • 141
  • 398
  • 607
  • 2
    Yes, you can do mixed-mode. Just mark the files you want to compile as native, without the `/clr` option. There are some #pragma directives to control this on a function level. – leppie Mar 24 '13 at 12:30

3 Answers3

9

You can start with an ordinary native C++ project (imported from, say, Visual Studio 6.0 from well over a decade ago) and when you build it today, it will link to the current version of the VC runtime.

Then you can add a single new foo.cpp file to it, but configure that file so it has the /CLR flag enabled. This will cause the compiler to generate IL from that one file, and also link in some extra support that causes the .NET framework to be loaded into the process as it starts up, so it can JIT compile and then execute the IL.

The remainder of the application is still compiled natively as before, and is totally unaffected.

The truth is that even a "pure" CLR application is really a hybrid, because the CLR itself is (obviously) native code. A mixed C++/CLI application just extends this by allowing you to add more native code that shares the process with some CLR-hosted code. They co-exist for the lifetime of the process.

If you make a header foo.h with a declaration:

void bar(int a, int b);

You can freely implement or call this either in your native code or in the foo.cpp CLR code. The compiler/linker combination takes care of everything. There should be no need to do anything special to call into native code from within your CLR code.

You may get compile errors about incompatible switches:

  • /ZI - Program database for edit and continue, change it to just Program database
  • /Gm - you need to disable Minimal rebuild
  • /EHsc - C++ exceptions, change it to Yes with SEH Exceptions (/EHa)
  • /RTC - Runtime checks, change it to Default
  • Precompiled headers - change it to Not Using Precompiled Headers
  • /GR- - Runtime Type Information - change it to On (/GR)

All these changes only need to be made on your specific /CLR enabled files.

Robin Rodricks
  • 110,798
  • 141
  • 398
  • 607
Daniel Earwicker
  • 114,894
  • 38
  • 205
  • 284
  • 1
    AMAZING! But how do I mark `/clr` for just one file? Do I have to enable `/clr` for the entire project in the Project Settings dialog also? – Robin Rodricks Mar 24 '13 at 12:51
  • No, just bring up the properties for foo.cpp, go to the C++ section, and for the *Common Language RunTime Support* setting choose *Common Language RunTime Support (/clr)* – Daniel Earwicker Mar 24 '13 at 12:54
  • You may then get some errors telling you that other options need to be switched off - will update. – Daniel Earwicker Mar 24 '13 at 12:57
  • FANTASTIC INFO, Daniel! Thanks a ton, I'm trying it out right now, I'll keep you updated. – Robin Rodricks Mar 24 '13 at 13:04
  • Thank you VERY MUCH! It work exactly as you said ... I edited your post (hope it gets approved) to add one more switch .. `/GR- Runtime Type Information - change it to On (/GR)` – Robin Rodricks Mar 24 '13 at 20:13
1

As mentioned from Daniel, you can fine-tune your settings on file level. You can also play with '#pragma managed' inside files, but I wouldn't do that without reason.

Have in mind, that you can create a complete mixed mode assembly. That means, you can compile your native code unchanged into this file PLUS some C++/CLI wrapper around this code. Finally, you will have the same file as native Dll with all your exported native symbols AND as full-fledged .NET assembly (exposing C++/CLI objects) at the same time!

That also means, you have only to care about exports as far as native client code outside your file is considered. Your C++/CLI code inside the mixed dll/assembly can access the native data structures using the usual access rules (provided simply by including the header)

Because you mentioned it, I did this for some non-trivial native C++ class hierarchy including a fair amount of DirectX code. So, no principal problem here.

I would advise against usage of pInvoke in a .NET-driven environment. True, it works. But for anything non-trivial (say more than 10 functions) you are certainly better with an OO approach as provided by C++/CLI. Your C# client developers will be thankful. You have all the .NET stuff like delegates/properties, managed threading and much more at your finger tips in C++/CLI. Starting with VS 2012 with a somewhat usable Intellisense too.

Michael
  • 107
  • 10
0

You can use PInvoke to call exported functions from unmanaged DLLs. This is how unmanaged Windows API is accessed from .Net. However, you may run into problems if your exported functions use C++ objects, and not just plain C data structures.

There also seems to be C++ interop technology that can be of use to you: http://msdn.microsoft.com/en-us/library/2x8kf7zx(v=vs.80).aspx

alex
  • 12,464
  • 3
  • 46
  • 67
  • I don't think I can do this. I mentioned in #5 that the library is very complex and manages its own objects, it maintains active DirectX object pointers and everything. It even has its own GC that runs periodically. I'm trying to wrap this without breaking anything. – Robin Rodricks Mar 24 '13 at 12:33
  • If I create static functions (I can), and use P/Invoke, will the actual C++ project run in a normal VC++ enviorment or will .NET simply invoke it for the duration of the function and then shut it down? – Robin Rodricks Mar 24 '13 at 12:35
  • NB explicit p/invoke is not required in C++/CLI - it has built-in seamless interop at the level of individual functions. – Daniel Earwicker Mar 24 '13 at 12:37
  • @DanielEarwicker, can you offer some links of calling unmanaged C++ from C#? – David Mar 24 '13 at 12:39
  • @David that would be best asked as a separate question (of course there are already a million such questions here!) – Daniel Earwicker Mar 24 '13 at 12:42
  • @DanielEarwicker - If I use C++/CLI, will the actual C++ project run in a normal VC++ enviorment or will .NET simply invoke it for the duration of the function and then shut it down, destroying all used memory? – Robin Rodricks Mar 24 '13 at 12:45