65.9K
CodeProject 正在变化。 阅读更多。
Home

在网络上查找 SQL Server

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.79/5 (79投票s)

2003 年 12 月 23 日

公共领域

4分钟阅读

viewsIcon

336810

downloadIcon

14354

通过 C# PInvoke 调用使用 ODBC 定位 MS SQL 服务器。

Sample Image - DBGrep.jpg

引言

我是一名 TSQL 狂热者。我编写的程序高度依赖于 MS SQL 存储过程。当我需要对系统进行重大改版时,我会为这份对 TSQL 的热爱付出代价。有时我的代码需要在两个地方进行大量更新:客户端和服务器端。为了方便这些更新,我创建了一个数据库搜索程序,我称之为 DB Grep。它使用 Regex 来搜索整个数据库中对单词或短语的每一次引用。该程序节省了大量研究时间,并给了我一种温暖的、令人放心的“我没有遗漏任何东西”的感觉。

在开发 DB Grep 时,我遇到了一个有趣的问题。我如何才能在我的网络上找到 SQL 服务器?

选项

经过数小时的网络研究,我提出了以下几种选择:

选项 Pro
无定位服务。 无需编写代码。 凭记忆输入 SQL 服务器名称很麻烦。
使用 Windows OS 服务。 使用 NetServerEnum 非常快。 不一定总能返回所需的结果。它返回 Windows 服务器名称,而不是 SQL 服务器名称,而且它们不总是相同的。它在非域网络上效果也不好。未能找到我的本地 MSDE 服务器。
使用 SQLDMO 对象。 返回所需结果。 安装麻烦。安装 COM 对象总是很麻烦,更不用说可能的许可问题了。
使用 ODBC 返回所需结果。应该已经安装。 尚未找到任何。

不用说,我选择了 ODBC 解决方案。这需要进行一些研究,并进行了大量的 PInvoke 试验和错误修正。

我应该提前说明,此方法尚未在 Windows 95/98/ME 上进行测试。我已经决定,这些操作系统不再是我新开发所必需的。该代码已在 Windows 2000 和 XP 上使用 Framework 1.1 进行了测试。

流程

为了获取 ODBC 可用 SQL 服务器的名称,我们必须分配一个环境,设置 ODBC 样式并连接到 ODBC 服务。SQLAllocHandle() 用于获取环境和连接句柄。在调用之间,有必要通过调用 SQLSetEnvAttr() 来指定要使用的 ODBC 版本。我选择使用系统常量 ODBC 3.0。当然,您必须始终通过调用 SQLFreeHandle() 来释放环境和连接句柄,以正确管理 ODBC 资源。

我将分配调用包装在 try 块中,并将释放调用放在 finally 部分,以确保 ODBC 资源的释放。设置和拆除 ODBC 环境所需的 PInvoke 声明如下:

private const short SQL_HANDLE_ENV = 1;
private const short SQL_HANDLE_DBC = 2;
private const int SQL_ATTR_ODBC_VERSION = 200;
private const int SQL_OV_ODBC3 = 3;
private const short SQL_SUCCESS = 0;


[DllImport("odbc32.dll")]
private static extern short SQLAllocHandle(
    short hType, 
    IntPtr inputHandle, 
    out IntPtr outputHandle);
[DllImport("odbc32.dll")]
private static extern short SQLSetEnvAttr(
    IntPtr henv, 
    int attribute, 
    IntPtr valuePtr, 
    int strLength);
[DllImport("odbc32.dll")]
private static extern short SQLFreeHandle(
    short hType, 
    IntPtr handle);

一旦建立连接,我们就可以利用 SQL ODBC 驱动程序的技巧来定位正在广播的 MS SQL 服务器。我尝试使用 SQLBrowseConnect() 打开一个 MS SQL 数据库连接,仅指定 SQL 驱动程序。驱动程序会响应我的请求,构建一个连接字符串,其中包含可以从驱动程序推导出的可能值的必需参数(例如可用的 MS SQL 服务器)。然后它会返回一个表明需要更多信息的返回值。服务器名称可以轻松地从该连接字符串中解析出来。

为了速度,我预先分配了一个容量为 1024 个字符的 StringBuilder。如果可用服务器列表特别大,我会测试一个 out 参数,看看是否需要一个更大的字符串。如有必要,我会使用新调整大小的字符串重新调用 SQLBrowseConnect() 函数。SQLBrowseConnect() 调用的 PInvoke 如下:

private const short SQL_NEED_DATA = 99;
private const string SQL_DRIVER_STR = "DRIVER=SQL SERVER";
 
[DllImport("odbc32.dll",CharSet=CharSet.Ansi)]
private static extern short SQLBrowseConnect(
    IntPtr hconn, 
    StringBuilder inString, 
    short inStringLength, 
    StringBuilder outString, 
    short outStringLength, 
    out short outLengthNeeded);

例如,我将 "DRIVER=SQL SERVER" 传递给 SQLBrowseConnect(),并得到类似 "SERVER:Server={(local),SQL_SERVER1,SQL_BKSVR};UID:Login ID=?; PWD:Password=?; *APP:AppName=?; *WSID:WorkStation ID=?" 的返回。从两个大括号之间提取逗号分隔的子字符串是一项简单的任务。为了简化服务器列表的使用,我调用该子字符串的 Split() 方法,返回一个字符串数组,该数组可用于 foreach 语句。

为了重用,我将 PInvoke 声明和静态方法封装在一个类中。由于这只是一个辅助方法,我采取了预防措施,以在发布代码时隐藏任何故障,并在该情况下返回一个 null string[] 值。 null 返回值表示未找到任何服务器。下面是一个调用结果代码的示例:

string[] theAvailableSqlServers = SqlLocator.GetServers();
if (theAvailableSqlServers != null)
{
    myListBox.DataSource = theAvailableSqlServers;
} 
else
{
    MessageBox.Show("No SQL servers found.");
}

摘要

这段代码更多是研究的成果,而非技巧的体现。希望我能为您在未来的项目中节省一些在 MSDN 上艰难摸索的时间。如果您进行大量的 MS SQL 工作,您可能会对我的 DB Grep 程序感兴趣,该程序在 GotDotNet 上免费提供(附带源代码)。它在我目前的环境中运行良好,但我敢肯定,在其他地方它也需要进行一次很好的测试。

© . All rights reserved.