当我从CreateProcess运行NETSH时,"系统找不到指定的文件",但它在命令提示符下运行正常吗?

Jos*_*hua 5 windows delphi createprocess netsh

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

procedure ExecConsoleApp(CommandLine: ansistring; Output, Errors: TStringList); 
Run Code Online (Sandbox Code Playgroud)

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

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

cmd.exe /c "C:\Windows\system32\netsh.exe interface delete address "Wireless Network Connection" 192.168.0.36" 
Run Code Online (Sandbox Code Playgroud)

ExecConsoleApp 将返回错误:

该系统找不到指定的文件

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

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

我没有Win 2003服务器在办公室进行测试,但我已经在XP和Win7上测试过它并且ExecConsoleApp工作得很好,虽然在XP上,我不得不修改ExecConsoleApp执行system32\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);
Run Code Online (Sandbox Code Playgroud)

我研究了一天,但没有线索,希望有人可以提供帮助.谢谢.

补充评论 -

  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;
    
    Run Code Online (Sandbox Code Playgroud)
  4. 尝试直接运行netsh.exe而不是"cmd.exe/c C:\ Windows\system32 \netsh.exe ...",并得到相同的"系统找不到指定的文件".错误.我还意外地发现,如果我发出错误的netsh命令,netsh实际上会返回错误,例如

netsh interface ip delete address"LocalArea Connection"10.40.201.65

指定的接口LocalArea连接无效.

如果我将拼写错误"LocalArea"更正为"Local Area",则返回以下内容.netsh interface ip delete address"Local Area Connection"10.40.201.65

该系统找不到指定的文件.

同样,我必须重复,如果我通过命令提示符而不是从我的应用程序发出它,相同的命令工作得很好.

小智 5

运行时,netsh interface portproxy delete v4tov4 ...如果我尝试删除的内容不存在,则会出现此错误。如果我尝试这样做,我也会收到此错误netsh interface portproxy delete v4tov4 all。那么,唯一适用于存在的项目的是,如果我同时指定端口和地址:netsh interface portproxy delete v4tov4 listenaddress=0.0.0.0 listenport=80。如果我只使用 thelistenaddress或仅使用 the,即使该项目存在,listenport我也会收到错误。The system cannot find the file specified.使用进程监视器,如果您使用除这两个参数之外的任何其他参数,它似乎netsh.exe会将无效参数传递给Win32 API。RegDeleteValue


Jen*_*off 2

你试过这个吗?

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

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

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

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

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

您不应该用作cmd.exe /c前缀。它是多余的,并且使错误诊断变得更加困难。可能无法反映正确的错误代码,因为您将实际进程GetLastError的创建委托给.netsh.execmd