Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hot Reloading and NativeLibrary.Load(String, Assembly, DllImportSearchPath?) #148

Closed
KatoStoelen opened this issue Jul 15, 2020 · 3 comments
Labels
bug Something isn't working

Comments

@KatoStoelen
Copy link
Contributor

KatoStoelen commented Jul 15, 2020

Describe the bug
Not sure if this is a bug or something this library cannot support. I was trying to reproduce #146 using the hot-reload sample. There we use Microsoft.Data.Sqlite to test shadow copying and loading of sni.dll. When expandig the sample by creating an instance of SqliteConnection, there is a lot more happening under the hood. Particulary in SQLitePCL.raw which uses system.runtime.interopservices.nativelibrary.load to load native libraries. This API does not invoke AssemblyLoadContext.LoadUnmanagedDll. Hence, this library does not control how those native libraries are loaded, and cannot perform any shadow copying etc. That is, if my understanding is correct.

To Reproduce
Steps to reproduce the behavior:

  1. Add using var dbConnection = new SqliteConnection("Data Source=db.sqlite"); to the InfoDisplayer in the hot-reload sample
  2. Run samples/hot-reload/run.ps1|sh
  3. See error System.DllNotFoundException: Unable to load DLL 'e_sqlite3' or one of its dependencies: The specified module could not be found. (0x8007007E)

Expected behavior
Would be nice if there is a solution to this issue, but I can understand that there might not be.

Additional context
Full stack trace:

Unhandled exception. System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
 ---> System.TypeInitializationException: The type initializer for 'Microsoft.Data.Sqlite.SqliteConnection' threw an exception.
 ---> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
 ---> System.DllNotFoundException: Unable to load DLL 'e_sqlite3' or one of its dependencies: The specified module could not be found. (0x8007007E)
   at System.Runtime.InteropServices.NativeLibrary.LoadByName(String libraryName, QCallAssembly callingAssembly, Boolean hasDllImportSearchPathFlag, UInt32 dllImportSearchPathFlag, Boolean throwOnError)
   at System.Runtime.InteropServices.NativeLibrary.LoadLibraryByName(String libraryName, Assembly assembly, Nullable`1 searchPath, Boolean throwOnError)
   at System.Runtime.InteropServices.NativeLibrary.Load(String libraryName, Assembly assembly, Nullable`1 searchPath)
   at SQLitePCL.Batteries_V2.MakeDynamic(String name, Int32 flags)
   at SQLitePCL.Batteries_V2.DoDynamic_cdecl(String name, Int32 flags)
   --- End of inner exception stack trace ---
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Microsoft.Data.Sqlite.Utilities.BundleInitializer.Initialize()
   at Microsoft.Data.Sqlite.SqliteConnection..cctor()
   --- End of inner exception stack trace ---
   at Microsoft.Data.Sqlite.SqliteConnection..ctor(String connectionString)
   at TimestampedPlugin.InfoDisplayer.Print()
   --- End of inner exception stack trace ---
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
   at HostApp.Program.InvokePlugin(PluginLoader loader) in C:\Source\private\DotNetCorePlugins\samples\hot-reload\HotReloadApp\Program.cs:line 39
   at HostApp.Program.Main(String[] args) in C:\Source\private\DotNetCorePlugins\samples\hot-reload\HotReloadApp\Program.cs:line 22
   at HostApp.Program.<Main>(String[] args)
@KatoStoelen KatoStoelen added the bug Something isn't working label Jul 15, 2020
@KatoStoelen
Copy link
Contributor Author

Well, what did work in this specific scenario was to explicitly load e_sqlite3.dll by calling LoadUnmanagedDll("e_sqlite3") during initialization of ManagedLoadContext. Not exactly sure why that worked. Probably due to my limited knowledge of how loading of managed/unmanaged DLLs actually works.

My first thought was that e_sqlite3.dll could be found if it existed within the shadow copy directory. However, I've been unable to confirm it.

Anyways. It seems like this would be solvable with an API to "pre-load" specific unmanaged DLLs. ManagedLoadContext would then probably have to include some clean-up logic to free the pre-loaded handles.

KatoStoelen added a commit to KatoStoelen/DotNetCorePlugins that referenced this issue Jul 15, 2020
Unmanaged DLLs that are loaded outide the assembly load context are not
found when hot reloading (load in memory) is enabled. This happens when
NativeLibrary.Load(String, Assembly, DllImportSearchPath?) is used.

This change adds an API that lets consumers of this package configure a
set of unmanaged libraries to preload before any other resolving
happens.

Fixes natemcmaster#148
@KatoStoelen KatoStoelen changed the title Hot Reloading and System.Runtime.InteropServices.NativeLibrary Hot Reloading and NativeLibrary.Load(String, Assembly, DllImportSearchPath?) Jul 16, 2020
@KatoStoelen
Copy link
Contributor Author

KatoStoelen commented Jul 21, 2020

The public API suggestion above was abandoned. Looking for solutions where the host does not have to care about the dependencies of plugins, instead.

NativeLibrary.SetDllImportResolver(Assembly, DllImportResolver) looks promising. However, havn't been able to solve the SQLite scenario using it yet. None of the managed callbacks are invoked...

On a positive note, seems like the behavior of NativeLibrary.Load(String, Assembly, DllImportSearchPath?) is changed in .NET 5 (dotnet/runtime#13819), and will call AssemblyLoadContext.LoadUnmanagedDll(String) going forward.

@KatoStoelen
Copy link
Contributor Author

Closing this. Not possible to fix for .NET Core.

Also, this issue is a duplicate of #84 (sorry about that).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant