Reloading native plugins

Hi

I’m developing a plugin for a project. To give a bit of context its a tool for generating assets in the editor, and needs c++ to voxelise efficiently. However, I’ve rapidly hit upon the issue that once Unity loads a DLL, it won’t unload (or even release) it without restarting Unity. This makes development of the native plugin insanely slow, as in order to make a change and test it I have to change, close unity, build, open unity, test etc. Not a nice dev cycle!

Has anybody hit upon this and found any nice solutions. The best I can think of is to use an approach like this one:

http://blogs.msdn.com/b/jonathanswift/archive/2006/10/03/dynamically-calling-an-unmanaged-dll-from-.net-_2800_c_23002900_.aspx

Loading the DLL dynamically when in the editor. I could even take temporary copies and detect when the source DLL reloaded, giving me a ‘live update’ style system.

However that’d mean defining the function linkage differently depending on whether you were in editor or not, which is a pain!

Any thoughts?

Thanks

-Chris

There is a (Windows only) blog post for a work-around: http://runningdimensions.com/blog/?p=5

Here is a tool that quite easly allows just that. You don’t have to modify your code, just simply add script to your scene.

Its nearly 2017 and this is still a huge pain. I couldnt find a solution that worked on OSX but I cobbled one together from a few places (sorry, lost track of the original links). Requires more boilerplate than Dllimport but at least it works.

class FunctionLoader
	{
		public static Delegate LoadFunction<T>(string dllPath, string functionName)
		{
			var hModule = dl_native.LoadLibrary(dllPath);
			handles.Add(hModule);
			var functionAddress = dl_native.GetProcAddress(hModule, functionName);
			return Marshal.GetDelegateForFunctionPointer(functionAddress, typeof (T));
		}
			
		// tracking what we loaded for easier cleanup
		static List<IntPtr> handles = new List<IntPtr>();

		public static void FreeLibraries()
		{
			foreach (var ptr in handles)
			{
				Debug.Log("Cleaning up module " + ptr);
				// todo: check if ptr was -1 or something bad like that...
				dl_native.FreeLibrary(ptr);
			}
		}

		////////////////////////////////////////////////////////////

	}


	class dl_native
	{
		
		public static IntPtr LoadLibrary(string fileName) {
			return dlopen(fileName, RTLD_NOW);
		}

		public static void FreeLibrary(IntPtr handle) {
			dlclose(handle);
		}

		public static IntPtr GetProcAddress(IntPtr dllHandle, string name) {
			// clear previous errors if any
			dlerror();
			var res = dlsym(dllHandle, name);
			var errPtr = dlerror();
			if (errPtr != IntPtr.Zero) {
				throw new Exception("dlsym: " + Marshal.PtrToStringAnsi(errPtr));
			}
			return res;
		}

		const int RTLD_NOW = 2;

		[DllImport("libdl.dylib")]
		private static extern IntPtr dlopen(String fileName, int flags);

		[DllImport("libdl.dylib")]
		private static extern IntPtr dlsym(IntPtr handle, String symbol);

		[DllImport("libdl.dylib")]
		private static extern int dlclose(IntPtr handle);

		[DllImport("libdl.dylib")]
		private static extern IntPtr dlerror();

	}

	       static List<string> temp_libs = new List<string>();
	       static string get_unique_library_instance(string orig_path)
		{
			var unique_path = Path.GetTempFileName() + "_" + Path.GetFileName(orig_path);

			File.Copy(orig_path, unique_path);

			Debug.Log("Copied " + orig_path + " to " + unique_path);
			temp_libs.Add(unique_path);

			return unique_path;
			// make sure we clean up later
		}

		void OnApplicationQuit()
		{
			foreach (var p in temp_libs)
			{
				Debug.Log("Cleaning up " + p);
				File.Delete(p);
			}

			FunctionLoader.FreeLibraries();

		}
	
            const string interface_lib = "path_to_your_dylib";

		private delegate void DebugCallback(string message);

		[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
		private delegate void RegisterDebugCallback_t(DebugCallback cb);
		static RegisterDebugCallback_t RegisterDebugCallback;

		[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
		private delegate void test_t();
		static test_t test;

		void Awake()
		{
			var libpath = get_unique_library_instance(interface_lib);
			test = (test_t)FunctionLoader.LoadFunction<test_t>(libpath, "test");
			RegisterDebugCallback = (RegisterDebugCallback_t)FunctionLoader.LoadFunction<RegisterDebugCallback_t>(libpath, "RegisterDebugCallback");

			Debug.Log("Loaded functions from " + libpath);
		}