- Home /
Import (mixed assembly) managed C++/CLI DLL plugin
This post covers my experiences with managed C++/CLI DLLs and ends with the question if it is possible to do it. Up to now I haven't been able to make it work.
So I do have some heavy and powerful C++ DLLs I need to use in my project. According to the Unity documentation there are two ways
Managed plugins (e.g. C# or C++/CLI)
Native plugins (e.g. C++)
Since it would be easier to use in Unity I tried the managed C++/CLI path. C++/CLI is a language developed by Microsoft to write managed C++ code which can use managed (.NET) and native (C++) functions as well as data structures. There is no need to do P/Invoke if we use the /clr compiler options and there's also no marshaling. Sounded about right.
Managed Plugin with C++/CLI
Prequisites
So we need to write a managed C++/CLI DLL in Visual Studio since this is the only environment offering a C++/CLI compiler. Since Unity uses an older version of Mono, we're limited to version 3.5 of the .NET framework and version 2 of the CLR (Source).
This means we need to install Visual Studio 2008 Express since this is the last version shipping .NET 3.5 (Source). Also I needed a 64 bit compiler since it is not shipped with VS 2008 Express. But it can be downloaded here as a part of the Windows SDK.
Project Hierarchy and Setup
Following this advises I created a VS solution consisting of two parts:
An interface written in C#
An implementation of that interface in C++/CLI that should eventually use my C++ DLLs
Furthermore we have a Unity C# script that uses and accesses the imported DLLs in Unity.
One of the main reasons to go this way is to overcome the issue that Unity doesn't release DLLs once they've been used the first time (Source). So loading the during runtime sounded about right. Plus I can directly deploy new versions of my C++/CLI DLL into the Unity folder and don't have to import it manually.
(Turned out, that Unity also doesn't release these DLLs. So one can either restart Unity or try to use this hack)).
C# Interface
This is the interface stub:
namespace IInterface
{
public interface IInterface
{
string Function1(string value);
}
}
It's compiled to a DLL using Visual Studio and selecting .Net Framework 3.5 as target framework. The DLL is imported into Unity as a normal asset/plugin to allow Unity to know about the interface.
C++/CLI Class implementing the interface
Header file:
using namespace System;
using namespace IInterface;
namespace InformationExtractor {
public ref class InformationExtractor : public IInterface
{
public:
virtual String^ __clrcall Function1(String^ value);
};
}
C++-file:
#include "stdafx.h"
#include "InformationExtractor.h"
String^ __clrcall InformationExtractor::InformationExtractor::Function1(String^ value)
{
return value + value;
}
Obviously depending on IInterface this is built to a 64bit DLL called Information_Extractor.dll setting the following features:
Platform Toolset: Visual Studio 2008 (v90)
Common Language Runtime Support: Common Language Runtime Support (/clr)
In my case I deployed it directly to the project folder of my Unity project (not the asset folder) to avoid the manual import process.
Up to here the code is verified to work within the .NET framework. So if I add another C# project to my Visual Studio solution, I can instantiate an object and call the function.
Unity C# Script
This is the Unity script using the DLL
using UnityEngine;
using System.Collections;
using System.Reflection;
using System;
public class TestScript : MonoBehaviour {
void Awake () {
Assembly dll = Assembly.LoadFile("Information_Extractor.dll");
System.Type Extractor = dll.GetType("InformationExtractor.InformationExtracter");
IInterface.IInterface extractor = (IInterface.IInterface)Activator.CreateInstance(Extractor);
Debug.Log(extractor.Function1("Hello World!"));
}
}
The DLL Import code is derived from the same solution that also pointed out the interface idea.
So what we do is to load the Information_Extractor.dll which is in the Unity project folder, extract the class, instantiate an object and call the function.
Here's where the trouble starts. So we make a little extra tour to Mono.
Unity and Mono
Unity uses the Mono implementation of the .NET framework, so we're limited to what Mono can do for us. As of version 5.3.4f1 of Unity it is shipped with Mono 2.0.50727.1433. (Find out using this command)
So that (derived) runtime version of Mono has to be able to deal with our DLL.
But according to the Mono C++ homepage, Mono can't deal with CLI if the code is a mix of CIL and native code, which is usually produced by the VS compiler.
Compiling the C++/CLI DLL
Microsoft's C++/CLI language allows us to use different compiler flags:
/clr
/clr:pure
/clr:safe
A basic explanation of their capabilities can be found in this blog post. A feature comparison is here. You can set this flag in the projects properties under Configuration Properties -> General -> Common Language Runtime Support.
Using /clr
Import the DLL using the code above throws this BadImageFormatException (Reference on MSDN). According to the MSDN article about Assembly.LoadFile this can happen if the format is not valid or another version of the CRT is used.
My personal guess is, that Mono throws this exception since the DLL is a mixed assembly and not solely featuring CIL instructions since the same compiler settings work with "/clr:safe".
Using /clr:pure
Compiling this C++/CLI DLL using the /clr:pure compiler option throws another exception *huray*. This time it's a NotImplementedException when instantiating the class. According to the MSDN Article about Activator.CreateInstance, this function can't throw a NotImplementedException. So this should be a Mono thing.
According to the Mono C++ page mentioned earlier this is probably thrown due to Mono's lack to have implemented the CRT DLL.
To overcome this, Mono suggests to add this signature to the code:
#pragma warning(disable:4483)
void __clrcall __identifier(".cctor")()
{
}
and to follow this MSDN article to create compiled code without a reference to the CRT DLL.
That actually worked for me, but when I started to add my native C++ code and tried to create an object using the keyword "new" in my C++/CLI DLL (using this technique), Unity/Mono complained about not finding a the MSVCR90D.dll by throwing an exception *huray²*.
DllNotFoundException: MSVCR90D.dll
<Module>.new (UInt64 size)
InformationExtractor.InformationExtractor.initialize ()
TestScript.Awake () (at Assets/Scripts/TestScript.cs:20)
One can just add this DLL to the Unity project folder, but that makes Unity crash and is probably also not the way we want to go.
Using /clr:safe
his actually works in terms of - I can import the DLL and call functions. This works even without the Mono-avoid-CRT workaround since CRT is not allowed in /clr:safe (Source).
But as far as I've understood the feature set of this flag, it also doesn't really help me to access and use my native C++ DLLs since I'm limited to P/Invokes which I can also use with a native plugin. In contrast to "/clr:pure" (as far as I've understood it) it is also not possible to use native types.
Conclusion and Question
Not mentioning a lot of pitfalls, that's how far I got.
My current conclusion is, that there's no easy and good way to use a C++/CLI DLL in Unity.
But I'm very open for suggestions or tips about how to use them. Meanwhile, I'll start to develop a native plugins since this seems the way to go.
A sight for sore eyes--I'm trying to do this too. I did most of what you did, up through the BadImageFormatException. Then I realized I'm using VS2015 and you're on 2008--I gotta say, I am not a fan of reverting to that in order to get this to work, but it might be the only way. VS2015 seems to only support /clr, and not /clr:pure or /clr:safe (at least, it recommends against them).
Regarding the missing $$anonymous$$SVCR90D.dll, you may be able to just stick that in your Windows/SysWOW64 folder.
As far as I've understood things, you don't need to use VS 2008 itself, but just need to install the compiler/framework. So selecting VS 2008 as platform toolset should be enough. I'm using VS 2013 myself.
Also, as far as I know, you can also use any other .NET compiler, but you risk, that this compiler will use features that are not implemented in Unity's version of $$anonymous$$ono.
$$anonymous$$eanwhile, I tried to integrate some of my C++ code into my C++/CLI project. First I had a lot of
Error 1223 error LN$$anonymous$$2019: unresolved external symbol "public: __clrcall bla::~bla(void)" (??1bla@@$$FQEA$$anonymous$$@XZ) referenced in function "public: virtual void __clrcall InformationExtracter::InformationExtracter::initialize(void)" (?initialize@InformationExtracter@1@$$FUE$AA$$anonymous$$XXZ) C:\Users\Foo\Solution\Project\InformationExtracter.obj Information_Extracter
According to this answer on StackOverflow, C++ headers should have a
#pragma managed(push, off)
#include <NativeHeader.h>
#pragma managed(pop)
around them since the linker tries to find them with the __clrcall prefix.
But that just created just tons of
Error 89 error C3642: 'int _atexit_m_appdomain(void (__clrcall *)(void))' : cannot call a function with __clrcall calling convention from native code C:\Program Files (x86)\$$anonymous$$icrosoft Visual Studio 9.0\VC\include\stdlib.h 395 1 Information_Extracter
It seems that my C++ DLLs use stdlib.h and stdlib.h tries to call managed code which is not allowed from native code.
I knew about this limitation and also about the fact that you can't have native function signatures. But I thought that just applies to the functions I'll create in my C++/CLI project.
In the meanwhile I found a rather good solution to integrate/access my C++ DLLs into a managed class following this how to. Since there are also a lot of limitations, pitholes and $$anonymous$$ono/.NET issues on the C++/CLI path, that is probably also a better and more stable way to go.
Your answer
Follow this Question
Related Questions
Unity plugins 0 Answers
Native c++ (mingw) plugin works in editor but not in build 1 Answer
Problem importing a mixed-mode (C++/CLI) library 2 Answers
Using Native C++ dll in Unity 1 Answer
Is there a way to create a C++ Managed DLL for Unity? 0 Answers