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:
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
Answer by efge · Jan 16, 2016 at 12:34 PM
There is a (Windows only) blog post for a work-around: http://runningdimensions.com/blog/?p=5
Yeah that was the best solution I could think of. Was hoping somebody might have hit upon an approach that didn't require different code paths for when you were modifying the plugin vs just building the game, but I can fully believe there isn't one.
Answer by lazer · Dec 24, 2016 at 11:53 PM
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);
}
I'v noticed that, when application quit, you'v free the loaded library. But when i use lsof or the system monitor to view opened files, the loaded library still there. Have you meet this issue? If yes, how did you solved it?
Your answer
Follow this Question
Related Questions
Calling functions from Native DLL (C++) inconsistently crashes Unity 1 Answer
Creating a native UAV into a meshes native vertex buffer (DX11) 1 Answer
Send Desktop Screencapture to Unity Plugin 0 Answers
Native DLL saying FileNotFoundException 2 Answers
Editor freezing with multithreaded DLL native plugin in Release but not in Debug 2 Answers