Search Unity

Unloading Native Plugins in the Unity Editor

Discussion in 'Scripting' started by FatiguedArtist, Aug 31, 2013.

  1. FatiguedArtist

    FatiguedArtist

    Joined:
    Mar 3, 2010
    Posts:
    38
    For those who, like me, have suffered endlessly closing and re-opening the Unity editor every time they wish to test a change to their native plugin, I have good news.

    Here is my first of many blog posts on my site, this time outlining how to streamline your native plugin workflow:
    http://runningdimensions.com/blog/?p=5

    Beware: This is for testing purposes on Windows only.
    Edit: Also, beware this will not support native plugins which hook into the rendering pipeline.

    Code snippets of interest:
    Code (csharp):
    1.  
    2. /*
    3.  * Native dll invocation helper by Francis R. Griffiths-Keam
    4.  * www.runningdimensions.com/blog
    5.  */
    6.  
    7. using System;
    8. using System.Runtime.InteropServices;
    9. using UnityEngine;
    10.  
    11. public static class Native
    12. {
    13.     public static T Invoke<T, T2>(IntPtr library, params object[] pars)
    14.     {
    15.         IntPtr funcPtr = GetProcAddress(library, typeof(T2).Name);
    16.         if (funcPtr == IntPtr.Zero)
    17.         {
    18.             Debug.LogWarning("Could not gain reference to method address.");
    19.             return default(T);
    20.         }
    21.  
    22.         var func = Marshal.GetDelegateForFunctionPointer(GetProcAddress(library, typeof(T2).Name), typeof(T2));
    23.         return (T)func.DynamicInvoke(pars);
    24.     }
    25.  
    26.     public static void Invoke<T>(IntPtr library, params object[] pars)
    27.     {
    28.         IntPtr funcPtr = GetProcAddress(library, typeof(T).Name);
    29.         if (funcPtr == IntPtr.Zero)
    30.         {
    31.             Debug.LogWarning("Could not gain reference to method address.");
    32.             return;
    33.         }
    34.  
    35.         var func = Marshal.GetDelegateForFunctionPointer(funcPtr, typeof(T));
    36.         func.DynamicInvoke(pars);
    37.     }
    38.  
    39.     [DllImport("kernel32", SetLastError = true)]
    40.     [return: MarshalAs(UnmanagedType.Bool)]
    41.     public static extern bool FreeLibrary(IntPtr hModule);
    42.  
    43.     [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
    44.     public static extern IntPtr LoadLibrary(string lpFileName);
    45.  
    46.     [DllImport("kernel32")]
    47.     public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
    48. }
    49.  
    Code (csharp):
    1.  
    2. using System;
    3. using UnityEngine;
    4.  
    5. public class Example : MonoBehaviour
    6. {
    7.     static IntPtr nativeLibraryPtr;
    8.  
    9.     delegate int MultiplyFloat(float number, float multiplyBy);
    10.     delegate void DoSomething(string words);
    11.  
    12.     void Awake()
    13.     {
    14.         if (nativeLibraryPtr != IntPtr.Zero) return;
    15.  
    16.         nativeLibraryPtr = Native.LoadLibrary("MyNativeLibraryName");
    17.         if (nativeLibraryPtr == IntPtr.Zero)
    18.         {
    19.             Debug.LogError("Failed to load native library");
    20.         }
    21.     }
    22.  
    23.     void Update()
    24.     {
    25.         Native.Invoke<DoSomething>(nativeLibraryPtr, "Hello, World!");
    26.         int result = Native.Invoke<int, MultiplyFloat>(nativeLibraryPtr, 10, 5); // Should return the number 50.
    27.     }
    28.  
    29.     void OnApplicationQuit()
    30.     {
    31.         if (nativeLibraryPtr == IntPtr.Zero) return;
    32.  
    33.         Debug.Log(Native.FreeLibrary(nativeLibraryPtr)
    34.                       ? "Native library successfully unloaded."
    35.                       : "Native library could not be unloaded.");
    36.     }
    37. }
    38.  
     
    Last edited: Sep 3, 2013
    stefan-velnita, levlaj and J_Moller like this.
  2. dimitroff

    dimitroff

    Joined:
    Apr 3, 2013
    Posts:
    131
    Thanks a bunch for this! I was really tired of opening and closing Unity :mad:
     
  3. dertom95

    dertom95

    Joined:
    Jan 27, 2014
    Posts:
    9
    Thanks a lot. I know this post is funky old and I cannot believe this problem is still there (at least for me). Again, thx! Otherwise I would have stopped developing a native plugin without even starting....
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,692
    Thanks for this little workaround. Another way I use all the time is to make an actual Windows or Mac binary build of the entire project, and export it somewhere to use it as a "test harness" for the native library inside of it.

    Then you fire up your Visual Studio or XCode to edit and rebuild the DLL, and have an optional post-build step that copies the DLL or the .a file into the completely-built game's data folder.

    Then all you need to do is launch and exit the game product itself in between DLL/a file rebuilds. I often make a stripped-down build that bypasses UI and goes directly to the scene I need to have for the native library testing, just to speed up startup or shutdown.

    NOTE: this does not use the Unity editor at all once the binary is built, so it relies on you having enough logging and debug output presentation already in the project to get meaningful information back from your native library changes.
     
  5. onehand

    onehand

    Joined:
    Dec 3, 2015
    Posts:
    8
    This is amazing.
    I only wish this approach would work with render pipeline native plugins.
     
  6. dchipman

    dchipman

    Joined:
    Jan 4, 2015
    Posts:
    39
    Thanks for surfacing this solution. It's rather frustrating that Unity doesn't have a proper solution for this.

    PSA for anyone else who comes across this: I would like to emphasize OP's warning about this being only for testing purposes. Not only is it Windows specific, but it leverages Delegate.DynamicInvoke out of necessity, which is ~500x slower than binding via the standard DllImport attribute. To provide ballpark frame of reference: running a simple addition function 10,000 times with DllImport takes about 0.55ms per frame, whereas this workaround is 270ms (not to mention churn on the GC). With this in mind I'm probably going to #if wrap all of my bindings and only enable this method of binding when I'm actively working on the native library. This will bloat my wrappers, but I'm hoping to automate generating the wrappers anyways. Even if you plan on only shipping your project on Windows, the performance of this would make leaning on this for release builds a very bad idea.
     
  7. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,692
    Why would it not work for that? Are the render pipeline native plugins not stored in a DLL or something?
     
    eanders-ms likes this.
  8. vsbaranov83

    vsbaranov83

    Joined:
    Nov 23, 2020
    Posts:
    2
    Maybe somebody have this Native module for linux? I mean that worked with ".so" files rather then ".dll". How i can change the source with minimal pain to force Unity to load .so file quickly. Is it possible in principle or this is in realm of paranormal
     
    Last edited: Jun 30, 2021
    Sneirox likes this.