diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Common.cs b/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Common.cs index 7c16bb0afb..7f181d89d4 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Common.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Common.cs @@ -31,8 +31,11 @@ internal enum SniSpecialErrors : uint [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIServerEnumCloseWrapper")] internal static extern void SNIServerEnumClose([In] IntPtr packet); - [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIServerEnumReadWrapper")] - internal static extern int SNIServerEnumRead([In] IntPtr packet, [In, Out] char[] readBuffer, int bufferLength, out bool more); + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIServerEnumReadWrapper", CharSet = CharSet.Unicode)] + internal static extern int SNIServerEnumRead([In] IntPtr packet, + [In][MarshalAs(UnmanagedType.LPArray)] char[] readBuffer, + [In] int bufferLength, + [MarshalAs(UnmanagedType.Bool)] out bool more); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SSRP.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SSRP.cs index 0f3d54e474..a171752ebf 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SSRP.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SSRP.cs @@ -11,7 +11,7 @@ namespace Microsoft.Data.SqlClient.SNI { - internal class SSRP + internal sealed class SSRP { private const char SemicolonSeparator = ';'; private const int SqlServerBrowserPort = 1434; //port SQL Server Browser @@ -78,11 +78,11 @@ private static byte[] CreateInstanceInfoRequest(string instanceName) { const byte ClntUcastInst = 0x04; instanceName += char.MinValue; - int byteCount = Encoding.ASCII.GetByteCount(instanceName); + int byteCount = Encoding.UTF7.GetByteCount(instanceName); byte[] requestPacket = new byte[byteCount + 1]; requestPacket[0] = ClntUcastInst; - Encoding.ASCII.GetBytes(instanceName, 0, instanceName.Length, requestPacket, 1); + Encoding.UTF7.GetBytes(instanceName, 0, instanceName.Length, requestPacket, 1); return requestPacket; } @@ -127,12 +127,12 @@ private static byte[] CreateDacPortInfoRequest(string instanceName) const byte ClntUcastDac = 0x0F; const byte ProtocolVersion = 0x01; instanceName += char.MinValue; - int byteCount = Encoding.ASCII.GetByteCount(instanceName); + int byteCount = Encoding.UTF7.GetByteCount(instanceName); byte[] requestPacket = new byte[byteCount + 2]; requestPacket[0] = ClntUcastDac; requestPacket[1] = ProtocolVersion; - Encoding.ASCII.GetBytes(instanceName, 0, instanceName.Length, requestPacket, 2); + Encoding.UTF7.GetBytes(instanceName, 0, instanceName.Length, requestPacket, 2); return requestPacket; } @@ -184,7 +184,9 @@ internal static string SendBroadcastUDPRequest() { StringBuilder response = new StringBuilder(); byte[] CLNT_BCAST_EX_Request = new byte[1] { CLNT_BCAST_EX }; //0x02 - int currentTimeOut = FirstTimeoutForCLNT_BCAST_EX; //wait 5 secs for first response and every 1 sec upto 15 secs (https://docs.microsoft.com/en-us/openspecs/windows_protocols/mc-sqlr/f2640a2d-3beb-464b-a443-f635842ebc3e#Appendix_A_3) + // Waits 5 seconds for the first response and every 1 second up to 15 seconds + // https://docs.microsoft.com/en-us/openspecs/windows_protocols/mc-sqlr/f2640a2d-3beb-464b-a443-f635842ebc3e#Appendix_A_3 + int currentTimeOut = FirstTimeoutForCLNT_BCAST_EX; using (TrySNIEventScope.Create(nameof(SSRP))) { @@ -203,7 +205,7 @@ internal static string SendBroadcastUDPRequest() SqlClientEventSource.Log.TrySNITraceEvent(nameof(SSRP), EventType.INFO, "Received instnace info from UDP Client."); if (receiveTask.Result.Buffer.Length < ValidResponseSizeForCLNT_BCAST_EX) //discard invalid response { - response.Append(Encoding.ASCII.GetString(receiveTask.Result.Buffer, ServerResponseHeaderSizeForCLNT_BCAST_EX, receiveTask.Result.Buffer.Length - ServerResponseHeaderSizeForCLNT_BCAST_EX)); //RESP_DATA(VARIABLE) - 3 (RESP_SIZE + SVR_RESP) + response.Append(Encoding.UTF7.GetString(receiveTask.Result.Buffer, ServerResponseHeaderSizeForCLNT_BCAST_EX, receiveTask.Result.Buffer.Length - ServerResponseHeaderSizeForCLNT_BCAST_EX)); //RESP_DATA(VARIABLE) - 3 (RESP_SIZE + SVR_RESP) } } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX64.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX64.cs index d851c01eda..13e35363a8 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX64.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX64.cs @@ -141,7 +141,10 @@ internal static extern unsafe uint SNISecGenClientContextWrapper( [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIServerEnumCloseWrapper")] internal static extern void SNIServerEnumClose([In] IntPtr packet); - [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIServerEnumReadWrapper")] - internal static extern int SNIServerEnumRead([In] IntPtr packet, [In, Out] char[] readBuffer, int bufferLength, out bool more); + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIServerEnumReadWrapper", CharSet = CharSet.Unicode)] + internal static extern int SNIServerEnumRead([In] IntPtr packet, + [In, Out][MarshalAs(UnmanagedType.LPArray)] char[] readBuffer, + [In] int bufferLength, + [MarshalAs(UnmanagedType.Bool)] out bool more); } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX86.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX86.cs index 276e8cdc29..5517ba8c0e 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX86.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX86.cs @@ -141,7 +141,10 @@ internal static extern unsafe uint SNISecGenClientContextWrapper( [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIServerEnumCloseWrapper")] internal static extern void SNIServerEnumClose([In] IntPtr packet); - [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIServerEnumReadWrapper")] - internal static extern int SNIServerEnumRead([In] IntPtr packet, [In, Out] char[] readBuffer, int bufferLength, out bool more); + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIServerEnumReadWrapper", CharSet = CharSet.Unicode)] + internal static extern int SNIServerEnumRead([In] IntPtr packet, + [In, Out][MarshalAs(UnmanagedType.LPArray)] char[] readBuffer, + [In] int bufferLength, + [MarshalAs(UnmanagedType.Bool)] out bool more); } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorManagedHelper.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorManagedHelper.cs index 7d093d487d..02ed400aea 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorManagedHelper.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorManagedHelper.cs @@ -1,10 +1,8 @@ // 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.Collections.Generic; using System.Data; -using System.Globalization; using Microsoft.Data.Sql; using Microsoft.Data.SqlClient.SNI; @@ -13,27 +11,21 @@ namespace Microsoft.Data.SqlClient.Server /// /// Provides a mechanism for enumerating all available instances of SQL Server within the local network /// - internal static class SqlDataSourceEnumeratorManagedHelper + internal static class SqlDataSourceEnumeratorManagedHelper { /// /// Provides a mechanism for enumerating all available instances of SQL Server within the local network. /// /// DataTable with ServerName,InstanceName,IsClustered and Version internal static DataTable GetDataSources() - { + { return ParseServerEnumString(SSRP.SendBroadcastUDPRequest()); } - static private System.Data.DataTable ParseServerEnumString(string serverInstances) + private static DataTable ParseServerEnumString(string serverInstances) { - DataTable dataTable = new("SqlDataSources") - { - Locale = CultureInfo.InvariantCulture - }; - dataTable.Columns.Add(SqlDataSourceEnumeratorUtil.ServerName, typeof(string)); - dataTable.Columns.Add(SqlDataSourceEnumeratorUtil.InstanceName, typeof(string)); - dataTable.Columns.Add(SqlDataSourceEnumeratorUtil.IsClustered, typeof(string)); - dataTable.Columns.Add(SqlDataSourceEnumeratorUtil.Version, typeof(string)); + System.Console.WriteLine(serverInstances); + DataTable dataTable = SqlDataSourceEnumeratorUtil.PrepareDataTable(); DataRow dataRow; if (serverInstances.Length == 0) @@ -41,8 +33,9 @@ static private System.Data.DataTable ParseServerEnumString(string serverInstance return dataTable; } - string[] numOfServerInstances = serverInstances.Split(new[] { SqlDataSourceEnumeratorUtil.EndOfServerInstanceDelimiterManaged }, StringSplitOptions.None); - SqlClientEventSource.Log.TryTraceEvent(" Number of server instances results recieved are {0}", numOfServerInstances.Length); + string[] numOfServerInstances = serverInstances.Split(SqlDataSourceEnumeratorUtil.s_endOfServerInstanceDelimiter_Managed, System.StringSplitOptions.None); + SqlClientEventSource.Log.TryTraceEvent(" Number of recieved server instances are {2}", + nameof(SqlDataSourceEnumeratorManagedHelper), nameof(ParseServerEnumString), numOfServerInstances.Length); foreach (string currentServerInstance in numOfServerInstances) { @@ -65,24 +58,19 @@ static private System.Data.DataTable ParseServerEnumString(string serverInstance if (InstanceDetails.Count > 0) { dataRow = dataTable.NewRow(); - dataRow[0] = InstanceDetails.ContainsKey(SqlDataSourceEnumeratorUtil.ServerName) == true ? - InstanceDetails[SqlDataSourceEnumeratorUtil.ServerName] : string.Empty; - dataRow[1] = InstanceDetails.ContainsKey(SqlDataSourceEnumeratorUtil.InstanceName) == true ? - InstanceDetails[SqlDataSourceEnumeratorUtil.InstanceName] : string.Empty; - dataRow[2] = InstanceDetails.ContainsKey(SqlDataSourceEnumeratorUtil.IsClustered) == true ? - InstanceDetails[SqlDataSourceEnumeratorUtil.IsClustered] : string.Empty; - dataRow[3] = InstanceDetails.ContainsKey(SqlDataSourceEnumeratorUtil.Version) == true ? - InstanceDetails[SqlDataSourceEnumeratorUtil.Version] : string.Empty; + dataRow[0] = InstanceDetails.ContainsKey(SqlDataSourceEnumeratorUtil.ServerNameCol) == true ? + InstanceDetails[SqlDataSourceEnumeratorUtil.ServerNameCol] : string.Empty; + dataRow[1] = InstanceDetails.ContainsKey(SqlDataSourceEnumeratorUtil.InstanceNameCol) == true ? + InstanceDetails[SqlDataSourceEnumeratorUtil.InstanceNameCol] : string.Empty; + dataRow[2] = InstanceDetails.ContainsKey(SqlDataSourceEnumeratorUtil.IsClusteredCol) == true ? + InstanceDetails[SqlDataSourceEnumeratorUtil.IsClusteredCol] : string.Empty; + dataRow[3] = InstanceDetails.ContainsKey(SqlDataSourceEnumeratorUtil.VersionNameCol) == true ? + InstanceDetails[SqlDataSourceEnumeratorUtil.VersionNameCol] : string.Empty; dataTable.Rows.Add(dataRow); } } - - foreach (DataColumn column in dataTable.Columns) - { - column.ReadOnly = true; - } - return dataTable; + return dataTable.SetColumnsReadOnly(); } } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorNativeHelper.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorNativeHelper.cs index 016dea0f2e..db40a6439e 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorNativeHelper.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorNativeHelper.cs @@ -1,22 +1,23 @@ // 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.Data; using System.Diagnostics; -using System.Globalization; using System.Runtime.CompilerServices; using System.Security; using System.Text; using Microsoft.Data.Common; using Microsoft.Data.SqlClient; +using static Microsoft.Data.Sql.SqlDataSourceEnumeratorUtil; namespace Microsoft.Data.Sql { /// /// Provides a mechanism for enumerating all available instances of SQL Server within the local network /// - internal class SqlDataSourceEnumeratorNativeHelper + internal static class SqlDataSourceEnumeratorNativeHelper { /// /// Retrieves a DataTable containing information about all visible SQL Server instances @@ -28,7 +29,7 @@ internal static DataTable GetDataSources() char[] buffer = null; StringBuilder strbldr = new(); - int bufferSize = 65536; + int bufferSize = 1024; int readLength = 0; buffer = new char[bufferSize]; bool more = true; @@ -45,26 +46,31 @@ internal static DataTable GetDataSources() finally { handle = SNINativeMethodWrapper.SNIServerEnumOpen(); + SqlClientEventSource.Log.TryTraceEvent(" {3} returned handle = {4}.", + nameof(SqlDataSourceEnumeratorNativeHelper), + nameof(GetDataSources), + nameof(SNINativeMethodWrapper.SNIServerEnumOpen), handle); } if (handle != ADP.s_ptrZero) { while (more && !TdsParserStaticMethods.TimeoutHasExpired(s_timeoutTime)) { -#if NETFRAMEWORK - readLength = SNINativeMethodWrapper.SNIServerEnumRead(handle, buffer, bufferSize, out more); -#else readLength = SNINativeMethodWrapper.SNIServerEnumRead(handle, buffer, bufferSize, out more); -#endif - SqlClientEventSource.Log.TryTraceEvent(" GetDataSources:SNIServerEnumRead returned readlength {0}", readLength); + + SqlClientEventSource.Log.TryTraceEvent(" {2} returned 'readlength':{3}, and 'more':{4} with 'bufferSize' of {5}", + nameof(SqlDataSourceEnumeratorNativeHelper), + nameof(GetDataSources), + nameof(SNINativeMethodWrapper.SNIServerEnumRead), + readLength, more, bufferSize); if (readLength > bufferSize) { failure = true; more = false; } - else if (0 < readLength) + else if (readLength > 0) { - strbldr.Append(buffer); + strbldr.Append(buffer, 0, readLength); } } } @@ -74,69 +80,75 @@ internal static DataTable GetDataSources() if (handle != ADP.s_ptrZero) { SNINativeMethodWrapper.SNIServerEnumClose(handle); + SqlClientEventSource.Log.TryTraceEvent(" {3} called.", + nameof(SqlDataSourceEnumeratorNativeHelper), + nameof(GetDataSources), + nameof(SNINativeMethodWrapper.SNIServerEnumClose)); } } if (failure) { - Debug.Assert(false, "GetDataSources:SNIServerEnumRead returned bad length"); - SqlClientEventSource.Log.TryTraceEvent(" GetDataSources:SNIServerEnumRead returned bad length, requested %d, received %d", bufferSize, readLength); - throw ADP.ArgumentOutOfRange("readLength"); + Debug.Assert(false, $"{nameof(GetDataSources)}:{nameof(SNINativeMethodWrapper.SNIServerEnumRead)} returned bad length"); + SqlClientEventSource.Log.TryTraceEvent(" {2} returned bad length, requested buffer {3}, received {4}", + nameof(SqlDataSourceEnumeratorNativeHelper), + nameof(GetDataSources), + nameof(SNINativeMethodWrapper.SNIServerEnumRead), + bufferSize, readLength); + + throw ADP.ArgumentOutOfRange(StringsHelper.GetString(Strings.ADP_ParameterValueOutOfRange, readLength), nameof(readLength)); } return ParseServerEnumString(strbldr.ToString()); } - static private System.Data.DataTable ParseServerEnumString(string serverInstances) + private static DataTable ParseServerEnumString(string serverInstances) { - DataTable dataTable = new("SqlDataSources"); - dataTable.Locale = CultureInfo.InvariantCulture; - dataTable.Columns.Add(SqlDataSourceEnumeratorUtil.ServerName, typeof(string)); - dataTable.Columns.Add(SqlDataSourceEnumeratorUtil.InstanceName, typeof(string)); - dataTable.Columns.Add(SqlDataSourceEnumeratorUtil.IsClustered, typeof(string)); - dataTable.Columns.Add(SqlDataSourceEnumeratorUtil.Version, typeof(string)); + DataTable dataTable = PrepareDataTable(); string serverName = null; string instanceName = null; string isClustered = null; string version = null; - string[] serverinstanceslist = serverInstances.Split(new string[] { SqlDataSourceEnumeratorUtil.EndOfServerInstanceDelimiterNative }, StringSplitOptions.None); - SqlClientEventSource.Log.TryTraceEvent(" Number of server instances results recieved are {0}", serverinstanceslist.Length); + string[] serverinstanceslist = serverInstances.Split(EndOfServerInstanceDelimiter_Native); + SqlClientEventSource.Log.TryTraceEvent(" Number of recieved server instances are {2}", + nameof(SqlDataSourceEnumeratorNativeHelper), nameof(ParseServerEnumString), serverinstanceslist.Length); // Every row comes in the format "serverName\instanceName;Clustered:[Yes|No];Version:.." // Every row is terminated by a null character. // Process one row at a time foreach (string instance in serverinstanceslist) { - // string value = instance.Trim('\0'); // MDAC 91934 - string value = instance.Replace("\0", ""); - if (0 == value.Length) + string value = instance.Trim(EndOfServerInstanceDelimiter_Native); // MDAC 91934 + if (value.Length == 0) { continue; } - foreach (string instance2 in value.Split(SqlDataSourceEnumeratorUtil.InstanceKeysDelimiter)) + foreach (string instance2 in value.Split(InstanceKeysDelimiter)) { if (serverName == null) { - foreach (string instance3 in instance2.Split(SqlDataSourceEnumeratorUtil.ServerNamesAndInstanceDelimiter)) + foreach (string instance3 in instance2.Split(ServerNamesAndInstanceDelimiter)) { if (serverName == null) { serverName = instance3; continue; } - Debug.Assert(instanceName == null); + Debug.Assert(instanceName == null, $"{nameof(instanceName)}({instanceName}) is not null."); instanceName = instance3; } continue; } if (isClustered == null) { - Debug.Assert(string.Compare(SqlDataSourceEnumeratorUtil.s_cluster, 0, instance2, 0, SqlDataSourceEnumeratorUtil.s_clusterLength, StringComparison.OrdinalIgnoreCase) == 0); - isClustered = instance2.Substring(SqlDataSourceEnumeratorUtil.s_clusterLength); + Debug.Assert(string.Compare(Clustered, 0, instance2, 0, s_clusteredLength, StringComparison.OrdinalIgnoreCase) == 0, + $"{nameof(Clustered)} ({Clustered}) doesn't equal {nameof(instance2)} ({instance2})"); + isClustered = instance2.Substring(s_clusteredLength); continue; } - Debug.Assert(version == null); - Debug.Assert(string.Compare(SqlDataSourceEnumeratorUtil.s_version, 0, instance2, 0, SqlDataSourceEnumeratorUtil.s_versionLength, StringComparison.OrdinalIgnoreCase) == 0); - version = instance2.Substring(SqlDataSourceEnumeratorUtil.s_versionLength); + Debug.Assert(version == null, $"{nameof(version)}({version}) is not null."); + Debug.Assert(string.Compare(SqlDataSourceEnumeratorUtil.Version, 0, instance2, 0, s_versionLength, StringComparison.OrdinalIgnoreCase) == 0, + $"{nameof(SqlDataSourceEnumeratorUtil.Version)} ({SqlDataSourceEnumeratorUtil.Version}) doesn't equal {nameof(instance2)} ({instance2})"); + version = instance2.Substring(s_versionLength); } string query = "ServerName='" + serverName + "'"; @@ -161,11 +173,7 @@ static private System.Data.DataTable ParseServerEnumString(string serverInstance isClustered = null; version = null; } - foreach (DataColumn column in dataTable.Columns) - { - column.ReadOnly = true; - } - return dataTable; + return dataTable.SetColumnsReadOnly(); } } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorUtil.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorUtil.cs index e1179e73fe..fb6972d8cf 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorUtil.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorUtil.cs @@ -1,21 +1,54 @@ +// 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.Data; +using System.Globalization; + namespace Microsoft.Data.Sql { /// /// const values for SqlDataSourceEnumerator /// - internal class SqlDataSourceEnumeratorUtil + internal static class SqlDataSourceEnumeratorUtil { - internal const string ServerName = "ServerName"; - internal const string InstanceName = "InstanceName"; - internal const string IsClustered = "IsClustered"; - internal const string Version = "Version"; - internal static readonly string s_version = "Version:"; - internal static readonly string s_cluster = "Clustered:"; - internal static readonly int s_clusterLength = s_cluster.Length; - internal static readonly int s_versionLength = s_version.Length; - internal const string EndOfServerInstanceDelimiterManaged = ";;"; + internal const string ServerNameCol = "ServerName"; + internal const string InstanceNameCol = "InstanceName"; + internal const string IsClusteredCol = "IsClustered"; + internal const string VersionNameCol = "Version"; + + internal const string Version = "Version:"; + internal const string Clustered = "Clustered:"; + internal static readonly int s_versionLength = Version.Length; + internal static readonly int s_clusteredLength = Clustered.Length; + + internal static readonly string[] s_endOfServerInstanceDelimiter_Managed = new[] { ";;" }; + internal const char EndOfServerInstanceDelimiter_Native = '\0'; internal const char InstanceKeysDelimiter = ';'; - internal const string EndOfServerInstanceDelimiterNative = "\0\0\0"; internal const char ServerNamesAndInstanceDelimiter = '\\'; + + internal static DataTable PrepareDataTable() + { + DataTable dataTable = new("SqlDataSources"); + dataTable.Locale = CultureInfo.InvariantCulture; + dataTable.Columns.Add(ServerNameCol, typeof(string)); + dataTable.Columns.Add(InstanceNameCol, typeof(string)); + dataTable.Columns.Add(IsClusteredCol, typeof(string)); + dataTable.Columns.Add(VersionNameCol, typeof(string)); + + return dataTable; + } + + /// + /// Sets all columns read-only. + /// + internal static DataTable SetColumnsReadOnly(this DataTable dataTable) + { + foreach (DataColumn column in dataTable.Columns) + { + column.ReadOnly = true; + } + return dataTable; + } } }