You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从注册表获取SQL Server实例名称遇iSizeInByte=-1问题求解决

解决SQL Server实例名称获取失败的问题

问题分析

你遇到的GetDataSize返回-1的核心原因是注册表重定向:

  • 64位Windows系统中,32位程序访问HKEY_LOCAL_MACHINE\SOFTWARE时,会被自动重定向到HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node
  • SQL Server的64位实例信息存储在原路径HKLM\SOFTWARE\Microsoft\Microsoft SQL Server,32位实例则在重定向后的路径中。若你的程序是32位编译,会找不到目标键值,导致GetDataSize返回-1。

此外你的ReadMultiSz实现存在边界错误:循环for z := 0 to iWCharsInBuffer do会越界访问数组,数组有效索引范围应为0到iWCharsInBuffer-1。

可行解决方法

方法1:修复注册表读取代码(处理64位重定向)

修改代码指定注册表访问的位模式,同时修正ReadMultiSz的循环逻辑:

type
  TRegistryHelper = class helper for TRegistry
  public
    function ReadMultiSz(const name: string; var Strings: TStrings): boolean;
  end;
    
function TRegistryHelper.ReadMultiSz(const name: string;
  var Strings: TStrings): boolean;
var
  iSizeInByte: integer;
  Buffer: array of WChar;
  iWCharsInBuffer: integer;
  z: integer;
  sString: string;
begin
  Result := False;
  iSizeInByte := GetDataSize(name);
  if iSizeInByte <= 0 then Exit;

  SetLength(Buffer, iSizeInByte div sizeof(WChar));
  iWCharsInBuffer := ReadBinaryData(name, Buffer[0], iSizeInByte) div sizeof(WChar);
  if iWCharsInBuffer <= 0 then Exit;

  sString := '';
  // 修正循环边界,避免越界
  for z := 0 to iWCharsInBuffer - 1 do begin
    if Buffer[z] <> #0 then begin
      sString := sString + Buffer[z];
    end else begin
      if sString <> '' then begin
        Strings.Append(sString);
        sString := '';
      end;
    end;
  end;
  // 处理最后一个未闭合的字符串(如果存在)
  if sString <> '' then
    Strings.Append(sString);
  Result := True;
end;

procedure TForm1.Button1Click(Sender: TObject);
const
  cKey = '\SOFTWARE\Microsoft\Microsoft SQL Server';
var
  Registry: TRegistry;
  MyList: TStrings;
begin
  Registry := TRegistry.Create;
  try
    Registry.RootKey := HKEY_LOCAL_MACHINE;
    // 设置访问权限,支持读取64位注册表(适用于64位系统)
    Registry.Access := KEY_READ or KEY_WOW64_64KEY;
    if Registry.OpenKeyReadOnly(cKey) then
    try
      MyList := TStringList.Create();
      try
        if Registry.ReadMultiSz('InstalledInstances', MyList) then
          ListBox1.Items.Assign(MyList);
      finally
        MyList.Free;
      end;
    finally
      Registry.CloseKey;
    end;
  finally
    Registry.Free;
  end;
end;

如果需要读取32位SQL Server实例,将Registry.Access改为KEY_READ or KEY_WOW64_32KEY即可。

方法2:使用SQL Browser服务枚举实例

通过ODBC的SQLBrowseConnectAPI直接枚举SQL Server实例,无需依赖注册表:

uses
  Windows, SysUtils, Classes, DB;

function GetSQLServerInstances(out InstanceList: TStrings): Boolean;
var
  hEnv: SQLHENV;
  hDbc: SQLHDBC;
  szConnStrIn, szConnStrOut: array[0..2047] of Char;
  cbConnStrOut: SQLSMALLINT;
  ret: SQLRETURN;
  Pos, Start: Integer;
  InstanceName: string;
begin
  Result := False;
  InstanceList.Clear;

  // 初始化ODBC环境
  ret := SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, @hEnv);
  if ret <> SQL_SUCCESS then Exit;
  try
    ret := SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, Pointer(SQL_OV_ODBC3), 0);
    if ret <> SQL_SUCCESS then Exit;

    // 分配连接句柄
    ret := SQLAllocHandle(SQL_HANDLE_DBC, hEnv, @hDbc);
    if ret <> SQL_SUCCESS then Exit;
    try
      // 设置连接字符串,请求枚举实例
      StrPCopy(szConnStrIn, 'DRIVER={SQL Server};');
      cbConnStrOut := SizeOf(szConnStrOut);
      ret := SQLBrowseConnect(hDbc, szConnStrIn, SQL_NTS, szConnStrOut, cbConnStrOut, @cbConnStrOut);
      
      if ret = SQL_SUCCESS_WITH_INFO then begin
        // 解析返回的字符串,提取实例名称
        Start := 1;
        Pos := Pos('SERVER=', szConnStrOut);
        if Pos > 0 then begin
          Inc(Pos, 7);
          Start := Pos;
          while Pos <= cbConnStrOut do begin
            if szConnStrOut[Pos] = ';' then Break;
            Inc(Pos);
          end;
          InstanceName := Copy(szConnStrOut, Start, Pos - Start);
          // 分割多实例(用逗号分隔)
          InstanceList.CommaText := InstanceName;
          Result := True;
        end;
      end;
    finally
      SQLFreeHandle(SQL_HANDLE_DBC, hDbc);
    end;
  finally
    SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
  end;
end;

// 调用示例
procedure TForm1.Button2Click(Sender: TObject);
var
  InstanceList: TStrings;
begin
  InstanceList := TStringList.Create;
  try
    if GetSQLServerInstances(InstanceList) then
      ListBox1.Items.Assign(InstanceList);
  finally
    InstanceList.Free;
  end;
end;

方法3:同时检查32位和64位注册表路径

若不确定系统位宽,可以同时读取两个路径下的实例信息并合并去重:

  • 64位路径:HKLM\SOFTWARE\Microsoft\Microsoft SQL Server
  • 32位路径:HKLM\SOFTWARE\Wow6432Node\Microsoft\Microsoft SQL Server

分别调用ReadMultiSz,将结果合并后去除重复项即可。


内容的提问来源于stack exchange,提问作者Issam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 20:45:48