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

问题描述 投票:0回答:3

我有一个

NT
服务,它调用用 Delphi 7 编写的控制台程序,我们称之为
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
将返回错误:

系统找不到指定的文件

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

奇怪的是,我记得它在 2003 年服务器上第一次尝试时工作正常,但之后,无论我尝试多少次,它都失败了。在其中一次尝试中,我还尝试将管理员用户登录分配给该服务,但没有成功。摆弄文件安全也没有帮助。

我没有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);

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

补充说明 -

  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 etsh.exe...”,并得到同样的“系统找不到指定的文件。”错误。我还意外地发现,如果我发出错误的 netsh 命令,netsh 实际上会返回错误,例如

netsh接口ip删除地址“本地连接”10.40.201.65

指定的本地连接接口无效。

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

系统找不到指定的文件。

再次,我必须重复,如果我通过命令提示符而不是从我的应用程序发出相同的命令,它可以完美地工作。

windows delphi createprocess netsh
3个回答
2
投票

你尝试过这个吗?

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
失败的任何指示。

你应该先解决这个问题。也许在代码中的

raise EExecConsoleAppCreateProcessFailed.Create(SysErrorMessage(GetLastError))
之前添加
Exit

您不应使用

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


0
投票

如果可执行文件所需的隐式加载的 DLL 不可用,也可能会出现“找不到指定的文件”错误。在这种情况下,这是最可能的原因 - 当 netsh.exe 在非交互式上下文中运行时,未找到一些基本的 DLL。

使用进程监视器(可从 Microsoft 网站下载)记录尝试期间发生的文件系统操作。在服务进程的上下文中或 netsh.exe 进程的上下文中查找文件未找到错误。


0
投票

运行时

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

© www.soinside.com 2019 - 2024. All rights reserved.