I can enable CLR support just for that file and not the entire project
There is no effective distinction between doing this and getting started by selecting a CLR project template. Either way the result is a mixed-mode .NET assembly, containing both MSIL and native code. Doing it at the project level merely sets the default value for /clr for the .cpp files in the project. You can also do this with a CLR project template by overriding the settings of an individual .cpp file, now disabling /clr. The end result is all the same.
I think I must add the managed C# dll file to Additional #using Directories
Yes, that is one way to do it. Also solves your query, you can use the $(Configuration) macro in the path name. Resolves to "Debug" or "Release" at compile time, depending on the configuration you build. I should emphasize that this is not actually necessary. The compiler only uses the metadata in the C# assembly. Just the declarations. The exact equivalent of a .h file, note how you rarely make an #include different based on the configuration. The only corner-case is when you used #if DEBUG
in your C# source code to include/exclude code, not very common.
But it looks like I can add a C# dll as a reference inside the native C++ project
This has been tinkered with in every VS version, not 100% sure what you are doing. Very little happens in practice when you add a reference, it merely gets the compiler driver to add the /FU compile option. If the .cpp file is not compiled with /clr then it does nothing, the compiler just completely ignores it. It does complain loudly when you use #using
in source code and didn't use /clr. No real distinction between the two otherwise, just easier to control the path of the file with /FU.
since it only makes the transition to managed world
A word of caution is appropriate here, there is a lot more going on. What you are doing here is called "reverse pinvoke", native code calling managed code. C++/CLI was primarily designed to do the exact opposite. It is more involved, the non-trivial thing that needs to happen is that the CLR needs to be loaded and initialized on the first call. That's all automagic, provided by a stub that's auto-generated when you use __declspec(dllexport)
. The same magic that the Robert Giesecke's "Unmanaged Exports" utility depends on, another option that you should look at.
But it is limited. You cannot expose an object model, just simple functions. There is added overhead in the function call, although it is pretty modest. And the big, big problem, you cannot easily diagnose errors. Both the CLR and most any C# code expects the caller to know how to handle exceptions. Problem is that you don't from native C++ code. You have some leeway in using __try/__except
but you can't get any details about the exception. That turns very simple problems, like FileNotFoundException, into completely undiagnosable crashes. You can debug it by setting the Debugger Type to "Mixed", but you can't do anything useful after you shipped it. Very ugly support phone calls.
Other ways to accomplish the same without these problems is by using [ComVisible(true)] in your C# library, allowing #import in your native C++ code. And custom-hosting the CLR yourself through its hosting interface, the approach used by the kind of programs that support plugins written in managed code. CAD programs like AutoCAD are good examples of that. And Visual Studio.