@@ -48,6 +48,8 @@ public partial class SqliteConnection : DbConnection
48
48
private static readonly StateChangeEventArgs _fromClosedToOpenEventArgs = new StateChangeEventArgs ( ConnectionState . Closed , ConnectionState . Open ) ;
49
49
private static readonly StateChangeEventArgs _fromOpenToClosedEventArgs = new StateChangeEventArgs ( ConnectionState . Open , ConnectionState . Closed ) ;
50
50
51
+ private static string [ ] ? NativeDllSearchDirectories ;
52
+
51
53
static SqliteConnection ( )
52
54
{
53
55
Type . GetType ( "SQLitePCL.Batteries_V2, SQLitePCLRaw.batteries_v2" )
@@ -624,11 +626,71 @@ public virtual void LoadExtension(string file, string? proc = null)
624
626
625
627
private void LoadExtensionCore ( string file , string ? proc )
626
628
{
627
- var rc = sqlite3_load_extension ( Handle , utf8z . FromString ( file ) , utf8z . FromString ( proc ) , out var errmsg ) ;
628
- if ( rc != SQLITE_OK )
629
+ SqliteException ? firstException = null ;
630
+ foreach ( var path in GetLoadExtensionPaths ( file ) )
631
+ {
632
+ var rc = sqlite3_load_extension ( Handle , utf8z . FromString ( path ) , utf8z . FromString ( proc ) , out var errmsg ) ;
633
+ if ( rc == SQLITE_OK )
634
+ {
635
+ return ;
636
+ }
637
+
638
+ if ( firstException == null )
639
+ {
640
+ // We store the first exception so that error message looks more obvious if file appears in there
641
+ firstException = new SqliteException ( Resources . SqliteNativeError ( rc , errmsg . utf8_to_string ( ) ) , rc , rc ) ;
642
+ }
643
+ }
644
+
645
+ if ( firstException != null )
646
+ {
647
+ throw firstException ;
648
+ }
649
+ }
650
+
651
+ private static IEnumerable < string > GetLoadExtensionPaths ( string file )
652
+ {
653
+ // we always try original input first
654
+ yield return file ;
655
+
656
+ string ? dirName = Path . GetDirectoryName ( file ) ;
657
+
658
+ // we don't try to guess directories for user, if they pass a path either absolute or relative - they're on their own
659
+ if ( ! string . IsNullOrEmpty ( dirName ) )
660
+ {
661
+ yield break ;
662
+ }
663
+
664
+ bool shouldTryAddingLibPrefix = ! file . StartsWith ( "lib" , StringComparison . Ordinal ) && ! RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) ;
665
+
666
+ if ( shouldTryAddingLibPrefix )
667
+ {
668
+ yield return $ "lib{ file } ";
669
+ }
670
+
671
+ NativeDllSearchDirectories ??= GetNativeDllSearchDirectories ( ) ;
672
+
673
+ foreach ( string dir in NativeDllSearchDirectories )
674
+ {
675
+ yield return Path . Combine ( dir , file ) ;
676
+
677
+ if ( shouldTryAddingLibPrefix )
678
+ {
679
+ yield return Path . Combine ( dir , $ "lib{ file } ") ;
680
+ }
681
+ }
682
+ }
683
+
684
+ private static string [ ] GetNativeDllSearchDirectories ( )
685
+ {
686
+ string ? searchDirs = AppContext . GetData ( "NATIVE_DLL_SEARCH_DIRECTORIES" ) as string ;
687
+
688
+ if ( string . IsNullOrEmpty ( searchDirs ) )
629
689
{
630
- throw new SqliteException ( Resources . SqliteNativeError ( rc , errmsg . utf8_to_string ( ) ) , rc , rc ) ;
690
+ return [ ] ;
631
691
}
692
+
693
+ return searchDirs ! . Split ( [ Path . PathSeparator ] , StringSplitOptions . RemoveEmptyEntries ) ;
632
694
}
633
695
634
696
/// <summary>
0 commit comments