diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
index 6246fc590d..7f30cb6ab3 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -624,7 +624,6 @@
-
@@ -779,6 +778,9 @@
Microsoft\Data\Sql\SqlDataSourceEnumerator.Windows.cs
+
+ Microsoft\Data\SqlClient\LocalDBAPI.Windows.cs
+
Microsoft\Data\SqlClient\SqlColumnEncryptionCngProvider.Windows.cs
@@ -797,9 +799,7 @@
Microsoft\Data\SqlTypes\SqlFileStream.Windows.cs
-
-
-
+
@@ -816,9 +816,11 @@
Microsoft\Data\ProviderBase\DbConnectionPoolIdentity.Unix.cs
+
+ Microsoft\Data\SqlClient\LocalDBAPI.Unix.cs
+
-
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Common.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Common.cs
deleted file mode 100644
index 616aad0ec1..0000000000
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Common.cs
+++ /dev/null
@@ -1,124 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Diagnostics;
-using System.Globalization;
-using System.Runtime.InteropServices;
-using System.Text;
-using Microsoft.Data.SqlClient;
-
-namespace Microsoft.Data
-{
- internal static partial class LocalDBAPI
- {
- private static LocalDBFormatMessageDelegate s_localDBFormatMessage = null;
-
- internal static void ReleaseDLLHandles()
- {
- s_userInstanceDLLHandle = IntPtr.Zero;
- s_localDBFormatMessage = null;
- }
-
-
- private static LocalDBFormatMessageDelegate LocalDBFormatMessage
- {
- get
- {
- if (s_localDBFormatMessage == null)
- {
- lock (s_dllLock)
- {
- if (s_localDBFormatMessage == null)
- {
- IntPtr functionAddr = LoadProcAddress();
-
- if (functionAddr == IntPtr.Zero)
- {
- // SNI checks for LocalDBFormatMessage during DLL loading, so it is practically impossible to get this error.
- int hResult = Marshal.GetLastWin32Error();
- SqlClientEventSource.Log.TryTraceEvent("LocalDBAPI.LocalDBFormatMessage> GetProcAddress for LocalDBFormatMessage error 0x{0}", hResult);
- throw CreateLocalDBException(errorMessage: Strings.LocalDB_MethodNotFound);
- }
- s_localDBFormatMessage = Marshal.GetDelegateForFunctionPointer(functionAddr);
- }
- }
- }
- return s_localDBFormatMessage;
- }
- }
-
- //This is copy of handle that SNI maintains, so we are responsible for freeing it - therefore there we are not using SafeHandle
- private static IntPtr s_userInstanceDLLHandle = IntPtr.Zero;
-
- private static readonly object s_dllLock = new object();
-
-
- private const uint const_LOCALDB_TRUNCATE_ERR_MESSAGE = 1;// flag for LocalDBFormatMessage that indicates that message can be truncated if it does not fit in the buffer
- private const int const_ErrorMessageBufferSize = 1024; // Buffer size for Local DB error message 1K will be enough for all messages
-
-
- internal static string GetLocalDBMessage(int hrCode)
- {
- Debug.Assert(hrCode < 0, "HRCode does not indicate error");
- try
- {
- StringBuilder buffer = new StringBuilder((int)const_ErrorMessageBufferSize);
- uint len = (uint)buffer.Capacity;
-
-
- // First try for current culture
- int hResult = LocalDBFormatMessage(hrLocalDB: hrCode, dwFlags: const_LOCALDB_TRUNCATE_ERR_MESSAGE, dwLanguageId: (uint)CultureInfo.CurrentCulture.LCID,
- buffer: buffer, buflen: ref len);
- if (hResult >= 0)
- return buffer.ToString();
- else
- {
- // Message is not available for current culture, try default
- buffer = new StringBuilder((int)const_ErrorMessageBufferSize);
- len = (uint)buffer.Capacity;
- hResult = LocalDBFormatMessage(hrLocalDB: hrCode, dwFlags: const_LOCALDB_TRUNCATE_ERR_MESSAGE, dwLanguageId: 0 /* thread locale with fallback to English */,
- buffer: buffer, buflen: ref len);
- if (hResult >= 0)
- return buffer.ToString();
- else
- return string.Format(CultureInfo.CurrentCulture, "{0} (0x{1:X}).", Strings.LocalDB_UnobtainableMessage, hResult);
- }
- }
- catch (SqlException exc)
- {
- return string.Format(CultureInfo.CurrentCulture, "{0} ({1}).", Strings.LocalDB_UnobtainableMessage, exc.Message);
- }
- }
-
-
- private static SqlException CreateLocalDBException(string errorMessage, uint sniError = 0)
- {
- Debug.Assert(!string.IsNullOrEmpty(errorMessage), "Error message should not be null or empty");
-
- if (sniError != 0)
- {
- string sniErrorMessage = SQL.GetSNIErrorMessage(sniError);
- errorMessage = $"{errorMessage} (error: {sniError} - {sniErrorMessage})";
- }
-
- SqlErrorCollection collection = new SqlErrorCollection
- {
- new SqlError(
- infoNumber: (int)sniError,
- errorState: 0,
- errorClass: TdsEnums.FATAL_ERROR_CLASS,
- server: null,
- errorMessage,
- procedure: null,
- lineNumber: 0)
- };
-
- SqlException exc = SqlException.CreateException(collection, serverVersion: null);
- exc._doNotReconnect = true;
-
- return exc;
- }
- }
-}
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Windows.cs
deleted file mode 100644
index e6b9a685dc..0000000000
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Windows.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using Interop.Windows.Kernel32;
-using Microsoft.Data.SqlClient;
-using Interop.Windows.Sni;
-
-namespace Microsoft.Data
-{
- internal static partial class LocalDBAPI
- {
- private static IntPtr LoadProcAddress() =>
- Kernel32.GetProcAddress(UserInstanceDLLHandle, "LocalDBFormatMessage");
-
- private static IntPtr UserInstanceDLLHandle
- {
- get
- {
- if (s_userInstanceDLLHandle == IntPtr.Zero)
- {
- lock (s_dllLock)
- {
- if (s_userInstanceDLLHandle == IntPtr.Zero)
- {
- SniNativeWrapper.SNIQueryInfo(QueryType.SNI_QUERY_LOCALDB_HMODULE, ref s_userInstanceDLLHandle);
- if (s_userInstanceDLLHandle != IntPtr.Zero)
- {
- SqlClientEventSource.Log.TryTraceEvent("LocalDBAPI.UserInstanceDLLHandle | LocalDB - handle obtained");
- }
- else
- {
- SniNativeWrapper.SNIGetLastError(out SniError sniError);
- throw CreateLocalDBException(errorMessage: StringsHelper.GetString("LocalDB_FailedGetDLLHandle"), sniError: sniError.sniError);
- }
- }
- }
- }
- return s_userInstanceDLLHandle;
- }
- }
- }
-}
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.cs
deleted file mode 100644
index ba2371c232..0000000000
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Runtime.InteropServices;
-using System.Text;
-
-namespace Microsoft.Data
-{
- internal static partial class LocalDBAPI
- {
- private const string LocalDbPrefix = @"(localdb)\";
- private const string LocalDbPrefix_NP = @"np:\\.\pipe\LOCALDB#";
-
-
- [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
- private delegate int LocalDBFormatMessageDelegate(int hrLocalDB, uint dwFlags, uint dwLanguageId, StringBuilder buffer, ref uint buflen);
-
- // check if name is in format (localdb)\ and return instance name if it is
- // localDB can also have a format of np:\\.\pipe\LOCALDB#\tsql\query
- internal static string GetLocalDbInstanceNameFromServerName(string serverName)
- {
- if (serverName is not null)
- {
- // it can start with spaces if specified in quotes
- // Memory allocation is reduced by using ReadOnlySpan
- ReadOnlySpan input = serverName.AsSpan().Trim();
- if (input.StartsWith(LocalDbPrefix.AsSpan(), StringComparison.OrdinalIgnoreCase))
- {
- input = input.Slice(LocalDbPrefix.Length);
- if (!input.IsEmpty)
- {
- return input.ToString();
- }
- }
- else if (input.StartsWith(LocalDbPrefix_NP.AsSpan(), StringComparison.OrdinalIgnoreCase))
- {
- return input.ToString();
- }
-
- }
- return null;
- }
- }
-}
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
index 591977ef2d..bc05538a37 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
@@ -298,12 +298,30 @@
Microsoft\Data\ProviderBase\DbMetaDataFactory.cs
+
+ Microsoft\Data\ProviderBase\DbReferenceCollection.cs
+
Microsoft\Data\ProviderBase\FieldNameLookup.cs
Microsoft\Data\ProviderBase\TimeoutTimer.cs
+
+ Microsoft\Data\Sql\SqlDataSourceEnumerator.Windows.cs
+
+
+ Microsoft\Data\Sql\SqlDataSourceEnumeratorNativeHelper.cs
+
+
+ Microsoft\Data\Sql\SqlDataSourceEnumeratorUtil.cs
+
+
+ Microsoft\Data\Sql\SqlNotificationRequest.cs
+
+
+ Resources\ResCategoryAttribute.cs
+
Microsoft\Data\SqlClient\SSPI\NativeSSPIContextProvider.cs
@@ -319,18 +337,6 @@
Microsoft\Data\Sql\SqlDataSourceEnumerator.cs
-
- Microsoft\Data\Sql\SqlDataSourceEnumerator.Windows.cs
-
-
- Microsoft\Data\Sql\SqlDataSourceEnumeratorNativeHelper.cs
-
-
- Microsoft\Data\Sql\SqlDataSourceEnumeratorUtil.cs
-
-
- Microsoft\Data\Sql\SqlNotificationRequest.cs
-
Microsoft\Data\SqlClient\AAsyncCallContext.cs
@@ -382,6 +388,9 @@
Microsoft\Data\SqlClient\EnclaveSessionCache.cs
+
+ Microsoft\Data\SqlClient\LocalDBAPI.Windows.cs
+
Microsoft\Data\SqlClient\LocalAppContextSwitches.cs
@@ -793,6 +802,9 @@
Microsoft\Data\SqlClient\VirtualSecureModeEnclaveProviderBase.cs
+
+ Microsoft\Data\SqlDbTypeExtensions.cs
+
Microsoft\Data\SqlTypes\SqlFileStream.cs
@@ -805,18 +817,9 @@
Microsoft\Data\SqlTypes\SqlJson.cs
-
- Resources\ResCategoryAttribute.cs
-
Resources\ResDescriptionAttribute.cs
-
- Microsoft\Data\ProviderBase\DbReferenceCollection.cs
-
-
- Microsoft\Data\SqlDbTypeExtensions.cs
-
System\IO\StreamExtensions.netfx.cs
@@ -827,7 +830,6 @@
-
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj
index c092b35a98..559b61c8af 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj
@@ -6,6 +6,7 @@
+
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Unix.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalDBAPI.Unix.cs
similarity index 82%
rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Unix.cs
rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalDBAPI.Unix.cs
index ff85256c56..7555a80d32 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Unix.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalDBAPI.Unix.cs
@@ -8,6 +8,9 @@ namespace Microsoft.Data
{
internal static partial class LocalDBAPI
{
+ internal static string GetLocalDbInstanceNameFromServerName(string serverName) =>
+ null;
+
internal static string GetLocalDBMessage(int hrCode) =>
throw new PlatformNotSupportedException(Strings.LocalDBNotSupported); // LocalDB is not available for Unix and hence it cannot be supported.
}
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/LocalDBAPI.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalDBAPI.Windows.cs
similarity index 74%
rename from src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/LocalDBAPI.cs
rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalDBAPI.Windows.cs
index 2d6192f6bf..77194052da 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/LocalDBAPI.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalDBAPI.Windows.cs
@@ -3,300 +3,167 @@
// See the LICENSE file in the project root for more information.
using System;
-using System.Collections.Generic;
-using System.Configuration;
using System.Diagnostics;
using System.Globalization;
-using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
-using System.Threading;
-using Interop.Windows.Kernel32;
using Interop.Windows.Sni;
using Microsoft.Data.SqlClient;
+using Interop.Windows.Kernel32;
+
+#if NETFRAMEWORK
+using System.Collections.Generic;
+using System.Configuration;
+using System.Runtime.CompilerServices;
+using System.Threading;
+#endif
namespace Microsoft.Data
{
internal static class LocalDBAPI
{
+ private const int const_ErrorMessageBufferSize = 1024; // Buffer size for Local DB error message 1K will be enough for all messages
+ private const uint const_LOCALDB_TRUNCATE_ERR_MESSAGE = 1;// flag for LocalDBFormatMessage that indicates that message can be truncated if it does not fit in the buffer
private const string LocalDbPrefix = @"(localdb)\";
private const string LocalDbPrefix_NP = @"np:\\.\pipe\LOCALDB#";
- const string Const_partialTrustFlagKey = "ALLOW_LOCALDB_IN_PARTIAL_TRUST";
-
- static PermissionSet _fullTrust = null;
- static bool _partialTrustFlagChecked = false;
- static bool _partialTrustAllowed = false;
-
-
- // check if name is in format (localdb)\ and return instance name if it is
- internal static string GetLocalDbInstanceNameFromServerName(string serverName)
- {
- if (serverName is not null)
- {
- // it can start with spaces if specified in quotes
- // Memory allocation is reduced by using ReadOnlySpan
- ReadOnlySpan input = serverName.AsSpan().Trim();
- if (input.StartsWith(LocalDbPrefix.AsSpan(), StringComparison.OrdinalIgnoreCase))
- {
- input = input.Slice(LocalDbPrefix.Length);
- if (!input.IsEmpty)
- {
- return input.ToString();
- }
- }
- else if (input.StartsWith(LocalDbPrefix_NP.AsSpan(), StringComparison.OrdinalIgnoreCase))
- {
- return input.ToString();
- }
-
- }
- return null;
- }
-
- internal static void ReleaseDLLHandles()
- {
- s_userInstanceDLLHandle = IntPtr.Zero;
- s_localDBFormatMessage = null;
- s_localDBCreateInstance = null;
- }
-
-
-
+
+ #if NETFRAMEWORK
+ private const string Const_partialTrustFlagKey = "ALLOW_LOCALDB_IN_PARTIAL_TRUST";
+ #endif
+
+ private static readonly object s_dllLock = new object();
+
+ #if NETFRAMEWORK
+ private static readonly object s_configLock = new object();
+ #endif
+
+ private static LocalDBFormatMessageDelegate s_localDBFormatMessage = null;
//This is copy of handle that SNI maintains, so we are responsible for freeing it - therefore there we are not using SafeHandle
- static IntPtr s_userInstanceDLLHandle = IntPtr.Zero;
-
- static object s_dllLock = new object();
-
- static IntPtr UserInstanceDLLHandle
- {
- get
- {
- if (s_userInstanceDLLHandle == IntPtr.Zero)
- {
- bool lockTaken = false;
- RuntimeHelpers.PrepareConstrainedRegions();
- try
- {
- Monitor.Enter(s_dllLock, ref lockTaken);
- if (s_userInstanceDLLHandle == IntPtr.Zero)
- {
- SniNativeWrapper.SNIQueryInfo(QueryType.SNI_QUERY_LOCALDB_HMODULE, ref s_userInstanceDLLHandle);
- if (s_userInstanceDLLHandle != IntPtr.Zero)
- {
- SqlClientEventSource.Log.TryTraceEvent(" LocalDB - handle obtained");
- }
- else
- {
- SniError sniError = new SniError();
- SniNativeWrapper.SNIGetLastError(out sniError);
- throw CreateLocalDBException(errorMessage: StringsHelper.GetString("LocalDB_FailedGetDLLHandle"), sniError: sniError.sniError);
- }
- }
- }
- finally
- {
- if (lockTaken)
- Monitor.Exit(s_dllLock);
- }
- }
- return s_userInstanceDLLHandle;
- }
- }
-
+ private static IntPtr s_userInstanceDLLHandle = IntPtr.Zero;
+
+ #if NETFRAMEWORK
+ private static PermissionSet _fullTrust = null;
+ private static bool _partialTrustFlagChecked = false;
+ private static bool _partialTrustAllowed = false;
+ private static Dictionary s_configurableInstances = null;
+ private static LocalDBCreateInstanceDelegate s_localDBCreateInstance = null;
+ #endif
+
+ #if NETFRAMEWORK
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int LocalDBCreateInstanceDelegate([MarshalAs(UnmanagedType.LPWStr)] string version, [MarshalAs(UnmanagedType.LPWStr)] string instance, UInt32 flags);
-
- static LocalDBCreateInstanceDelegate s_localDBCreateInstance = null;
-
- static LocalDBCreateInstanceDelegate LocalDBCreateInstance
+ #endif
+
+ [SuppressUnmanagedCodeSecurity]
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+ private delegate int LocalDBFormatMessageDelegate(int hrLocalDB, uint dwFlags, uint dwLanguageId, StringBuilder buffer, ref uint buflen);
+
+ #if NETFRAMEWORK
+ private static LocalDBCreateInstanceDelegate LocalDBCreateInstance
{
get
{
if (s_localDBCreateInstance == null)
{
- bool lockTaken = false;
RuntimeHelpers.PrepareConstrainedRegions();
- try
+
+ lock (s_dllLock)
{
- Monitor.Enter(s_dllLock, ref lockTaken);
if (s_localDBCreateInstance == null)
{
- IntPtr functionAddr = Kernel32Safe.GetProcAddress(UserInstanceDLLHandle, "LocalDBCreateInstance");
-
+ IntPtr functionAddr = LoadProcAddress("LocalDBCreateInstance");
if (functionAddr == IntPtr.Zero)
{
int hResult = Marshal.GetLastWin32Error();
SqlClientEventSource.Log.TryTraceEvent(" GetProcAddress for LocalDBCreateInstance error 0x{0}", hResult);
throw CreateLocalDBException(errorMessage: StringsHelper.GetString("LocalDB_MethodNotFound"));
}
+
s_localDBCreateInstance = (LocalDBCreateInstanceDelegate)Marshal.GetDelegateForFunctionPointer(functionAddr, typeof(LocalDBCreateInstanceDelegate));
}
}
- finally
- {
- if (lockTaken)
- Monitor.Exit(s_dllLock);
- }
}
return s_localDBCreateInstance;
}
}
-
-
- [SuppressUnmanagedCodeSecurity]
- [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
- private delegate int LocalDBFormatMessageDelegate(int hrLocalDB, UInt32 dwFlags, UInt32 dwLanguageId, StringBuilder buffer, ref UInt32 buflen);
-
- static LocalDBFormatMessageDelegate s_localDBFormatMessage = null;
-
- static LocalDBFormatMessageDelegate LocalDBFormatMessage
+ #endif
+
+ private static LocalDBFormatMessageDelegate LocalDBFormatMessage
{
get
{
if (s_localDBFormatMessage == null)
{
- bool lockTaken = false;
+ #if NETFRAMEWORK
RuntimeHelpers.PrepareConstrainedRegions();
- try
+ #endif
+
+ lock (s_dllLock)
{
- Monitor.Enter(s_dllLock, ref lockTaken);
if (s_localDBFormatMessage == null)
{
- IntPtr functionAddr = Kernel32Safe.GetProcAddress(UserInstanceDLLHandle, "LocalDBFormatMessage");
-
+ IntPtr functionAddr = LoadProcAddress("LocalDBFormatMessage");
if (functionAddr == IntPtr.Zero)
{
// SNI checks for LocalDBFormatMessage during DLL loading, so it is practically impossible to get this error.
int hResult = Marshal.GetLastWin32Error();
- SqlClientEventSource.Log.TryTraceEvent(" GetProcAddress for LocalDBFormatMessage error 0x{0}", hResult);
- throw CreateLocalDBException(errorMessage: StringsHelper.GetString("LocalDB_MethodNotFound"));
+ SqlClientEventSource.Log.TryTraceEvent("LocalDBAPI.LocalDBFormatMessage> GetProcAddress for LocalDBFormatMessage error 0x{0}", hResult);
+ throw CreateLocalDBException(errorMessage: Strings.LocalDB_MethodNotFound);
}
- s_localDBFormatMessage = (LocalDBFormatMessageDelegate)Marshal.GetDelegateForFunctionPointer(functionAddr, typeof(LocalDBFormatMessageDelegate));
+
+ s_localDBFormatMessage = Marshal.GetDelegateForFunctionPointer(functionAddr);
}
}
- finally
- {
- if (lockTaken)
- Monitor.Exit(s_dllLock);
- }
}
return s_localDBFormatMessage;
}
}
-
- const UInt32 const_LOCALDB_TRUNCATE_ERR_MESSAGE = 1;// flag for LocalDBFormatMessage that indicates that message can be truncated if it does not fit in the buffer
- const int const_ErrorMessageBufferSize = 1024; // Buffer size for Local DB error message, according to Serverless team, 1K will be enough for all messages
-
-
- internal static string GetLocalDBMessage(int hrCode)
- {
- Debug.Assert(hrCode < 0, "HRCode does not indicate error");
- try
- {
- StringBuilder buffer = new StringBuilder((int)const_ErrorMessageBufferSize);
- UInt32 len = (UInt32)buffer.Capacity;
-
-
- // First try for current culture
- int hResult = LocalDBFormatMessage(hrLocalDB: hrCode, dwFlags: const_LOCALDB_TRUNCATE_ERR_MESSAGE, dwLanguageId: (UInt32)CultureInfo.CurrentCulture.LCID,
- buffer: buffer, buflen: ref len);
- if (hResult >= 0)
- return buffer.ToString();
- else
- {
- // Message is not available for current culture, try default
- buffer = new StringBuilder((int)const_ErrorMessageBufferSize);
- len = (UInt32)buffer.Capacity;
- hResult = LocalDBFormatMessage(hrLocalDB: hrCode, dwFlags: const_LOCALDB_TRUNCATE_ERR_MESSAGE, dwLanguageId: 0 /* thread locale with fallback to English */,
- buffer: buffer, buflen: ref len);
- if (hResult >= 0)
- return buffer.ToString();
- else
- return string.Format(CultureInfo.CurrentCulture, "{0} (0x{1:X}).", StringsHelper.GetString("LocalDB_UnobtainableMessage"), hResult);
- }
- }
- catch (SqlException exc)
- {
- return string.Format(CultureInfo.CurrentCulture, "{0} ({1}).", StringsHelper.GetString("LocalDB_UnobtainableMessage"), exc.Message);
- }
- }
-
-
- static SqlException CreateLocalDBException(string errorMessage, string instance = null, int localDbError = 0, uint sniError = 0)
- {
- Debug.Assert((localDbError == 0) || (sniError == 0), "LocalDB error and SNI error cannot be specified simultaneously");
- Debug.Assert(!string.IsNullOrEmpty(errorMessage), "Error message should not be null or empty");
- SqlErrorCollection collection = new SqlErrorCollection();
-
- int errorCode = (localDbError == 0) ? (int)sniError : localDbError;
-
- if (sniError != 0)
- {
- string sniErrorMessage = SQL.GetSNIErrorMessage(sniError);
- errorMessage = String.Format((IFormatProvider)null, "{0} (error: {1} - {2})",
- errorMessage, sniError, sniErrorMessage);
- }
-
- collection.Add(new SqlError(errorCode, 0, TdsEnums.FATAL_ERROR_CLASS, instance, errorMessage, null, 0));
-
- if (localDbError != 0)
- collection.Add(new SqlError(errorCode, 0, TdsEnums.FATAL_ERROR_CLASS, instance, GetLocalDBMessage(localDbError), null, 0));
-
- SqlException exc = SqlException.CreateException(collection, null);
-
- exc._doNotReconnect = true;
-
- return exc;
- }
-
- private class InstanceInfo
- {
- internal InstanceInfo(string version)
- {
- this.version = version;
- this.created = false;
- }
-
- internal readonly string version;
- internal bool created;
- }
-
- static object s_configLock = new object();
- static Dictionary s_configurableInstances = null;
-
- internal static void DemandLocalDBPermissions()
+
+ private static IntPtr UserInstanceDLLHandle
{
- if (!_partialTrustAllowed)
+ get
{
- if (!_partialTrustFlagChecked)
+ if (s_userInstanceDLLHandle == IntPtr.Zero)
{
- object partialTrustFlagValue = AppDomain.CurrentDomain.GetData(Const_partialTrustFlagKey);
- if (partialTrustFlagValue != null && partialTrustFlagValue is bool)
- {
- _partialTrustAllowed = (bool)partialTrustFlagValue;
- }
- _partialTrustFlagChecked = true;
- if (_partialTrustAllowed)
+ #if NETFRAMEWORK
+ RuntimeHelpers.PrepareConstrainedRegions();
+ #endif
+
+ lock (s_dllLock)
{
- return;
+ if (s_userInstanceDLLHandle == IntPtr.Zero)
+ {
+ SniNativeWrapper.SNIQueryInfo(QueryType.SNI_QUERY_LOCALDB_HMODULE, ref s_userInstanceDLLHandle);
+ if (s_userInstanceDLLHandle != IntPtr.Zero)
+ {
+ #if NETFRAMEWORK
+ SqlClientEventSource.Log.TryTraceEvent(" LocalDB - handle obtained");
+ #else
+ SqlClientEventSource.Log.TryTraceEvent("LocalDBAPI.UserInstanceDLLHandle | LocalDB - handle obtained");
+ #endif
+ }
+ else
+ {
+ SniNativeWrapper.SNIGetLastError(out SniError sniError);
+ throw CreateLocalDBException(errorMessage: StringsHelper.GetString("LocalDB_FailedGetDLLHandle"), sniError: sniError.sniError);
+ }
+ }
}
}
- if (_fullTrust == null)
- {
- _fullTrust = new NamedPermissionSet("FullTrust");
- }
- _fullTrust.Demand();
+ return s_userInstanceDLLHandle;
}
}
+ #if NETFRAMEWORK
internal static void AssertLocalDBPermissions()
{
_partialTrustAllowed = true;
}
-
-
+ #endif
+
+ #if NETFRAMEWORK
internal static void CreateLocalDBInstance(string instance)
{
DemandLocalDBPermissions();
@@ -317,7 +184,10 @@ internal static void CreateLocalDBInstance(string instance)
// validate section type
LocalDBConfigurationSection configSection = section as LocalDBConfigurationSection;
if (configSection == null)
+ {
throw CreateLocalDBException(errorMessage: StringsHelper.GetString("LocalDB_BadConfigSectionType"));
+ }
+
foreach (LocalDBInstanceElement confElement in configSection.LocalDbInstances)
{
Debug.Assert(confElement.Name != null && confElement.Version != null, "Both name and version should not be null");
@@ -334,22 +204,32 @@ internal static void CreateLocalDBInstance(string instance)
finally
{
if (lockTaken)
+ {
Monitor.Exit(s_configLock);
+ }
}
}
InstanceInfo instanceInfo = null;
if (!s_configurableInstances.TryGetValue(instance, out instanceInfo))
- return; // instance name was not in the config
+ {
+ // instance name was not in the config
+ return;
+ }
if (instanceInfo.created)
- return; // instance has already been created
+ {
+ // instance has already been created
+ return;
+ }
Debug.Assert(!instance.Contains("\0"), "Instance name should contain embedded nulls");
if (instanceInfo.version.Contains("\0"))
+ {
throw CreateLocalDBException(errorMessage: StringsHelper.GetString("LocalDB_InvalidVersion"), instance: instance);
+ }
// LocalDBCreateInstance is thread- and cross-process safe method, it is OK to call from two threads simultaneously
int hr = LocalDBCreateInstance(instanceInfo.version, instance, flags: 0);
@@ -362,6 +242,157 @@ internal static void CreateLocalDBInstance(string instance)
SqlClientEventSource.Log.TryTraceEvent(" Finished creation of instance {0}", instance);
instanceInfo.created = true; // mark instance as created
- } // CreateLocalDbInstance
+ }
+ #endif
+
+ #if NETFRAMEWORK
+ internal static void DemandLocalDBPermissions()
+ {
+ if (!_partialTrustAllowed)
+ {
+ if (!_partialTrustFlagChecked)
+ {
+ object partialTrustFlagValue = AppDomain.CurrentDomain.GetData(Const_partialTrustFlagKey);
+ if (partialTrustFlagValue != null && partialTrustFlagValue is bool)
+ {
+ _partialTrustAllowed = (bool)partialTrustFlagValue;
+ }
+ _partialTrustFlagChecked = true;
+ if (_partialTrustAllowed)
+ {
+ return;
+ }
+ }
+ if (_fullTrust == null)
+ {
+ _fullTrust = new NamedPermissionSet("FullTrust");
+ }
+ _fullTrust.Demand();
+ }
+ }
+ #endif
+
+ // check if name is in format (localdb)\ and return instance name if it is
+ // localDB can also have a format of np:\\.\pipe\LOCALDB#\tsql\query
+ internal static string GetLocalDbInstanceNameFromServerName(string serverName)
+ {
+ if (serverName is not null)
+ {
+ // it can start with spaces if specified in quotes
+ // Memory allocation is reduced by using ReadOnlySpan
+ ReadOnlySpan input = serverName.AsSpan().Trim();
+ if (input.StartsWith(LocalDbPrefix.AsSpan(), StringComparison.OrdinalIgnoreCase))
+ {
+ input = input.Slice(LocalDbPrefix.Length);
+ if (!input.IsEmpty)
+ {
+ return input.ToString();
+ }
+ }
+ else if (input.StartsWith(LocalDbPrefix_NP.AsSpan(), StringComparison.OrdinalIgnoreCase))
+ {
+ return input.ToString();
+ }
+
+ }
+ return null;
+ }
+
+ internal static string GetLocalDBMessage(int hrCode)
+ {
+ Debug.Assert(hrCode < 0, "HRCode does not indicate error");
+ try
+ {
+ StringBuilder buffer = new StringBuilder((int)const_ErrorMessageBufferSize);
+ uint len = (uint)buffer.Capacity;
+
+ // First try for current culture
+ int hResult = LocalDBFormatMessage(hrLocalDB: hrCode, dwFlags: const_LOCALDB_TRUNCATE_ERR_MESSAGE, dwLanguageId: (uint)CultureInfo.CurrentCulture.LCID,
+ buffer: buffer, buflen: ref len);
+ if (hResult >= 0)
+ {
+ return buffer.ToString();
+ }
+ else
+ {
+ // Message is not available for current culture, try default
+ buffer = new StringBuilder((int)const_ErrorMessageBufferSize);
+ len = (uint)buffer.Capacity;
+ hResult = LocalDBFormatMessage(hrLocalDB: hrCode, dwFlags: const_LOCALDB_TRUNCATE_ERR_MESSAGE, dwLanguageId: 0 /* thread locale with fallback to English */,
+ buffer: buffer, buflen: ref len);
+ if (hResult >= 0)
+ {
+ return buffer.ToString();
+ }
+ else
+ {
+ return string.Format(CultureInfo.CurrentCulture, "{0} (0x{1:X}).", Strings.LocalDB_UnobtainableMessage, hResult);
+ }
+ }
+ }
+ catch (SqlException exc)
+ {
+ return string.Format(CultureInfo.CurrentCulture, "{0} ({1}).", Strings.LocalDB_UnobtainableMessage, exc.Message);
+ }
+ }
+
+ internal static void ReleaseDLLHandles()
+ {
+ s_userInstanceDLLHandle = IntPtr.Zero;
+ s_localDBFormatMessage = null;
+
+ #if NETFRAMEWORK
+ s_localDBCreateInstance = null;
+ #endif
+ }
+
+ private static SqlException CreateLocalDBException(string errorMessage, string instance = null, int localDbError = 0, uint sniError = 0)
+ {
+ Debug.Assert((localDbError == 0) || (sniError == 0), "LocalDB error and SNI error cannot be specified simultaneously");
+ Debug.Assert(!string.IsNullOrEmpty(errorMessage), "Error message should not be null or empty");
+ SqlErrorCollection collection = new SqlErrorCollection();
+
+ int errorCode = (localDbError == 0) ? (int)sniError : localDbError;
+
+ if (sniError != 0)
+ {
+ string sniErrorMessage = SQL.GetSNIErrorMessage(sniError);
+ errorMessage = string.Format("{0} (error: {1} - {2})", errorMessage, sniError, sniErrorMessage);
+ }
+
+ collection.Add(new SqlError(errorCode, 0, TdsEnums.FATAL_ERROR_CLASS, instance, errorMessage, null, 0));
+
+ if (localDbError != 0)
+ {
+ collection.Add(new SqlError(errorCode, 0, TdsEnums.FATAL_ERROR_CLASS, instance, GetLocalDBMessage(localDbError), null, 0));
+ }
+
+ SqlException exc = SqlException.CreateException(collection, null);
+
+ exc._doNotReconnect = true;
+
+ return exc;
+ }
+
+ private static IntPtr LoadProcAddress(string funcName) =>
+ #if NETFRAMEWORK
+ Kernel32Safe.GetProcAddress(UserInstanceDLLHandle, funcName);
+ #else
+ Kernel32.GetProcAddress(UserInstanceDLLHandle, funcName);
+ #endif
+
+ #if NETFRAMEWORK
+ private class InstanceInfo
+ {
+ internal InstanceInfo(string version)
+ {
+ this.version = version;
+ this.created = false;
+ }
+
+ internal readonly string version;
+ internal bool created;
+ }
+ #endif
}
}