windows - 当我从 CreateProcess 运行 NETSH 时得到 "The system cannot find the file specified"但它在命令提示符下工作正常?

标签 windows delphi createprocess netsh

我有一个调用用 Delphi 7 编写的控制台程序的 NT 服务,我们称它为 failover.exe,它又调用 NETSH使用我发现的程序:

procedure ExecConsoleApp(CommandLine: ansistring; Output, Errors: TStringList); 

注意:ExecConsoleApp 使用 CreateProcess,完整代码见以下链接:http://www.delphisources.ru/pages/faq/base/createprocess_console.html

在调用 ExecConsoleApp 之前,我会将以下内容传递给 CommandLine:

cmd.exe /c "C:\Windows\system32\netsh.exe interface delete address "Wireless Network Connection" 192.168.0.36" 

ExecConsoleApp 将返回错误:

The system cannot find the file specified

但如果我在命令提示符下运行它,它运行完美。

奇怪的是,我记得它在 2003 服务器上的第一次尝试中工作,但在那之后,无论我尝试了多少次,它都失败了。在其中一次尝试中,我还尝试将以管理员用户身份登录分配给该服务,但无济于事。摆弄文件安全性也无济于事。

我没有 Win 2003 服务器可以在办公室进行测试,但我已经在 XP 和 Win7 上对其进行了测试,ExecConsoleApp 运行完美,尽管在 XP 上,我不得不修改 ExecConsoleAppsystem32\wbem 执行以使其正常工作:

 Res := CreateProcess(nil, PChar(CommandLine), nil, nil, True,
  // **** Attention: Amended by to point current directory to system32\wbem, this is to solve an error returned by netsh.exe if not done otherwise.
 //   CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, @env, nil, si, pi);
   CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, @env, pchar(GetSystemPath(WindRoot) + 'system32\wbem'), si, pi);

我研究了一天,没有任何线索,希望有人能提供帮助。谢谢。

补充说明-

  1. 服务器是 32 位 Win2k3。

  2. 尝试过域管理员,但没有用。

  3. 代码片段:

    Procedure ExecConsoleApp(CommandLine: ansistring; Output, Errors: TStringList);
      var
        sa: TSECURITYATTRIBUTES;
        si: TSTARTUPINFO;
        pi: TPROCESSINFORMATION;
        hPipeOutputRead: THANDLE;
        hPipeOutputWrite: THANDLE;
        hPipeErrorsRead: THANDLE;
        hPipeErrorsWrite: THANDLE;
        Res, bTest: boolean;
        env: array[0..100] of char;
        szBuffer: array[0..256] of char;
        dwNumberOfBytesRead: DWORD;
        Stream: TMemoryStream;
      begin
        sa.nLength := sizeof(sa);
        sa.bInheritHandle := True;
        sa.lpSecurityDescriptor := nil;
        CreatePipe(hPipeOutputRead, hPipeOutputWrite, @sa, 0);
        CreatePipe(hPipeErrorsRead, hPipeErrorsWrite, @sa, 0);
        ZeroMemory(@env, SizeOf(env));
        ZeroMemory(@si, SizeOf(si));
        ZeroMemory(@pi, SizeOf(pi));
        si.cb := SizeOf(si);
        si.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
        si.wShowWindow := SW_HIDE;
        si.hStdInput := 0;
        si.hStdOutput := hPipeOutputWrite;
        si.hStdError := hPipeErrorsWrite;
    
      (* Remember that if you want to execute an app with no parameters you nil the
         second parameter and use the first, you can also leave it as is with no
         problems.                                                                 *)
        Res := CreateProcess(nil, PChar(CommandLine), nil, nil, True,
        CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, @env, nil, si, pi);
    
    
        // Procedure will exit if CreateProcess fail
        if not Res then
        begin
          CloseHandle(hPipeOutputRead);
          CloseHandle(hPipeOutputWrite);
          CloseHandle(hPipeErrorsRead);
          CloseHandle(hPipeErrorsWrite);
          Exit;
        end;
        CloseHandle(hPipeOutputWrite);
        CloseHandle(hPipeErrorsWrite);
    
        //Read output pipe
        Stream := TMemoryStream.Create;
        try
          while True do
          begin
            bTest := ReadFile(hPipeOutputRead, szBuffer, 256, dwNumberOfBytesRead, nil);
            if not bTest then
            begin
              break;
            end;
            OemToAnsi(szBuffer, szBuffer);
            Stream.Write(szBuffer, dwNumberOfBytesRead);
          end;
          Stream.Position := 0;
          Output.LoadFromStream(Stream);
        finally
          Stream.Free;
        end;
    
        //Read error pipe
        Stream := TMemoryStream.Create;
        try
          while True do
          begin
            bTest := ReadFile(hPipeErrorsRead, szBuffer, 256, dwNumberOfBytesRead, nil);
            if not bTest then
            begin
              break;
            end;
            OemToAnsi(szBuffer, szBuffer);
            Stream.Write(szBuffer, dwNumberOfBytesRead);
          end;
          Stream.Position := 0;
          Errors.LoadFromStream(Stream);
        finally
          Stream.Free;
        end;
    
        WaitForSingleObject(pi.hProcess, INFINITE);
        CloseHandle(pi.hProcess);
        CloseHandle(hPipeOutputRead);
        CloseHandle(hPipeErrorsRead);
      end;
    
    
      cmdstring :=
        'cmd.exe /c "' + GetSystemPath(WindRoot) + 'system32\netsh.exe interface ' +
        ip + ' delete address "' + NetworkInterfaceName + '" ' + VirtualFailoverIPAddress + '"';
    
      logstr('cmdstring: ' + cmdstring);
      ExecConsoleApp(cmdstring, OutP, ErrorP);
    
      if OutP.Text <> '' then
      begin
        logstr('Delete IP Result: ' + OutP.Text);
      end
      else
      begin
        logstr('Delete IP Error: ' + ErrorP.Text);
      end;
    
  4. 尝试直接运行 netsh.exe 而不是“cmd.exe/c C:\Windows\system32\netsh.exe...”,得到同样的“系统找不到指定的文件”。错误。我还无意中发现,如果我发出错误的 netsh 命令,netsh 实际上会返回一个错误,例如

netsh interface ip delete address "LocalArea Connection"10.40.201.65

Invalid interface LocalArea Connection specified.

如果我将拼写错误“LocalArea”更正为“Local Area”,则会返回以下内容。 netsh接口(interface)ip删除地址“本地连接”10.40.201.65

The system cannot find the file specified.

我必须再次强调,如果我通过命令提示符而不是从我的应用程序发出相同的命令,它会工作得很好。

最佳答案

你试过吗?

if not CreateProcess(PChar('C:\Windows\system32\netsh.exe'), PChar(Arguments), ...) then
begin
  // Do somehting with `GetLastError`
end;

当然最好在运行时检测C:\Windows\system32 的路径,因为这可能在另一个驱动程序或另一个目录中。

当您以这种方式运行它时,您可以在 CreateProcess 之后立即使用 GetLastError 调用从 Windows 获取错误消息。

ExecConsoleApp 过程是有缺陷的,因为它没有返回 GetLastError,甚至没有任何指示 CreateProcess 失败。

你应该先解决这个问题。也许在代码的 Exit 之前添加 raise EExecConsoleAppCreateProcessFailed.Create(SysErrorMessage(GetLastError))

您不应使用 cmd.exe/c 作为前缀。它是多余的,并且使错误诊断更加困难。 GetLastError 可能不会反射(reflect)正确的错误代码,因为您将创建实际 netsh.exe 进程委托(delegate)给 cmd

关于windows - 当我从 CreateProcess 运行 NETSH 时得到 "The system cannot find the file specified"但它在命令提示符下工作正常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8166840/

相关文章:

delphi - 来自 TCustomWinSocket 的 ReceiveBuf 不适用于缓冲区的动态数组

delphi - 如何在循环中定期更改 TLabel 标题?

debugging - 如何使用windbg调试子进程和父进程?

c - 获取由 CreateProcess() 启动的进程的 PID

windows - PowerShell:使用 Invoke-Command 执行命令时出错?

C++:在WINDOWS上写入BMP图像格式错误

windows - 我可以确定我的单元的初始化顺序吗?

windows - 我在哪里可以了解可用的各种源代码控制系统的优缺点?

Delphi - 可以indy TIdTCPClient同时发送/接收大流

winapi - GetCommandLine() 中缺少进程名称