正确的方法来检测SQLCMD.exe是否已安装?

11
我正在创建一个类库,它以.SQL文件 (FileInfo) 和连接字符串作为输入,尝试执行这些SQL文件对应的操作。
我决定支持Microsoft的SMO和SQLCMD.exe。
在测试中,我注意到在我环境中的很多机器上,并没有默认安装SQLCMD。仅尝试运行进程SQLCMD.exe时,我的程序会报错。
有没有一种正确的方式来查找它(SQLCMD)而不是搜索整个硬盘?是否有一个常见的注册表位置指定其是否已安装?通常情况下,当它被安装后,会设置一个PATH位置。
非常感谢。

你可以在C#应用程序的进程中尝试执行sqlcmd.exe -?,如果它能够正常工作,那么SQLCMD就已经存在了。如果不能正常工作,它会告诉你一些类似于“文件未找到”或“命令无效”的信息... - marc_s
@marc_s 是的,那正是我所想的...只是我认为对于许多应用程序的一个基本组件来说,这不是最清晰的类库实现方式。 - Issa Fram
3个回答

12

我的机器是64位的,上面安装了64位的SQL SERVER (2k8 R2),而且我的SQLCMD.EXE文件在 c:\Program Files\Microsoft SQL Server\100\Tools\Binn\SQLCMD.EXE 目录下。

同时,这个目录也被添加到了环境变量中。

你也可以直接从SQL Server注册表位置查找路径:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\100\Tools\ClientSetup\path

或者针对不同版本进行修改。

这里的一个大问题是SQLCMD是客户端工具的一部分,而不是SQL Server,因此我认为你无法要求SQL Server告诉你。除非当然你正在服务器本身上运行。


1
SQL Server 可以安装在不同的目录中(比如 D: 驱动器或其他位置),因此检查目录并不是非常可靠的方式... 您绝对需要先从注册表中获取“工具”目录... - marc_s
请注意,如果您在C#中使用Registry.LocalMachine.OpenSubKey()方法,则在运行64位Windows的计算机上将返回x86位置(例如C:\Program Files (x86)...)。 如果您的应用程序是x86,则会出现“文件未找到”的情况,因为SQL Server不会在那里安装SQLCMD。 请注意,这仅适用于x86应用程序。 - Dan Nolan
5
请注意,SQL2012工具安装到..Microsoft SQL Server \ 110 \ Tools..(即不是“100”),如果通过SqlCmdLnUtils.msi安装嵌入式工具,则不会将其放置在路径中。使用包含特定版本号的注册表键似乎是不明智的。那么HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Microsoft SQL Server \ SqlCmdLnUtils \ CurrentVersion怎么样? - PandaWood
@PandaWood:嗯,那行不通,因为在2008版本中,“SqlCmdLnUtils”被称为“SQLLNCLI10”,虽然它有一个版本号,但它显然与“100”无关。所以我们只能检查注册表中所有可能的位置,即“110”,然后是“100”,以及您可能知道的任何其他地方。 - Rob

10

打开 cmd.exe 并使用

where sqlcmd.exe

这对我很有帮助,因为我正在寻找创建数据库备份脚本的路径。谢谢! - bikram s.

0
这里有一个解决方案 - 使用执行进程。
它适用于所有安装变体的 MS SQL(带有100-170)。
 function ExecuteProcess(const FileName, Params: string;
      Folder: string; WaitUntilTerminated, WaitUntilIdle, RunMinimized: Boolean;
      var ErrorCode: Integer): Boolean;
    var
      cmdLine: string;
      WorkingDirP: PChar;
      StartupInfo: TStartupInfo;
      ProcessInfo: TProcessInformation;
    begin
      Result := true;
      cmdLine := '"' + FileName + '" ' + Params;
      if Folder = '' then
        Folder := ExcludeTrailingPathDelimiter(ExtractFilePath(FileName));
      ZeroMemory(@StartupInfo, SizeOf(StartupInfo));
      StartupInfo.cb := SizeOf(StartupInfo);
      if RunMinimized then
      begin
        StartupInfo.dwFlags := STARTF_USESHOWWINDOW;
        StartupInfo.wShowWindow := SW_SHOWMINIMIZED;
      end;
      if Folder <> '' then
        WorkingDirP := PChar(Folder)
      else
        WorkingDirP := nil;
      if not CreateProcess(nil, PChar(cmdLine), nil, nil, false, 0, nil,
        WorkingDirP, StartupInfo, ProcessInfo) then
      begin
        Result := false;
        ErrorCode := GetLastError;
        exit;
      end;
      with ProcessInfo do
      begin
        CloseHandle(hThread); // CHECK - CLOSE HERE? or move line down?
        if WaitUntilIdle then
          WaitForInputIdle(hProcess, INFINITE);
        // CHECK ::WaitUntilTerminated was used in C++ sqlcmd.exe
        if WaitUntilTerminated then
          repeat
            Application.ProcessMessages;
          until MsgWaitForMultipleObjects(1, hProcess, false, INFINITE, QS_ALLINPUT)
            <> WAIT_OBJECT_0 + 1;
        CloseHandle(hProcess);
        // CHECK :: CloseHandle(hThread); was originally placed here in C++ ...
      end;
    
    end;



procedure TestForsqlCmd();
var
  errCode: Integer;
  success: Boolean;
begin
    
      success := ExecuteProcess('sqlcmd.exe', '-?', '', true, true, true, errCode);
    
      if not success then
      begin
        s := 'The application ''sqlcmd.exe'' wasn''t found!' + sLineBreak +
          'The MS SQLEXPRESS utility is missing.' + sLineBreak +
          'Press EXIT when ready.';
        MessageDlg(s, TMsgDlgType.mtError, [mbOk], 0);
        exit;
      end;
end;
    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接