Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Unity DllNotFoundException when adding .so plugins

Discussion in 'Editor & General Support' started by jerrybeanman, Jan 15, 2016.

  1. jerrybeanman

    jerrybeanman

    Joined:
    Apr 26, 2015
    Posts:
    3
    Hey guys, I'm trying to build a .so plugin for Linux to use on Unity. So far I've compiled the C++ script and dropped the plugin into Assets/Plugins folder. When I tried to import the DLL and call my function in script I got an DllNotFoundException: pluginName. Any ideas?
    Here is my .cpp:
    Code (C++):
    1. #include "plugin.h"
    2.  
    3. void hello()
    4. {
    5.   ofstream myfile;
    6.   myfile.open ("example.txt");
    7.   myfile << "Writing this to a file.\n";
    8.   myfile.close();
    9. }
    10.  
    and .h:
    Code (C++):
    1. // basic file operations
    2. #include <iostream>
    3. #include <fstream>
    4. #include <stdexcept>
    5. using namespace std;
    6.  
    7. extern "C"
    8. {
    9.     extern void hello();
    10. }
    I build it as a .so file and put it under Assets/Plugins folder and called the function as such:
    Code (CSharp):
    1.     [DllImport("plugin")]
    2.     private static extern void hello();
    3.     void Start()
    4.     {
    5.         hello();
    6.     }
    I'm running the editor on Windows and keep getting the DllNotFound exception. Tried building it on Fedora and ran it and get the same thing
     
    Last edited: Jan 15, 2016
  2. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,559
    I think the file should be called libplugin.so if you declare it like you did.
    Also, when you select the .so file in Unity, what platforms is it listed for ?
     
  3. GMC_Jason

    GMC_Jason

    Joined:
    Apr 14, 2015
    Posts:
    30
    You have a few issues. First, the C++ function itself needs to be declared extern "C", not just the header for it. It also needs to be declared as a shared export, which is different per platform. In your cpp, replace line 3 with:

    Code (C++):
    1. extern "C" __declspec(dllexport) void hello()
    That will work for Windows (remove the __declspec(dllexport) for other platforms). In addition, for each platform, you'll have to compile to a different type of shared library. On Windows, you need a DLL. On Linux, you need an SO. And on Mac, you need a BUNDLE.

    I use Visual Studio (free here) for Windows, so I don't know the command line for that. But for Linux, use this:
    Code (Shell):
    1. g++ *.cpp -O3 -fPIC -shared -o MySharedLibrary.so
    And on Mac, use this:
    Code (Shell):
    1. clang *.cpp -O3 -dynamiclib -arch i386 -arch x86_64 -o MySharedLibrary.bundle
    Add those shared library files to your Assets directory (or a subfolder), and mark them in the editor for their respective platforms. Here's an example on Linux:
    upload_2016-1-18_12-46-6.png

    Finally, you need to make sure that they are named the same as you've marked in your C#. So, in this case, they should be plugin.dll, libPlugin.so, etc.
     
    Last edited: Jan 18, 2016
    liortal likes this.
  4. jerrybeanman

    jerrybeanman

    Joined:
    Apr 26, 2015
    Posts:
    3
    Thank you so much!!
    It worked for me after following the steps, I've been stuck on this for a week lol.
    I think it turned out that I was calling the importdll in my C# script wrong, I didnt have the extension.
    And I had a different method for compiling the library
     
    GMC_Jason likes this.
  5. qoobit

    qoobit

    Joined:
    Aug 19, 2014
    Posts:
    51
    Hi there! We're doing something similar and need to be developing platform specific solutions as well.

    Build Settings:
    Standalone->Linux running on OSX Unity 5.5.0f3

    We tried out the code as follows.

    plugin.h:
    Code (CSharp):
    1. // basic file operations
    2. #include <iostream>
    3. #include <fstream>
    4. #include <stdexcept>
    5. using namespace std;
    6.  
    7. extern "C"
    8. {
    9.     extern void zzz();
    10. }
    11.  
    plugin.cpp:
    Code (CSharp):
    1. #include "plugin.h"
    2.  
    3. extern "C" void zzz()
    4. {
    5.     ofstream myfile;
    6.     myfile.open ("example.txt");
    7.     myfile << "Writing this to a file.\n";
    8.     myfile.close();
    9. }
    10.  

    Compile:
    Code (CSharp):
    1. g++ *.cpp -O3 -fPIC -shared -o libplugin.so
    Dropped libplugin.so into:
    Assets/plugins



    Made a C# script in Unity:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5. using System.Runtime.InteropServices;
    6.  
    7. public class PluginTest : MonoBehaviour {
    8.  
    9.  
    10.     [DllImport ("plugin")]
    11.     private static extern void zzz();
    12.  
    13.     void Start () {
    14.         zzz();
    15.      }
    16.  
    17.      void Update () {
    18.      }
    19. }
    20.  
    We've tried setting libplugin to the following settings as well:
    - Any Platform
    - Standalone only > Linux x86
    - Standalone only > Linux x86_64
    - Standalone only > Linux x86 and Linux_x86_64
    - Moving libplugin.so into Assets/Plugins/Linux, Assets/Plugins/Linux/x86,Assets/Plugins/Linux/x86_64

    Must be something we're missing here.

    Thanks for any help you guys can provide.
     
  6. qoobit

    qoobit

    Joined:
    Aug 19, 2014
    Posts:
    51
    We finally got it to work! So I'll try to explain it here so everyone doesn't get stuck in the future.

    Reference: https://docs.unity3d.com/Manual/PluginsForDesktop.html

    To get your feet wet, watch this:


    It covers how to make a unmanaged C# and managed native Windows C++ DLL.
    Unmanaged generally are cross platform compatible for .NET 2.0.

    For each platform, you need to build it on their respective Operating Systems! or perhaps use VMs.

    IMPORTANT NOTE: After bringing the plugins into the Unity plugin's folder, be sure to set their respective platforms. Windows (native) and OSX (native) can have their platforms set for use in editor as well.


    For Windows (native)

    IDE Used : Visual Studio 2015
    Filename: RandomNumberDLL.dll
    Acceptable Paths: Plugins/, Plugins/x86, Plugins/x86_64, Plugins/Win/, Plugins/Win/x86, Plugins/Win/x86_64

    C++:

    Code (CSharp):
    1. #include <iostream>
    2.  
    3. extern "C" int GetRandom()
    4. {
    5.     return rand();
    6. }

    C# import syntax:

    Code (CSharp):
    1.  #if UNITY_EDITOR_WINDOWS || UNITY_STANDALONE_WINDOWS
    2.     private const string LIBRARY_NAME = "RandomNumberDLL";
    3. #endif
    4. [DllImport(LIBRARY_NAME)]
    5. private static extern int GetRandom ();
    6.  
    Follow both examples in the Youtube tutorial and it will get you both the unmanaged C# dll and the managed native C++ dll. They're built directly out of Visual Studio for both x86 and x86_64 versions. Switching between them inside VS2015 should be fairly trivial and googleable if you're stuck. As stated in the reference link, these need to be .dll format and be put in a "Plugins/" folder. If not placed in "Plugins/Win/x86" or "Plugins/Win/x86_64" folders respectively, the fall back will be used in "Plugins/". Please note that the ".dll" file extension is not required for the DllImport syntax.

    For OSX (native)


    IDE Used : Xcode
    Filename: RandomNumberBundle.bundle
    Acceptable Paths: Plugins/, Plugins/x86, Plugins/x86_64, Plugins/MacOS/, Plugins/MacOS/x86, Plugins/MacOS/x86_64


    C++:
    Code (CSharp):
    1. #include <iostream>
    2.  
    3. extern "C" int GetRandom()
    4. {
    5.     return rand();
    6. }


    C# import syntax:


    Code (CSharp):
    1. #if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
    2.   private const string LIBRARY_NAME = "RandomNumberPlugin";
    3. #endif
    4.  
    5. [DllImport(LIBRARY_NAME)]
    6. private static extern int GetRandom ();
    7.  
    In Xcode, create a new project. Under Framework & Library, choose "Bundle" and name the project "RandomNumberPlugin". After the project has been created, add the framework to the project: /Applications/Unity/Unity.app/Contents/PlaybackEngines/macstandalonesupport/Variations/universal_development_mono/UnityPlayer.app/Contents/Frameworks/MonoEmbedRuntime/osx.

    When you're done, create a C++ file and paste the code above in and build. Find the path of RandomNumberPlugin.bundle by right clicking it in the left panel and showing in finder.
    Copy it into the respective folders in Unity and you're good to go.

    Please note that the ".bundle" file extension is not required for the DllImport syntax.


    For Linux (native)


    IDE Used : Vim, Nano, Sublime or your favorite text editor
    Filename: libRandomNumberSO.so
    Acceptable Paths: Plugins/, Plugins/x86, Plugins/x86_64, Plugins/Linux/, Plugins/Linux/x86, Plugins/Linux/x86_64


    Plugin.cpp
    Code (CSharp):
    1. #include <iostream>
    2.  
    3. extern "C" int GetRandom()
    4. {
    5.     return rand();
    6. }
    Terminal:
    Code (CSharp):
    1. g++ *.cpp -O3 -fPIC -shared -o libRandomNumberSO.so
    C# import syntax:

    Code (CSharp):
    1. #if UNITY_STANDALONE_LINUX
    2. private const string LIBRARY_NAME = "RandomNumberSO";
    3. #endif
    4.  
    5. [DllImport(LIBRARY_NAME)]
    6. private static extern int GetRandom ();
    7.  
    It is super important to have "lib" as the prefix or else it won't be recognized in Unity as a linux plugin.

    Please note that the ".so" file extension and the "lib" prefix is not required for the DllImport syntax.


    For iOS (native)

    IDE Used : Vim, Nano, Sublime or your favorite text editor
    Acceptable Paths: Plugins/, Plugins/iOS


    Plugin.cpp
    Code (CSharp):
    1. #include <iostream>
    2.  
    3. extern "C" int GetRandom()
    4. {
    5.     return rand();
    6. }


    C# import syntax:


    Code (CSharp):
    1. #if UNITY_IOS
    2.   private const string LIBRARY_NAME = "__Internal";
    3. #endif
    4.  
    5. [DllImport(LIBRARY_NAME)]
    6. private static extern int GetRandom ();
    7.  
    For iOS it's a bit different. You can just copy the actual files (uncompiled) into your project's "Plugins/iOS" folder. When building out your Xcode project for iOS, it will automatically copy those over into the project where they can be directly used.


    For Android (native)

    This is the most complicated of them all so bear with me... There are two ways to import native code for Android: .jar and .so. Since we are using C/C++, we will skip the .jar method (since it bundles .class files) and opt for the .so version.

    IDE Used : Android Studio
    Acceptable Paths: Plugins/, Plugins/Android


    RandomNumberAndroid.cpp
    Code (CSharp):
    1. #include <jni.h>
    2. #include <iostream>
    3.  
    4. extern "C" int GetRandom()
    5. {
    6.     return -1;
    7. }

    Note: There is a bug with Android SDK version for using "rand()", looking around StackOverflow, people are saying that you need to target SDK v19/v21 in order for it to work. I didn't get it to work but other C code still works. For this example we will just be returning -1.

    C# import syntax:


    Code (CSharp):
    1. #if UNITY_ANDROID
    2.   private const string LIBRARY_NAME = "RandomNumberAndroid";
    3. #endif
    4.  
    5. [DllImport(LIBRARY_NAME)]
    6. private static extern int GetRandom ();
    7.  
    Begin by downloading Android Studio and follow the instructions here:
    https://developer.android.com/ndk/guides/index.html
    to setup your system for NDK

    After you've installed LLDB, CMake and NDK, create a new project and include C++ libraries and press next until you are finished. You will then be presented with a boilerplate project that has a cpp folder with a prepopulated native-lib.cpp.

    You can write your native code here or create your own cpp by right clicking the folder and choosing C/C++ Source File. We will create a new file called RandomNumberAndroid.cpp. Please note that the newly created cpp will not show up in your project by default (even though the file is created in the cpp folder). You must import it via a few lines in gradle and CMakeLists.txt.

    Open Up CMakeLists.txt, and you will see the boilerplate file and notable the following lines:
    Code (CSharp):
    1. add_library( # Sets the name of the library.
    2.              native-lib
    3.  
    4.              # Sets the library as a shared library.
    5.              SHARED
    6.  
    7.              # Provides a relative path to your source file(s).
    8.              # Associated headers in the same location as their source
    9.              # file are automatically included.
    10.              src/main/cpp/native-lib.cpp )
    Copy and paste that line while renaming the native-lib to the name of your newly created cpp (without the file extension) "RandomNumberAndroid" and update the path in the last line to "src/main/cpp/RandomNumberAndroid.cpp". Rebuild/sync gradle and it should show up now in your cpp folder on the left.

    One more thing to add before it will actually export the .so files. Open up MainActivity.java and you will see a line that looks like this:
    Code (CSharp):
    1. System.loadLibrary("native-lib");
    Replace it with:

    Code (CSharp):
    1. System.loadLibrary("RandomNumberAndroid");
    You can press Play to make sure it compiles and everything loads fine.

    From here, we must export the project as an .apk file to get access to the compiled libraries. So press Build > Build APK and locate the path where the file has been built to. Rename the .apk to .zip and uncompress the package. You will see a lib folder with your .so files compiled for all the compatible architectures. Copy these into Unity and set their appropriate platforms and you're finally good to go.

    Keep in mind that the file format for the .so file will be libRandomNumberAndroid.so.

    Please note that the ".so" file extension and the "lib" prefix is not required for the DllImport syntax.


    ----

    Comprehensive C# Import
    Code (CSharp):
    1.  #if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
    2. private const string LIBRARY_NAME = "RandomNumberBundle";
    3. #elif UNITY_EDITOR_WINDOWS || UNITY_STANDALONE_WINDOWS
    4. private const string LIBRARY_NAME = "RandomNumberDLL";
    5. #elif UNITY_STANDALONE_LINUX
    6. private const string LIBRARY_NAME = "RandomNumberSO";
    7. #eli UNITY_IOS
    8. private const string LIBRARY_NAME = "__Internal";
    9. #eli UNITY_ANDROID
    10. private const string LIBRARY_NAME = "RandomNumberAndroid";
    11. #elif UNITY_EDITOR
    12. //private const string LIBRARY_NAME = "RandomNumber";
    13. #endif
    14.  
    15. [DllImport(LIBRARY_NAME)]
    16. private static extern int GetRandom ();
    Then inside your monobehaviour scripts, just call:
    Code (CSharp):
    1. GetRandom()
    and it will work.

    Keep in mind when inside Unity Editor, it will only load the one for the editor on OSX or Windows depending one which system you're on. Trying to use the Windows DLL on OSX Editor even with Windows as the set target won't allow you to test your plugin AFAIK. The only way is to build it out for the platform and run it on that platform.

    Tested to work in OSX, Windows, Linux, iOS and Android.
     
    Last edited: Jan 31, 2017
    PierreNS, t_lith85, bab202 and 2 others like this.
  7. ModeLolito

    ModeLolito

    Joined:
    Jun 16, 2016
    Posts:
    19
    Hi, your post is very interesting. I would like get an so file using Assimp library which permits to import many 3d format like fbx. Currently that works on Windows thanks to a dll but impossible to use on android even after creating so file using assimp c++ library and NDK on android studio.
    Could you help me or do you think your tuto can help me to get an so which works using c++ library?
     
  8. qoobit

    qoobit

    Joined:
    Aug 19, 2014
    Posts:
    51
    Hi ModeLolito sorry for the late reply. If you still need help please send me a DM and I can see if I can provide any assistance.
     
  9. bab202

    bab202

    Joined:
    Oct 18, 2018
    Posts:
    9
    For now on Android, add C++ plugin just like on iOS when you using the IL2CPP scripting backend.

    Just add C++ source file to Plugins/Android folder, and use [DllImport ("__Internal")] on extern methods. No need to prebuilt to .so file anymore.

    Reference here: https://docs.unity3d.com/Manual/AndroidNativePlugins.html
     
  10. Cazforshort

    Cazforshort

    Joined:
    Feb 22, 2016
    Posts:
    16
    How does this relate when I have a .cpp and a .h file?
    Plus my cpp has includes at the top?
    Code (CSharp):
    1. #include "converter.h"
    2.  
    3. #include "libwebp/src/webp/mux_types.h"
    4. #include "libwebp/src/webp/decode.h"
    I think I need to move the whole package somewhere, but unity wont build because it can't seem to find all the files inside Plugins\Android

    Just trying to include this in my project
    https://github.com/CrypticWit/WebpConverter
     
    Last edited: Oct 21, 2022
  11. bab202

    bab202

    Joined:
    Oct 18, 2018
    Posts:
    9
    As I remember, to make the include work you have to put all the files in the same main directory because the sub directory in #include wont work. For example, move the decode.h file to same folder as converter.h and then in your include, it will be:

    Code (CSharp):
    1.  
    2. #include "converter.h"
    3. #include "decode.h"
    4.  
     
  12. Finijumper

    Finijumper

    Joined:
    Jul 12, 2016
    Posts:
    75
    Hi, I would appreciate some assistance.

    I am trying to build this library: https://github.com/ValveSoftware/GameNetworkingSockets

    And with vcpkg I got it to generate some .so files for arm-android and arm64-android, but when I build the apk for Android in Unity, I get an error after calling a function like this one:

    Code (CSharp):
    1. [DllImport("GameNetworkingSockets", CallingConvention = CallingConvention.Cdecl)]
    2.         internal static extern bool GameNetworkingSockets_Init(IntPtr identity, StringBuilder errorMessage);
    Could someone tell me what I might be doing wrong?

    Thanks :)
     
  13. bab202

    bab202

    Joined:
    Oct 18, 2018
    Posts:
    9
    Dont build the .so files, if your project use IL2CPP scripting backend, you can put the cpp sourcecode files in the Plugins folder and call function directly like below:
    Code (CSharp):
    1. [DllImport ("__Internal")]
    2. private static extern float YourNativePluginFunction();
    3.  
    Workflow to include native code for Android is the same as for iOS as you use IL2CPP scripting backend. Reference here: https://docs.unity3d.com/Manual/PluginsForIOS.html

    But NOTICE that the include with subfolder in the cpp sourcecode wont work, so you have to refactor the include so that all files are in the same main folder. Just do a test to make sure the setup works first.
     
    Last edited: Aug 2, 2023