Calling C# code from visual C++

2019-05-18 12:52发布

问题:

Basically I need to call C# code from Visual C++ code. After reading lots of articles about possible ways, I decided I want to use C++/CLI mechanism.

Initially I decided I will have some functions in C++ native code (dll library project), they will call some functions from a CLR project which will call some functions from a C# project.

After that I thought that maybe I could get rid of the bridge project (the CLR project) since it only makes the transition to managed world. I think that I can just create my native project, and I can add a c++ source file to it and I can enable CLR support just for that file and not the entire project. So this would mean my native project will include just one file which can use C++/CLI syntax and represents the bridge . All other files are just native C++ source files. Is this right from a design perspective ?!

In order to do the above, I think I must add the managed C# dll file to Additional #using Directories properties of the C++ native dll File. The problem is I don't know how to set the path to C# dll debug or release versions based on current configuration.

Also I know that I can't add a native C++ DLL to a C# project references. But it looks like I can add a C# dll as a reference inside the native C++ project. How come ?! Does this even work ?! If I can add a C# dll to a native C++ project references, do I need to set those #using directories ?!

回答1:

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.



回答2:

I doubt you can have the whole project non-CLI and make only one file in it CLI-featured. Overall, this approach is rather tricky.

I suggest to consider exporting your C# functions with DllExportAttribute: https://sites.google.com/site/robertgiesecke/Home/uploads/unmanagedexports



回答3:

Try ILASM 2.0 https://msdn.microsoft.com/tr-tr/library/496e4ekx(v=vs.110).aspx

Basically, work is the same as the exports in C++/CLI.

// unmexports.il
// Compile with : ilasm unmexports.il /dll
.assembly extern mscorlib { auto }
.assembly UnmExports {}
.module UnmExports.dll
.method public static void Bar()
{
  .export [1] as bar
  ldstr "Hello from managed function"
  call void [mscorlib]System.Console::WriteLine(string)
  ret
}

Note: You can decompile with ILDASM both C# and C++/CLI to see how exports it. (As in the example)



标签: c# c++ c++-cli