且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

得到“系统找不到指定的文件”当我从CreateProcess运行NETSH,但它可以在命令提示符上运行吗?

更新时间:2023-01-19 13:27:00

您是否尝试过?

  if不是CreateProcess(PChar('C:\Windows\system32\\\
etsh.exe'),PChar(Arguments),...)然后
begin
//使用`GetLastError`
结束

当然***检测 C:\ Windows \system32 在运行时,因为这可能是在另一个驱动程序或在另一个目录。



当你运行这样你可以得到一个来自Windows的错误消息使用CreateProcess之后的 GetLastError 调用。



ExecConsoleApp 程序是有缺陷的,因为它不返回 GetLastError 或甚至指示 CreateProcess 失败。



您应该首先解决这个问题。可能添加退出之前,将代码中的EExecConsoleAppCreateProcessFailed.Create(SysErrorMessage(GetLastError))添加到



您不应该使用 cmd.exe / c 作为前缀。它是多余的,它使错误诊断更加困难。 GetLastError 可能不会反映出正确的错误代码,因为您正在将创建的 netsh.exe 进程委派给 cmd


I have an NT service that calls a console program written in Delphi 7, let's call it failover.exe that in turn calls NETSH using a procedure I found:

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

Note: ExecConsoleApp uses CreateProcess, see the following link for full code: http://www.delphisources.ru/pages/faq/base/createprocess_console.html

I would pass the following to CommandLine before calling ExecConsoleApp:

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

ExecConsoleApp will return an error:

The system cannot find the file specified

But if I were to run it in Command Prompt, it runs perfectly.

The strange thing is that I remembered it working on the first attempt on that 2003 Server, but after that, it failed regardless of the number of times I tried. In one of the attempt, I've also tried assigning logon as administrator user to the service but to no avail. Neither does fiddling with file security help.

I don't have a Win 2003 server to test with in office, but I have tested it on XP and Win7 and ExecConsoleApp works perfectly, although on XP, I had to amend ExecConsoleApp to execute from system32\wbem in order for it work work:

 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);

I've researched for a day but no clues, hope someone can help. Thanks.

Additional remarks -

  1. Server is 32 bit Win2k3.

  2. Tried domain administrator, doesn't work.

  3. Code snippets:

    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. Tried running netsh.exe directly instead of "cmd.exe /c C:\Windows\system32\netsh.exe...", and got the same "The system cannot find the file specified." error. I also accidentally discovered that if I were to issue a wrong netsh command, netsh will actually return an error, e.g.

netsh interface ip delete address "LocalArea Connection" 10.40.201.65

Invalid interface LocalArea Connection specified.

The following is returned if i correct the typo "LocalArea" to "Local Area". netsh interface ip delete address "Local Area Connection" 10.40.201.65

The system cannot find the file specified.

Again, I must repeat that the same command works perfectly fine if I issue it via Command Prompt instead of from my application.

Have you tried this?

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

Of course it would be better to detect the path of C:\Windows\system32 at runtime as this could be on another driver or in another directory.

When you run it this way you can get an error message from Windows using the GetLastError call right after CreateProcess.

The ExecConsoleApp procedure is flawed, because it doesn't return the GetLastError or even any indication that CreateProcess failed.

You should fix this first. Maybe add raise EExecConsoleAppCreateProcessFailed.Create(SysErrorMessage(GetLastError)) before Exit to the code.

You shouldn't use cmd.exe /c as a prefix. It's redundant and it makes error diagnostics more difficult. GetLastError might not reflect the correct error code, because you're delegating the creation of the acutal netsh.exe process to cmd.