Runtime: C++ Interop Documentation Request

Created on 5 Apr 2015  Â·  63Comments  Â·  Source: dotnet/runtime

Would it be possible to document how C++ interop happens? From embedding into a C++ Application, to calling to C++ from .net, and calling .net functions from C++. I'm not finding any docs on how to do this.

area-Interop-coreclr documentation

Most helpful comment

Is it in the documentation now? I cannot find it.

I think the documentation should be something similar to what mono has provided to host/embed coreclr in C/C++.

A related StackOverflow question.

All 63 comments

I noticed that @jakesays and @pdelvo had some reasonable suggestion in the gitter chat just after you posted this issue. Did you take a look?

As noted, calling C++ from C# is super easy via DllImport and hosting CoreCLR uses the sameish APIs as hosting big CLR.. examples are under src\coreclr\hosts.

Calling into C from C# is easy with DllImport. Calling into C++, however, is nontrivial. The two main obstacles are 1)Name mangling differs across compilers and 2) VTable may differ across compilers.

There is a tool to assist with doing it: https://github.com/mono/CppSharp

can I just pass a pointer to a function manually? I'm not interested in C#
loading C or C++ code but the opposite, and not interested into C# reading
C++ symbols. Is there API for this?

On Mon, Apr 6, 2015 at 7:12 PM, OtherCrashOverride <[email protected]

wrote:

Calling into C from C# is easy with DllImport. Calling into C++, however,
is nontrivial. The two main obstacles are 1)Name mangling differs across
compilers and 2) VTable may differ across compilers.

There is a tool to assist with doing it: https://github.com/mono/CppSharp

—
Reply to this email directly or view it on GitHub
https://github.com/dotnet/coreclr/issues/641#issuecomment-90261119.

To clarify, I just need a single binary, no DLLs, and no C# entry point,
but C++ calling random C# functions.

On Mon, Apr 6, 2015 at 7:16 PM, Juan Linietsky [email protected] wrote:

can I just pass a pointer to a function manually? I'm not interested in C#
loading C or C++ code but the opposite, and not interested into C# reading
C++ symbols. Is there API for this?

On Mon, Apr 6, 2015 at 7:12 PM, OtherCrashOverride <
[email protected]> wrote:

Calling into C from C# is easy with DllImport. Calling into C++, however,
is nontrivial. The two main obstacles are 1)Name mangling differs across
compilers and 2) VTable may differ across compilers.

There is a tool to assist with doing it: https://github.com/mono/CppSharp

—
Reply to this email directly or view it on GitHub
https://github.com/dotnet/coreclr/issues/641#issuecomment-90261119.

Hey @reduz. How'd it go?

It seems like coreclr should have support for all the features you need. Perhaps you're better served getting help via stackoverflow or from some folks in the gitter channel?

haven't really found much on stackoverflow or gitter.. I'm interested in
using C# as an extension language for a C++ game engine, in this case C++
does the heavy load and calls a few C# functions. I'm not interested in a
C# entry point andI can't use DllImport.

I am unable to find any information on how to do this. I don't mind doing
low level coding or writing VM code myself. I just can't find any
information, neither here or on Microsoft's site.

On Thu, Apr 9, 2015 at 12:10 AM, Matthew Whilden [email protected]
wrote:

Hey @reduz https://github.com/reduz. How'd it go?

It seems like coreclr should have support for all the features you need.
Perhaps you're better served getting help via stackoverflow or from some
folks in the gitter channel?

—
Reply to this email directly or view it on GitHub
https://github.com/dotnet/coreclr/issues/641#issuecomment-91103149.

Doesn't Microsoft have some sort of internal documentation for the VM,
bytecode or this kind of interoperability? Is it possible to open it too?

On Thu, Apr 9, 2015 at 12:15 AM, Juan Linietsky [email protected] wrote:

haven't really found much on stackoverflow or gitter.. I'm interested in
using C# as an extension language for a C++ game engine, in this case C++
does the heavy load and calls a few C# functions. I'm not interested in a
C# entry point andI can't use DllImport.

I am unable to find any information on how to do this. I don't mind doing
low level coding or writing VM code myself. I just can't find any
information, neither here or on Microsoft's site.

On Thu, Apr 9, 2015 at 12:10 AM, Matthew Whilden <[email protected]

wrote:

Hey @reduz https://github.com/reduz. How'd it go?

It seems like coreclr should have support for all the features you need.
Perhaps you're better served getting help via stackoverflow or from some
folks in the gitter channel?

—
Reply to this email directly or view it on GitHub
https://github.com/dotnet/coreclr/issues/641#issuecomment-91103149.

I'm not an interop expert, but you can convert between function pointers and delegates using:
Marshal.GetDelegateForFunctionPointer()
Marshal.GetFunctionPointerForDelegate()
https://github.com/dotnet/coreclr/blob/cbf46fb0b6a0b209ed1caf4a680910b383e68cba/src/mscorlib/src/System/Runtime/InteropServices/Marshal.cs

You would still need an initial mechanism to invoke the first C# function, presumably using the hosting API if you don't want a C# entry point.

If you study coreconsole.cpp you'll see exactly how to load the clr and then execute managed code.

Also see this article
And this project

thanks for the answers, will give it a try soon to see if/how it works

On Thu, Apr 9, 2015 at 3:26 AM, Jake Helfert [email protected]
wrote:

If you study coreconsole.cpp
https://github.com/dotnet/coreclr/blob/master/src/coreclr/hosts/coreconsole/coreconsole.cpp
you'll see exactly how to load the clr and then execute managed code.

Also see this article
http://www.fancy-development.net/hosting-net-core-clr-in-your-own-process
And this project https://github.com/fancyDevelopment/Fancy.CoreClrHost

—
Reply to this email directly or view it on GitHub
https://github.com/dotnet/coreclr/issues/641#issuecomment-91126366.

@jakesays code you have posted make use of "windows.h". Do you know any sources that works on Linux and OS X?

@Marqin try unixcorerun.

@akoeplinger I tried, but it only can run whole assembly. I just want to call few selected C# functions.

@Marqin I meant you should take that file as an example of how to load the CLR. You need to adapt it to your own needs ;)

@Marqin take a look at src\dlls\mscoree\unixinterface.cpp how the ExecuteAssembly is implemented. At line 207, you can see that we create a delegate to a function to execute and then call it. You can do the same for the functions you want to call.
To use it in your C++ game engine, somewhere in your engine initialization, you will need to do all the steps that we do in the ExecuteAssembly upto the point where the delegate is created.
Then in your engine shutdown, you'll need to do the rest of the stuff that we do in the ExecuteAssembly, that means unloading the app domain and stopping the host.

@janvorli I was trying to try that, but I'm getting "ExecuteAssembly failed - status: 0x80131040" for every dll/method I try to envoke when using that custom entrypoint.

The symbolic name of that error code is FUSION_E_REF_DEF_MISMATCH. It can stem from the case when you reference an assembly during the managed code compilation, but the assembly that you provide at runtime doesn't match (has different strong name, version, ...).

@janvorli but, when I just pass managedAssemblyAbsolutePath it loads fine from Main entry point. Problem is when I specify my own entry point - I don't get how that's connected to assembly not matching. Also, is there any way to check which part of Assembly is not matching?

Are you trying to use the code path in the unixinterface.cpp that creates the delegate (by passing in non-NULL entryPointAssemblyName, entryPointTypeName and entryPointMethodName) or do you use your own code? If it is the latter, can you share that piece of code with me so that I can take a look?

@janvorli
http://pastebin.com/HBbYKnT1 this is my code, that is just modified ./src/coreclr/hosts/unixcoreruncommon/coreruncommon.cpp from official repo. And then I have there

managedAssemblyAbsolutePath, NULL,  NULL, NULL

it's running Main() from Square.dll and it's OK. But when I change it to

NULL,  "Square", "Square", "SquareFour"

I'm getting 0x80131040. From what I see it uses function from unixinterface to create that delegate. I also tried recreating unixinterface.cpp but that put me into some include dependecy hell from whole coreCLR, so I'm currently trying to do it this way.

Can you try to run it under debugger, set a breakpoint at AssemblySpec::LoadDomainAssemblystep and when it is hit, step through the code and see where the error comes from? You can also set a breakpoint to all places in the coreclr source where you can see
hr = FUSION_E_REF_DEF_MISMATCH;,
IF_FAIL_GO(FUSION_E_REF_DEF_MISMATCH);,
IfFailGo(FUSION_E_REF_DEF_MISMATCH); and
ThrowHR(FUSION_E_REF_DEF_MISMATCH);
I can see 8 such places. Then you should hit the breakpoint at the place the error stems from.

@janvorli I'm currently using coreCLR downloaded from "DNX SDK" from Readme and debbuger cannot find those. There is libcorecrl.so with debbuging symbols on coreCLR CI to download, but I cannot find there mscorlib.dll that would be compatible with -debug libcorecrl.so, and I do not have Windows to create my own mscorlib.dll :-1:
Is there any publicly available mscorlib.dll for x64 bit Linux or OS X, that is compatible with newest libcorecrl.so from Your CI system?

@Marqin you can get mscorlib.dll for linux from the ci build artifacts here http://dotnet-ci.cloudapp.net/job/dotnet_coreclr_windows_debug/

@Marqin You can always get the latest debug version of the mscorlib.dll here:
http://dotnet-ci.cloudapp.net/job/dotnet_coreclr_windows_debug
Get the bin/Product/Linux.x64.Debug/mscorlib.dll one.

@janvorli @shahid-pk Thanks!
So I've debugged and it breaks on assemblybinder.cpp:158, which is caused, because of line assemblybinder.cpp:150 ( also breakpointed that one ), which is caused because of true:

else if (pRequestedVersion->IsLargerFeatureVersion(pFoundVersion))

And IsLargerFeatureVersion that is used is defined in assemblyversion.inl:79, then I've printed:

(lldb) p GetMajor()
(DWORD) $0 = 4294967295
(lldb) p pAssemblyVersion->GetMajor()
(DWORD) $1 = 0
(lldb) p GetMinor()
(DWORD) $2 = 4294967295
(lldb) p pAssemblyVersion->GetMinor()
(DWORD) $3 = 0

And btw. 4294967295 == 0 - 1 in terms of unsigned int.

Interesting. Since the pRequestedVersion is major=(DWORD)-1, minor=(DWORD)-1, that means no explicit version was requested:

BOOL AssemblyName::HaveAssemblyVersion()
{
    return (m_version.GetMajor() != static_cast<DWORD>(-1));
}

Can you please print the fBeingBoundToPlatformAssembly and fWindowsPhone7 at the breakpoint?
Anyways, it looks like the condition below doesn't allow requesting assembly without specifying its version. I don't know this code, but it looks like a bug to me:

                if (!fBeingBoundToPlatformAssembly
                    && pRequestedName->HaveAssemblyVersion()
                    && !pFoundName->HaveAssemblyVersion())
                {
                    hr = FUSION_E_APP_DOMAIN_LOCKED;
                }
                else if (pRequestedVersion->IsEqualFeatureVersion(pFoundVersion))
                {
                    // Now service version matters
                    if (pRequestedVersion->IsLargerServiceVersion(pFoundVersion))
                    {
                        hr = FUSION_E_APP_DOMAIN_LOCKED;
                    }
                }
                else if (pRequestedVersion->IsLargerFeatureVersion(pFoundVersion))
                {
                    hr = FUSION_E_APP_DOMAIN_LOCKED;
                }

Could you please try to use full assembly name with version which would be "Square, Version=0.0.0.0". Maybe that would make it work.

Could you please try to use full assembly name with version which would be "Square, Version=0.0.0.0". Maybe that would make it work.

Yes! It worked that way. And about those two variables, you asked:

(lldb) p fWindowsPhone7
(bool) $0 = false
(lldb) p fBeingBoundToPlatformAssembly
(BOOL) $1 = YES

Great! So you have a workaround for now. It is strange that the fBeingBoundToPlatformAssembly is TRUE, that seems actually to be the real cause of the issue. The TRUE here means that the assembly is considered to be a platform assembly, which is not the case of your assembly.
I wonder, do you have your assembly placed in the same folder as the platform assemblies? If that's the case, that could be the culprit. Then your assembly would be enumerated in the TPA (trusted platform assemblies) list and we would considered it being platform assembly, which requires binding by version.
If that's the case, it would be interesting to try to move your assembly to a different folder, remove the version from the assembly name and try if that works too.

Yes, it was listed in TPA list, and when moved to another folder it's working without adding version string!

So that seem to work. Now, I wanted to try using directly creating Delegate in my code, not just using ExecuteAssembly, so I tried to compile unixinterface.cpp and I'm getting those compiler errors:
http://pastebin.com/mfV43Ln3
I wonder if it's possible to do that without including all those windows headers.

Unfortunately it is not reasonably doable. Using the COM interfaces pulls in a lot of windows headers and windows specific types. That's why I have created the simplified ExecuteAssembly that wraps all the stuff and exposes an interface that can be easily consumed without the windows headers usage.
I believe that what you need is something we want to support in the CoreCLR as well, so I think we should modify the existing interface and split it into three parts. One would be the initialization, including the host and appdomain creation, the second would be the delegate creation (that could be performed multiple times in case you need to be able to call multiple functions) and the last would be the appdomain unloading and host stopping.
Unfortunatelly I am on vacation starting tomorrow and ending on 7/6, so I won't be able to make such a change before getting back. But feel free to add this to the unixinterface.cpp yourself if you want. I would keep the ExecuteAssembly API, but internally let it use the same code for initialization and shutdown that you would expose as the new APIs.
I just hope that the current way of creating the delegate would work for multiple delegates. I think that there is a problem trying to use the CorHost::ExecuteAssembly multiple times, but maybe it is a different issue and maybe I am just mistaken.

PAL tries to redefine lots of standard interfaces to align the platforms. I've run into this in this pretty simple bug https://github.com/dotnet/coreclr/issues/1091 but I think there is a deeper problem in how PAL exposes things.

This is all documented here

https://github.com/dotnet/coreclr/blob/master/src/pal/src/include/pal/palinternal.h#L14-L136

@janvorli So have a nice vacation! And we would really appreciate if you could someday divide that API this way you described :)

I've thanked you in my PoC README and also added there explanation why 0x80131040 occurs - maybe it will help someone with the same problem, who cannot find it in documentation:
https://github.com/Marqin/simpleCoreCLRHost#why-we-will-get-0x80131040-error-whats-the-solution-if-i-must-have-them-in-the-same-dir

The other day I was playing with the idea to create a header-file (e.g. coreclr.h) which could contain symbols exported by _libcoreclr_. This header could be used if developer wan't to dynamically link CoreCLR.

Quickly testing I got PAL_InitializeCoreCLR working and when moving to next function used in _unixinterface.cpp_. Got stuck on CorHost2::CreateObject, but then I noticed that the _libcoreclr_ exports a nice wrapper for this, i.e. GetCLRRuntimeHost. Haven't yet have time to test that, but should be fairly straightforward since the ICLRRuntimeHost2 struct is defined in _mscoree.h_.

So to the question/comment:
In addition of creating/modifying these wrapper functions (e.g. ExecuteAssembly), wouldn't it be nice to also have a header-file that could be included also under Linux/unix-systems as _mscoree.h_ could most likely not be used. This header-file could then be used for dynamic linking of _libcoreclr_. In a _perfect_ world, this header-file could be auto-generated during build.

@Marqin I have created a github issue dotnet/coreclr#1234 to track work on the hosting API refactoring. I will just wait a bit to see if there is any feedback on the proposed API refactoring and then implement it.

@janvorli , coreclr_initialize stopped working on Linux ( on OS X it's ok ) in last months builds. Does anything changed? Here's stack trace from Linux:
http://pastebin.com/raw.php?i=AyigRNXC
The same code works OK on OS X.
Both tried with coreclr 1.0.0-rc1-16048.

@Marqin The coreclr_initialize is used in the corerun / coreconsole and so it is executed in all of our tests and any time we launch anything managed on Linux. There was no change in this function as far as I know, so I wonder what is causing the problem for you.
From your stack, it is hard to guess what was going on. It looks like an unhandled exception happened somewhere in managed code down the call chain from the coreclr_initialize.

The stack dump also shows there is a missing trap for unhandled exceptions on this code path, so I need to fix that. But that's kind of unrelated to your issue. Even when I fix that, your app would abort.
But you would at least see the managed exception logged to console after the fix.

@Marqin Now looking at the code, I believe no exceptions should go unhandled down the CorHost2::_CreateAppDomain callchain. But there was a bug related to that that I've fixed last week, so I wonder if it is possible that you have a version of coreclr before that fix.
The fix was merged in as commit 7a08037ba5c7d5858485985dcb1d4750c77d31a0.
Could you please check if the latest CoreCLR fixes the issue for you?

Ok, now, with the latest CI build there's no exception on Linux,coreclr_initialize just returns 0x80131500 error code( but why? ). On OS X it's all ok.

Ok, I run it with debug wersion of libcoreclr.so and that's the full error that I get: http://pastebin.com/raw.php?i=USsnUBRe

Ok, that mostl likely means a mismatch betweem libcoreclr.so and mscorlib.dll. Either they are one release and the other debug or they are not from the same build.

Ah, thanks. Now in error message I see again that CLRException::GetThrowable that was in stack strace: http://pastebin.com/raw.php?i=crMpXz1v

Ok, so you are missing the System.Globalization.Native.so. It is built as part of the coreclr build.

I have System.Globalization.Native.so. Tested with both release and debug version of it, and still geting this error. Here is full stack trace from lldb: http://pastebin.com/raw.php?i=QG0hQqcD
Also, as I mentioned, there is no problem on OS X.

Interesting. So far the missing System.Globalization.Native.so was the only case where I've seen this assert. Can you please select the frame 4 and print the pException object (using p *pException)?

(lldb) f 4
frame dotnet/coreclr#4: 0x00007ffff5fbc9e8 libcoreclr.so`CLRException::GetThrowableFromException(pException=0x00000000006bdfb0) + 248 at clrex.cpp:728
(lldb) p *pException
(Exception) $0 = {
  m_innerException = 0x0000000000000000
}

Hmm, GDB shows the actual exception type when I print it like this. LLDB obviously doesn't. My guess is that the actual type of the pException is EEMessageException. Could you please also try this?:
p _(EEMessageException_)pException

Sorry, the markdown has screwed it, I meant:
p *(EEMessageException*)pException

Hmm, is says that it cannot find "System.Globalization.Native". But I have in the same folder as libcoreclr.so and all .dlls. And I also copied it inside dir with my executable, and in dir with my managed code. And it didn't helped.

http://pastebin.com/raw.php?i=znJkE1Qb

Hmm, I wonder what could be preventing the linker from seeing it.
@adityamandaleeka do you have any idea why the DllImport would not see the System.Globalization.Native.so?

I have it in libcoreclr and my binary dirs:

marqin@nibbler [09:52:30] [~/csharp/simpleCoreCLRHost] [master *]
-> % ls $SCCH_COREPATH | egrep "(mscorlib|libcoreclr\.so|Globalization\.Native)"
libcoreclr.so
mscorlib.dll
System.Globalization.Native.so
marqin@nibbler [09:52:34] [~/csharp/simpleCoreCLRHost] [master *]
-> % ls . | grep Native
System.Globalization.Native.so

This $SSCH_COREPATH dir is also in $PATH, as set by dnvm.

So it prevents on Linux, and on OS X works. But why.

@janvorli, @adityamandaleeka, I found something regarding System.Globalization.Native.so/System.Globalization.Native.dylib.
When I try to link it to some code in OS X, it just passes.
On Linux I get long list of undefined references.

Is it because I have libicuuc.so.55 and not libicuuc.so.52?

@janvorli, @adityamandaleeka, I found something regarding System.Globalization.Native.so/System.Globalization.Native.dylib.
When I try to link it to some code in OS X, it just passes.
On Linux I get long list of undefined references.

You can check what is needed with ldd name-of.so . You should have
a matching version of the ICU for things to work, yes.

Ehh, and ICU download page is giving me 500 error.
So I rebuild by myself System.Globalization.Native.so to use ICU 55 and now I'm getting 0x80070057 from coreclr_initialize.


Also, cmake didn't set -std=c++11 flag, which caused build to fail, until I modified Makefile by hand, should I report is as bug, right?

Ok, it was my bug. Now, after building System.Globalization.Native.so with ICU 55 support everything works OK :+1:

I'm going to fill bug about that c++11 flag, and that ICU 52 dependency is not documented on DNX page.

The c++11 flag is actually being set if you use the build.sh or the src/pal/tools/gen-buildsys-clang.sh. Using cmake with no parameters is not a supported scenario.

The c++11 flag is set using this:
src\pal\tools\clang-compiler-override.txt
But there is more than the c++11 flag set there, see the end of the src/pal/tools/gen-buildsys-clang.sh too where the cmake is invoked.

Thanks for clarification. So I just repored that ICU should be documented.

Is it in the documentation now? I cannot find it.

I think the documentation should be something similar to what mono has provided to host/embed coreclr in C/C++.

A related StackOverflow question.

@zwcloud here's my example code: https://github.com/Marqin/simpleCoreCLRHost

(beware, it uses Filesystem TS, for easier finding files to TPA)

For us to ever consider coreclr, it really needs to provide some way to interact with C++ properly. Similar to something like pybind11 for Python.
Especially ownership handling needs to be supported properly (way beyond COM).

CC: @yizhang82

As @zwcloud asked, is there some decent documentation?

Bah!

cc @jkoritzinsky @jeffschwMSFT

Was this page helpful?
0 / 5 - 0 ratings

Related issues

GitAntoinee picture GitAntoinee  Â·  3Comments

v0l picture v0l  Â·  3Comments

chunseoklee picture chunseoklee  Â·  3Comments

jzabroski picture jzabroski  Â·  3Comments

EgorBo picture EgorBo  Â·  3Comments