7

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.

Community
  • 1
  • 1
Lee White
  • 3,649
  • 8
  • 37
  • 62
  • How are you calling the C++ DLL? You shouldn't need to be adding it to your solution as a reference.. it's a native resource. – Simon Whitehead Apr 10 '13 at 10:44
  • @SimonWhitehead I use `PInvoke` to call the C++ functions, with `[DllImport("Foo.dll")]`. It works when the two dlls are in the same folder, but I have no idea how Unity handles dlls plugins. – Lee White Apr 10 '13 at 10:47
  • What license are you using? What is your target? – aukaost Apr 10 '13 at 10:51

3 Answers3

7

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.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Will I really have to use the absolute path to the library for this? I am not the only person working on this project, so that would make things very complicated... – Lee White Apr 10 '13 at 10:56
  • Well yes. But that doesn't mean that you have to hard code the path. You can build it up at run time. Put the native DLL in the same folder and the managed DLL, and build up the path that way. Your p/invoke declarations still use just the DLL name, i.e. `"mydll.dll"`. But there's no getting away from the fact that the system has to find your DLL somehow. You can't just put a DLL anywhere you fancy and hope that the system will find it for you. – David Heffernan Apr 10 '13 at 10:58
  • I know that I can not just randomly place it anywhere; I was hoping that Unity3D would let me specify it as a dependency somehow so that it would get accessed in a way similar to how the C# one is accessed. When you say I can build up the path at runtime, how do you suggest I do that? Unity3D moves my dll around once it has been imported as a plugin, so I'm not sure how I can do that. – Lee White Apr 10 '13 at 11:07
  • Native DLLs are different. You cannot add them as references in the same way. You have to get down and dirty with DLL search paths and the like. I don't know anything on the Unity plugin side. I do know that the method in the answer works, so long as you can work out where your DLL is located. – David Heffernan Apr 10 '13 at 11:11
  • So, after using that code, I still use PInvoke the way I always did? Or will I change my existing code to somehow use the `lib` pointer? – Lee White Apr 10 '13 at 11:52
  • No, the beauty is that you use the p/invoke exactly as before. When the p/invoke is first executed, the framework calls `LoadLibrary(YouDllName)`. And if a DLL with that name is already loaded then it is used. – David Heffernan Apr 10 '13 at 11:54
  • I see. Thanks a lot, this is very useful. I'm still stuck though, but this is maybe material for a new question: When executing this in a console application, it works like a charm. When trying in a Unity project, `lib` always remains `IntPtr.Zero` even though all checks for the file's existence are positive. Really strange... Nevertheless, I'm upvoting and accepting your answer. – Lee White Apr 10 '13 at 12:07
  • That sounds like a failure to resolve other dependencies. That is the DLLs that your native DLL rely on. – David Heffernan Apr 10 '13 at 12:12
  • The `DllNotFoundException`'s error message specifies the name of the C++ library though. Is that to be expected when it's not the dll, but one of its dependencies that can't be loeded? – Lee White Apr 10 '13 at 12:30
  • Yes I think that's quite plausible. What dependencies does your DLL have? – David Heffernan Apr 10 '13 at 12:38
  • A C++ SDK that otherwise links perfectly, no matter where I place my files. – Lee White Apr 10 '13 at 12:40
  • I'd normally suggest using Dependency Walker in profile mode to debug this. But I'm not sure if it will work inside a managed process. And I don't know what the managed equivalent is. – David Heffernan Apr 10 '13 at 12:47
  • Is there a Mac/OSX version of this? – Forest J. Handford Dec 17 '15 at 20:37
2

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.

bugmagnet
  • 191
  • 1
  • 5
  • So theres no way to run it in editor to test stuff you have to compile to release every time ? – WDUK Jul 09 '20 at 07:11
  • As far as I know, that is correct. You can not instrument the DLL while running it in Unity. I've worked around this by making C++ test units, but yea, that's it. – bugmagnet Jul 12 '20 at 17:19
2

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.

Alexey Podlasov
  • 993
  • 10
  • 18