System.Data.SQLite
Check-in [c37b617ed1]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Enhancements for the native library pre-loader for .NET Core.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | netStandard20
Files: files | file ages | folders
SHA1:c37b617ed1ece85f5693d548c89bada2d7313a84
User & Date: mistachkin 2018-04-07 14:40:28
Context
2018-04-07
16:03
Fix harmless compiler warning. check-in: 49bbc3b912 user: mistachkin tags: netStandard20
14:40
Enhancements for the native library pre-loader for .NET Core. check-in: c37b617ed1 user: mistachkin tags: netStandard20
13:30
More work on the native library pre-loader for POSIX. check-in: c62df132f4 user: mistachkin tags: netStandard20
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to Doc/Extra/Provider/environment.html.

183
184
185
186
187
188
189






190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
          <td>No_SQLiteXmlConfigFile</td>
          <td>If this environment variable is set [to anything], calls to the
          GetSettingValue method will never result in the XML configuration
          file being read; instead, the default value will be returned.  This
          will effectively prevent any setting values specified via the XML
          configuration file from having any effect.</td>
        </tr>






        <tr valign="top">
          <td>PreLoadSQLite_BaseDirectory</td>
          <td>If this environment variable is set [to anything], it will be
          used instead of the application base directory by the native
          library pre-loader.  This environment variable can be especially
          useful in ASP.NET and other hosted environments where direct control
          of the location of the managed assemblies is not under the control
          of the application.</td>
        </tr>
        <tr valign="top">
          <td>PreLoadSQLite_BreakIntoDebugger</td>
          <td>If this configuration variable is set [to anything], the native
          library pre-loader subsystem will attempt to give the interactive
          user an opportunity to attach a debugger to the current process.</td>
        </tr>
        <tr valign="top">
          <td>PreLoadSQLite_LibraryFileNameOnly</td>
          <td>If this environment variable is set [to anything], it will be
          used as the base file name (without directory information) for the







>
>
>
>
>
>











|







183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
          <td>No_SQLiteXmlConfigFile</td>
          <td>If this environment variable is set [to anything], calls to the
          GetSettingValue method will never result in the XML configuration
          file being read; instead, the default value will be returned.  This
          will effectively prevent any setting values specified via the XML
          configuration file from having any effect.</td>
        </tr>
        <tr valign="top">
          <td>PreLoadSQLite_AllowBaseDirectoryOnly</td>
          <td>If this environment variable is set [to anything], the base
          directory itself is considered valid for pre-loading the native
          SQLite library.</td>
        </tr>
        <tr valign="top">
          <td>PreLoadSQLite_BaseDirectory</td>
          <td>If this environment variable is set [to anything], it will be
          used instead of the application base directory by the native
          library pre-loader.  This environment variable can be especially
          useful in ASP.NET and other hosted environments where direct control
          of the location of the managed assemblies is not under the control
          of the application.</td>
        </tr>
        <tr valign="top">
          <td>PreLoadSQLite_BreakIntoDebugger</td>
          <td>If this environment variable is set [to anything], the native
          library pre-loader subsystem will attempt to give the interactive
          user an opportunity to attach a debugger to the current process.</td>
        </tr>
        <tr valign="top">
          <td>PreLoadSQLite_LibraryFileNameOnly</td>
          <td>If this environment variable is set [to anything], it will be
          used as the base file name (without directory information) for the

Changes to System.Data.SQLite/Configurations/System.Data.SQLite.dll.config.

140
141
142
143
144
145
146









147
148
149
150
151
152
153
                      does not make any sense to use it here as it will never
                      be consulted.  For this setting to work properly, it must
                      be set via an environment variable.
    -->
    <!--
    <add key="No_SQLiteXmlConfigFile" value="1" />
    -->










    <!--
        NOTE: If this configuration variable is set [to anything], it will be
              used instead of the application base directory by the native
              library pre-loader.  This environment variable can be especially
              useful in ASP.NET and other hosted environments where direct
              control of the location of the managed assemblies is not under







>
>
>
>
>
>
>
>
>







140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
                      does not make any sense to use it here as it will never
                      be consulted.  For this setting to work properly, it must
                      be set via an environment variable.
    -->
    <!--
    <add key="No_SQLiteXmlConfigFile" value="1" />
    -->

    <!--
        NOTE: If this configuration variable is set [to anything], the base
              directory itself is considered valid for pre-loading the native
              SQLite library.
    -->
    <!--
    <add key="PreLoadSQLite_AllowBaseDirectoryOnly" value="1" />
    -->

    <!--
        NOTE: If this configuration variable is set [to anything], it will be
              used instead of the application base directory by the native
              library pre-loader.  This environment variable can be especially
              useful in ASP.NET and other hosted environments where direct
              control of the location of the managed assemblies is not under

Changes to System.Data.SQLite/System.Data.SQLite.NetStandard20.csproj.

53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
  </PropertyGroup>

  <!--
  ******************************************************************************
  **                    .NET Standard 2.0 Specific Targets                    **
  ******************************************************************************
  -->


  <Target Name="GenerateResourcesForNetStandard20"
          Condition="'$(GenerateResourcesForNetStandard20)' != 'false' And
                     '$(BuildTool)' == 'DotNetCore' And
                     '$(OperatingSystem)' == 'Windows' And
                     '$(MSBuildProjectDirectory)' != '' And
                     !HasTrailingSlash('$(MSBuildProjectDirectory)') And







<







53
54
55
56
57
58
59

60
61
62
63
64
65
66
  </PropertyGroup>

  <!--
  ******************************************************************************
  **                    .NET Standard 2.0 Specific Targets                    **
  ******************************************************************************
  -->


  <Target Name="GenerateResourcesForNetStandard20"
          Condition="'$(GenerateResourcesForNetStandard20)' != 'false' And
                     '$(BuildTool)' == 'DotNetCore' And
                     '$(OperatingSystem)' == 'Windows' And
                     '$(MSBuildProjectDirectory)' != '' And
                     !HasTrailingSlash('$(MSBuildProjectDirectory)') And

Changes to System.Data.SQLite/UnsafeNativeMethods.cs.

352
353
354
355
356
357
358
359

360
361
362
363
364
365
366
367
368
369
370






371
372
373
374
375









376
377
378
379
380
381
382
...
417
418
419
420
421
422
423































424
425
426
427
428
429
430
....
1482
1483
1484
1485
1486
1487
1488

1489
1490
1491
1492

1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
....
2533
2534
2535
2536
2537
2538
2539





2540
2541
2542
2543
2544
2545
2546
2547

2548
2549
2550
2551
2552
2553
2554
....
2570
2571
2572
2573
2574
2575
2576









2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
....
2619
2620
2621
2622
2623
2624
2625


2626
2627
2628
2629
2630
2631
2632
....
2906
2907
2908
2909
2910
2911
2912




2913
2914
2915
2916
2917
2918
2919
....
2923
2924
2925
2926
2927
2928
2929

2930
2931
2932
2933
2934
2935
2936
....
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
      };
      #endregion

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

      #region Private Data
      /// <summary>
      /// This lock is used to protect the static <see cref="isMono" /> field.

      /// </summary>
      private static readonly object staticSyncRoot = new object();

      /////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// This type is only present when running on Mono.
      /// </summary>
      private static readonly string MonoRuntimeType = "Mono.Runtime";

      /////////////////////////////////////////////////////////////////////////
      /// <summary>






      /// Keeps track of whether we are running on Mono.  Initially null, it is
      /// set by the <see cref="IsMono" /> method on its first call.  Later, it
      /// is returned verbatim by the <see cref="IsMono" /> method.
      /// </summary>
      private static bool? isMono = null;










      /////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// Keeps track of whether we successfully invoked the
      /// <see cref="Debugger.Break" /> method.  Initially null, it is set by
      /// the <see cref="MaybeBreakIntoDebugger" /> method on its first call.
      /// </summary>
................................................................................
              lock (staticSyncRoot)
              {
                  if (isMono == null)
                      isMono = (Type.GetType(MonoRuntimeType) != null);

                  return (bool)isMono;
              }































          }
          catch
          {
              // do nothing.
          }

          return false;
................................................................................
              //
              // BUGBUG: What about other application domains?
              //
              if (_SQLiteNativeModuleHandle == IntPtr.Zero)
              {
                  string baseDirectory = null;
                  string processorArchitecture = null;


                  /* IGNORED */
                  SearchForDirectory(
                      ref baseDirectory, ref processorArchitecture);


                  //
                  // NOTE: Attempt to pre-load the SQLite core library (or
                  //       interop assembly) and store both the file name
                  //       and native module handle for later usage.
                  //
                  /* IGNORED */
                  PreLoadSQLiteDll(
                      baseDirectory, processorArchitecture,
                      ref _SQLiteNativeModuleFileName,
                      ref _SQLiteNativeModuleHandle);
              }
#endif
#endif
          }
      }
................................................................................
      /// Upon success, this parameter will be modified to refer to the base
      /// directory containing the native SQLite library.
      /// </param>
      /// <param name="processorArchitecture">
      /// Upon success, this parameter will be modified to refer to the name
      /// of the immediate directory (i.e. the offset from the base directory)
      /// containing the native SQLite library.





      /// </param>
      /// <returns>
      /// Non-zero (success) if the native SQLite library was found; otherwise,
      /// zero (failure).
      /// </returns>
      private static bool SearchForDirectory(
          ref string baseDirectory,        /* out */
          ref string processorArchitecture /* out */

          )
      {
          if (GetSettingValue(
                "PreLoadSQLite_NoSearchForDirectory", null) != null)
          {
              return false; /* DISABLED */
          }
................................................................................
          //
          string[] directories = {
              GetAssemblyDirectory(),
#if !PLATFORM_COMPACTFRAMEWORK
              AppDomain.CurrentDomain.BaseDirectory,
#endif
          };










          string[] subDirectories = {
              GetProcessorArchitecture(), /* e.g. "x86" */
              GetPlatformName(null),      /* e.g. "Win32" */
#if NET_STANDARD_20 && !WINDOWS           // .NET Core on POSIX
              String.Empty,               /* e.g. base directory only */
#endif
          };

          foreach (string directory in directories)
          {
              if (directory == null)
                  continue;

................................................................................
                      {
                          // do nothing.
                      }
#endif

                      baseDirectory = directory;
                      processorArchitecture = subDirectory;


                      return true; /* FOUND */
                  }
              }
          }

          return false; /* NOT FOUND */
      }
................................................................................
      /// the current application domain).  This directory should contain the
      /// processor architecture specific sub-directories.
      /// </param>
      /// <param name="processorArchitecture">
      /// The requested processor architecture, null for default (the
      /// processor architecture of the current process).  This caller should
      /// almost always specify null for this parameter.




      /// </param>
      /// <param name="nativeModuleFileName">
      /// The candidate native module file name to load will be stored here,
      /// if necessary.
      /// </param>
      /// <param name="nativeModuleHandle">
      /// The native module handle as returned by LoadLibrary will be stored
................................................................................
      /// <returns>
      /// Non-zero if the native module was loaded successfully; otherwise,
      /// zero.
      /// </returns>
      private static bool PreLoadSQLiteDll(
          string baseDirectory,            /* in */
          string processorArchitecture,    /* in */

          ref string nativeModuleFileName, /* out */
          ref IntPtr nativeModuleHandle    /* out */
          )
      {
          //
          // NOTE: If the specified base directory is null, use the default
          //       (i.e. attempt to automatically detect it).
................................................................................
          //
          // NOTE: If the native SQLite library exists in the base directory
          //       itself, stop now.
          //
          string fileName = FixUpDllFileName(MaybeCombinePath(baseDirectory,
              fileNameOnly));

          if (File.Exists(fileName))
              return false;

          //
          // NOTE: If the specified processor architecture is null, use the
          //       default.
          //
          if (processorArchitecture == null)







|
>











>
>
>
>
>
>





>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>



|
>







|
|







 







>
>
>
>
>






|
|
>







 







>
>
>
>
>
>
>
>
>




|
<
<







 







>
>







 







>
>
>
>







 







>







 







|







352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
...
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
....
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
....
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
....
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645


2646
2647
2648
2649
2650
2651
2652
....
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
....
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
....
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
....
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
      };
      #endregion

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

      #region Private Data
      /// <summary>
      /// This lock is used to protect the static <see cref="isMono" /> and
      /// <see cref="isDotNetCore" /> field.
      /// </summary>
      private static readonly object staticSyncRoot = new object();

      /////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// This type is only present when running on Mono.
      /// </summary>
      private static readonly string MonoRuntimeType = "Mono.Runtime";

      /////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// This type is only present when running on .NET Core.
      /// </summary>
      private static readonly string DotNetCoreLibType = "System.CoreLib";

      /////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// Keeps track of whether we are running on Mono.  Initially null, it is
      /// set by the <see cref="IsMono" /> method on its first call.  Later, it
      /// is returned verbatim by the <see cref="IsMono" /> method.
      /// </summary>
      private static bool? isMono = null;

      /////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// Keeps track of whether we are running on .NET Core.  Initially null,
      /// it is set by the <see cref="IsDotNetCore" /> method on its first
      /// call.  Later, it is returned verbatim by the
      /// <see cref="IsDotNetCore" /> method.
      /// </summary>
      private static bool? isDotNetCore = null;

      /////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// Keeps track of whether we successfully invoked the
      /// <see cref="Debugger.Break" /> method.  Initially null, it is set by
      /// the <see cref="MaybeBreakIntoDebugger" /> method on its first call.
      /// </summary>
................................................................................
              lock (staticSyncRoot)
              {
                  if (isMono == null)
                      isMono = (Type.GetType(MonoRuntimeType) != null);

                  return (bool)isMono;
              }
          }
          catch
          {
              // do nothing.
          }

          return false;
      }

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

      /// <summary>
      /// Determines whether or not this assembly is running on .NET Core.
      /// </summary>
      /// <returns>
      /// Non-zero if this assembly is running on .NET Core.
      /// </returns>
      public static bool IsDotNetCore()
      {
          try
          {
              lock (staticSyncRoot)
              {
                  if (isDotNetCore == null)
                  {
                      isDotNetCore = (Type.GetType(
                          DotNetCoreLibType) != null);
                  }

                  return (bool)isDotNetCore;
              }
          }
          catch
          {
              // do nothing.
          }

          return false;
................................................................................
              //
              // BUGBUG: What about other application domains?
              //
              if (_SQLiteNativeModuleHandle == IntPtr.Zero)
              {
                  string baseDirectory = null;
                  string processorArchitecture = null;
                  bool allowBaseDirectoryOnly = false;

                  /* IGNORED */
                  SearchForDirectory(
                      ref baseDirectory, ref processorArchitecture,
                      ref allowBaseDirectoryOnly);

                  //
                  // NOTE: Attempt to pre-load the SQLite core library (or
                  //       interop assembly) and store both the file name
                  //       and native module handle for later usage.
                  //
                  /* IGNORED */
                  PreLoadSQLiteDll(baseDirectory,
                      processorArchitecture, allowBaseDirectoryOnly,
                      ref _SQLiteNativeModuleFileName,
                      ref _SQLiteNativeModuleHandle);
              }
#endif
#endif
          }
      }
................................................................................
      /// Upon success, this parameter will be modified to refer to the base
      /// directory containing the native SQLite library.
      /// </param>
      /// <param name="processorArchitecture">
      /// Upon success, this parameter will be modified to refer to the name
      /// of the immediate directory (i.e. the offset from the base directory)
      /// containing the native SQLite library.
      /// </param>
      /// <param name="allowBaseDirectoryOnly">
      /// Upon success, this parameter will be modified to non-zero only if
      /// the base directory itself should be allowed for loading the native
      /// library.
      /// </param>
      /// <returns>
      /// Non-zero (success) if the native SQLite library was found; otherwise,
      /// zero (failure).
      /// </returns>
      private static bool SearchForDirectory(
          ref string baseDirectory,         /* out */
          ref string processorArchitecture, /* out */
          ref bool allowBaseDirectoryOnly   /* out */
          )
      {
          if (GetSettingValue(
                "PreLoadSQLite_NoSearchForDirectory", null) != null)
          {
              return false; /* DISABLED */
          }
................................................................................
          //
          string[] directories = {
              GetAssemblyDirectory(),
#if !PLATFORM_COMPACTFRAMEWORK
              AppDomain.CurrentDomain.BaseDirectory,
#endif
          };

          string extraSubDirectory = null;

          if ((GetSettingValue(
                  "PreLoadSQLite_AllowBaseDirectoryOnly", null) != null) ||
              (HelperMethods.IsDotNetCore() && !HelperMethods.IsWindows()))
          {
              extraSubDirectory = String.Empty; /* .NET Core on POSIX */
          }

          string[] subDirectories = {
              GetProcessorArchitecture(), /* e.g. "x86" */
              GetPlatformName(null),      /* e.g. "Win32" */
              extraSubDirectory           /* base directory only? */


          };

          foreach (string directory in directories)
          {
              if (directory == null)
                  continue;

................................................................................
                      {
                          // do nothing.
                      }
#endif

                      baseDirectory = directory;
                      processorArchitecture = subDirectory;
                      allowBaseDirectoryOnly = (subDirectory.Length == 0);

                      return true; /* FOUND */
                  }
              }
          }

          return false; /* NOT FOUND */
      }
................................................................................
      /// the current application domain).  This directory should contain the
      /// processor architecture specific sub-directories.
      /// </param>
      /// <param name="processorArchitecture">
      /// The requested processor architecture, null for default (the
      /// processor architecture of the current process).  This caller should
      /// almost always specify null for this parameter.
      /// </param>
      /// <param name="allowBaseDirectoryOnly">
      /// Non-zero indicates that the native SQLite library can be loaded
      /// from the base directory itself.
      /// </param>
      /// <param name="nativeModuleFileName">
      /// The candidate native module file name to load will be stored here,
      /// if necessary.
      /// </param>
      /// <param name="nativeModuleHandle">
      /// The native module handle as returned by LoadLibrary will be stored
................................................................................
      /// <returns>
      /// Non-zero if the native module was loaded successfully; otherwise,
      /// zero.
      /// </returns>
      private static bool PreLoadSQLiteDll(
          string baseDirectory,            /* in */
          string processorArchitecture,    /* in */
          bool allowBaseDirectoryOnly,     /* in */
          ref string nativeModuleFileName, /* out */
          ref IntPtr nativeModuleHandle    /* out */
          )
      {
          //
          // NOTE: If the specified base directory is null, use the default
          //       (i.e. attempt to automatically detect it).
................................................................................
          //
          // NOTE: If the native SQLite library exists in the base directory
          //       itself, stop now.
          //
          string fileName = FixUpDllFileName(MaybeCombinePath(baseDirectory,
              fileNameOnly));

          if (!allowBaseDirectoryOnly && File.Exists(fileName))
              return false;

          //
          // NOTE: If the specified processor architecture is null, use the
          //       default.
          //
          if (processorArchitecture == null)