How to use a C++ dll in Unity3D? - c#

I am aware of this similar question, but it does not respond to my problem.
I have written two .dlls using Visual Studio 2010. One is in C++, and communicates with an SDK that was written in C++. The other is a C# wrapper for that C++ library, so that it can be used in C# contexts.
My plan was that this would let me use my code in Unity3D, but apparently that is not the case. It seems like Unity3D does not allow me to import .dlls as Assets if they are not a .NET assembly. So I can add my C# wrapper, but not the C++ dll.
This results in a DllNotFoundException whenever I try to access the C++ library. I have tried simply copying the C++ library into the Assets/Plugins folder, but that gives the same results.
Is there a way to do this properly? This is a very vital part of my project setup.

The problem is that the DLL is not being found when the p/invoke runtime code calls LoadLibrary(YourNativeDllName).
You could resolve this by making sure that your DLL is on the DLL search path at the point where the first p/invoke call to it is made. For example by calling SetDllDirectory.
The solution that I personally prefer is for your managed code to p/invoke a call to LoadLibrary passing the full absolute path to the native DLL. That way when the subsequent p/invoke induced call to LoadLibrary(YourNativeDllName) is make, your native DLL is already in the process and so will be used.
internal static class NativeMethods
{
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern IntPtr LoadLibrary(
string lpFileName
);
}
And then somewhere in your code:
private static IntPtr lib;
....
public static void LoadNativeDll(string FileName)
{
if (lib != IntPtr.Zero)
{
return;
}
lib = NativeMethods.LoadLibrary(FileName);
if (lib == IntPtr.Zero)
{
throw new Win32Exception();
}
}
Just make sure that you call LoadNativeDll passing the full path to the native library, before you call any of the p/invokes to that native library.

Note that the DllNotFoundException can be caused by building your Unity DLL in Debug instead of Release!
A simple oversight that can cause a headache.

This also happens when Unity can find your DLL, but is not able to find it's dependencies. Obvious fix is to place dependency DLLs into /Plugins as well, or link your dependencies statically.
Less obvious reason is when your DLL depends on Visual Studio runtime library dynamically, i.e. is built with Properties -> C/C++ -> Code Generation -> /MD option. Change it to /MT to link with runtime statically.

Related

Unable to import c++ dll to c# project using DllImport

I'm trying to import a C++ Project Dll into a C# project. I found a lot of people talk about using DllImport. I tried using that and here is what I have-
CPP Code:
int __declspec(dllexport) beginCode(double reportId);
C# Code:
[DllImport("C:\\Users\\<my_user_id>\\Desktop\\ctxmix\\Release\\ctxmix.dll",CallingConvention =CallingConvention.Cdecl ,CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern int beginCode(double reportId);
int result = beginCode(reportId);
But when I run, I'm getting an exception - Exception thrown:
System.DllNotFoundException
Do I have to add any references for the CPP Dll in the project or do anything else apart from the code which I have on the top?
Edit: I'm trying to run my .exe using VS2015 and I get this exception on my local machine. Also, I don't see my CPP Dll in the Project->References section where as I see other references there.
The unmanaged DLL needs to be locateable by your managed process. Typically that means placing the DLL in the same directory as the executable file. But you gave used an absolute path which I presume you transcribed correctly.
You may also encounter this error if the DLL's dependencies cannot be located. That seems the likely explanation here. Most likely the MSVC runtime cannot be located when your DLL is loaded.
Using an absolute path isn't a great idea. That will break down when you distribute to another machine. Use just the DLL file name and place it in the same directory as the executable.
Your DllImport attribute seems fussy. No point specifying CharSet when there is no text. I doubt your function calls SetLastError. And do you really need ExactSpelling?

How can a C# program use a C++ dll of any version?

We're creating a dll, written in C++, providing access to some hardware. We also have a C# program that uses this dll.
We're having an issue with the versions. Indeed, when running the C# program, it absolutely wants to use the exact C++ dll version it used when compiling. I.e. if the C# program was compiled using C++ dll 1.2.3.4, then the program will refuse to run with C++ dll 1.2.3.5.
I'd like to instruct the C# program to use any C++ dll with version 1.2.anything.
Where can I configure this in the C# project?
This question has been superseded by that one, more related to COM.
Nothing this fancy exists in C++. Using a side-by-side manifest technically permits this but you would have known about it since you would have typed the version number in the manifest of your C# program.
The far more likely explanation is that you actually created a C++/CLI assembly. Many programmers confuse C++/CLI with C++. Easy mistake since that language permits using native C++ code. But it actually gets compiled to a mixed-mode assembly, an assembly that contains both IL and native code. The normal CLR version checking occurs for such an assembly when the CLR loads it, it is only happy with an exact version match. A strong DLL Hell counter-measure.
And the normal CLR version wrangling option is available to bypass this check, a <bindingRedirect> element in your app.exe.config file. As well as controlling the assembly version number the way you do it for your C# code so this isn't necessary.
The easiest way to check if this guess is accurate is by using Project + Add Reference and select the DLL. If that doesn't draw any complaint and the assembly gets added to the References node of your C# project then you know it is a normal .NET assembly. Don't forget to take advantage of that, no pinvoke required.
Load the dll at runtime and use reflection to call it's methods.
Assembly assembly = Assembly.LoadFrom("C:\\test.dll");
Assembly.GetTypes();
Activator.CreateInstance(type);
I don't think it is possible to configure your program to use 1.2.* version and no others. Unless you would write the code for that on your own. Another possibility would be not to change the version tag of the C++ dll, but it does not seem you want to that.
A solution avoiding the version dependency would be the usage of dllimport. You can load any dll written in C++ with it. It is free of version dependency. See the example from the msdn and link at the end:
using System;
using System.Runtime.InteropServices;
class Example
{
// Use DllImport to import the Win32 MessageBox function.
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int MessageBox(IntPtr hWnd, String text, String caption, uint type);
static void Main()
{
// Call the MessageBox function using platform invoke.
MessageBox(new IntPtr(0), "Hello World!", "Hello Dialog", 0);
}
}
Description in MSDN

Using DllImport to load unmanaged dll into managed application

In my project I have an unmanaged native C++ dll and a C# application. I am trying to import a function from the unmanaged dll using DllImport but I keep getting a DllNotFoundException.
Here is my code that calls the DLL.
using System.Runtime.InteropServices;
namespace TestApp
{
public delegate void UpdateDelegate(string s);
class Program
{
[DllImport("CGPUnmanagedLibrary.dll")]
internal static extern int parse_raw_gsod_file(
[MarshalAs(UnmanagedType.LPStr)]
string filePath,
int minTemp,
UpdateDelegate callBack);
static void Main(string[] args)
{
UpdateDelegate myCallBack = new UpdateDelegate(Program.Report);
string path = #"C:\Creative Solutions\Projects\Clyde's Garden Planner\Frost Data Database\GSOD Data\GSOD_RAW_DATA\1992\gsod_1992.txt";
int result = parse_raw_gsod_file(path, 32, myCallBack);
Console.Write("Parse completed with exit code: " + result.ToString());
Console.ReadLine();
} // end main function
public static void Report(string msg)
{
Console.Write("Message is ");
Console.WriteLine(msg);
}
} // End class
} // end namespace
I tried copying the DLL to the app output directory but it still can't find it. I also tried adding the DLL project as a reference but I get a popup window saying it can't be added. How do you properly link an unmanged DLL to a managed application?
Update - Here is the full error:
Unable to load DLL 'CGPUnmanagedLibrary': The specified module could not be found. (Exception from HRESULT: 0x8007007E)
Update 2 - I know for sure that the DLL is in the same directory as the .exe trying to load it. This makes me think there is a dependency in the DLL that isn't getting loaded. I'm only using basic C++ libraries in the DLL (string, math, iostream, etc). Any ideas what could not be loading and why?
Update 3 - Tested with Dependency Walker
Loading my unmanaged C++ DLL in dependency walker showed no errors. I also tried to open my executable in dependency walker and it showed errors loading two DLLs: GPSVC.DLL and IESHIMS.DLL - doesn't make any sense because I am only using standard c++ libraries in my code. I think it may have something to do with the fact that I have a managed C++/CLI DLL trying to load the unmanaged DLL as well (I was trying to implement some C++/CLI wrappers). Anyway, I have since started a new VS solution and moved on. See my answer.
In all likelihood the problem isn't the DLL you're trying to load but one of its (chained) dependencies. Run depends.exe or a similar utility on the DLL to see if all the dependencies can be found. The misleading message "The specified module could not be found" has become a classic annoyance (if not FAQ material!): it leads you to think that your DLL is not being found when almost all of the time it's one of its dependencies that's not being found.
To test, the *.dll needs to be in the same directory as the .exe that is trying to load it. Don't trust Visual Studio to do it for you at this point. Physically copy the file to C:******\Debug\x86\bin\ or whichever configuration you are running under. If in doubt, copy it to all of your bin folders. After you figure out the path, then you start finding ways to automate the project build to copy the file correctly. If that doesn't do it, put it in system32--it will certainly find it there. However, if after doing these things, you still can't find it. There is probably a dependency to your unmanaged dll that is also missing.
First I want to thank everyone for their help. Unfortunately I never did solve this issue (see edits in my main question). The answer turned out to be starting a totally new Visual Studio solution and creating two new projects: C# app and C++ dll. I did away with the need for wrappers as I am now just marshaling two main functions.
Thanks again.
The anwer of user arayq2 made much sense and I was quickly able to solve my problem also.
The dll that couldn't be loaded (DllNotFoundException) in my case is depending upon another dll. This dll (that is not part of my project) was actually compiled with newer versions of certain .h and .lib files. Older versions of these .h and .lib files (with the same filename) were part of the project that compiled the dll that couldn't be loaded.
After I updated my dll project with the newer versions of these .h and .lib files and recompiling my dll project, my problem was solved.
Thank you arayq2!

How can I control when a DLL is detached

I got a DLL that I want to add to my C# project and I have some problems. First, my DLL is coded in C++ and I got an interface of one function to export it.
extern "C" __declspec(dllexport) char* sniff()
{
return ps.Sniff();
}
I have an instance "ps" that initialise a socket when the DLL is attached. The point is that I need to have this instance initialise when I call my exported function. My problem is when I import it in my C# project, my DLL is detached for no apparent reason and I can no longer call my exported function.
I use this syntax in my C# projet :
[DllImport(#"C:\Documents and Settings\Pat\Bureau\sniffoporn\Release\sniff.dll", EntryPoint = "sniff", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr sniff();
Some important things that I tryed :
Import the DLL in the reference of the project : Can't because it's not a COM component.
Manually import the DLL using LoadLibrary, FreeLibrary and GetProcAddress : Same result
Call my exported function in loop : Same thing but the DLL is attached for a little longer before detaching.
I would want to know why my DLL is detaching and how can I keep it attached for the rest of the process life.
Thanks a lot
Maybe an exception occurs in your DLL itself. Making it detach in Visual Studio to prevent your application from crashing. After all C# is managed code.
Try your same procedures with a different (dummy) dll with very simple functionality. If it still occurs then something with your method is wrong, otherwise with the DLL you are attempting to use.

Unable to load Win32 Native DLL file from C#.NET

I have a C# winapp. I call a native .dll file (created in C++ by myself) from the C# app, and it works fine.
But when I copy my application (.exe and .dll files) to another machine, I get an error that says:
Unable to load DLL "c:\dllname.dll": The specified module could not be found. (Exception from HRESULT: 0x8007007E)
Here is the C# code:
class IsoMessageHelper
{
public const string ISO8583_DLL = "c:\\Hc8583.dll";
[DllImport(ISO8583_DLL, CallingConvention = CallingConvention.Cdecl)]
public static extern bool InitializationRq(...)
}
What should I do?
A common issue when deploying .Net applications that have native dependencies, is that the native dlls may be missing dependencies themselves on the target machines e.g. the correct version of the C runtime.
Use a tool such a Dependency Walker to analyze your native dll and determine if it has a missing dependency on the machine you have copied it too.
Try not to hard code any paths in the DllImport attribute parameter that specifies the name of the file. Then you should make usre the file is right besides the executable.
Something like this:
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
Move the DLL to the root. If that works, then look at your attribute to determine why. You haven't posted any code, so I can't give you any specific reason.

Categories

Resources