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

发布于 2024-12-15 20:58:58 字数 4876 浏览 0 评论 0原文

我有一个 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 code> 从 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. 代码片段:

    过程 ExecConsoleApp(CommandLine: ansistring; 输出,错误:TStringList);
      变量
        sa:T安全属性;
        si:TSTARTUPINFO;
        pi:T进程信息;
        hPipeOutputRead:THANDLE;
        hPipeOutputWrite:THANDLE;
        hPipeErrorsRead:THANDLE;
        hPipeErrorsWrite:THANDLE;
        Res,bTest:布尔值;
        env: 字符数组[0..100];
        szBuffer:字符数组[0..256];
        dwNumberOfBytesRead:DWORD;
        流:TMemoryStream;
      开始
        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 或 STARTF_USESTDHANDLES;
        si.wShowWindow := SW_HIDE;
        si.hStdInput := 0;
        si.hStdOutput := hPipeOutputWrite;
        si.hStdError := hPipeErrorsWrite;
    
      (* 请记住,如果您想执行不带参数的应用程序,则将
         第二个参数并使用第一个,您也可以保持原样,不使用
         问题。 *)
        Res := CreateProcess(nil, PChar(CommandLine), nil, nil, True,
        CREATE_NEW_CONSOLE 或 NORMAL_PRIORITY_CLASS, @env, nil, si, pi);
    
    
        // 如果CreateProcess失败,程序将退出
        如果不是 Res 那么
        开始
          CloseHandle(hPipeOutputRead);
          CloseHandle(hPipeOutputWrite);
          CloseHandle(hPipeErrorsRead);
          CloseHandle(hPipeErrorsWrite);
          出口;
        结尾;
        CloseHandle(hPipeOutputWrite);
        CloseHandle(hPipeErrorsWrite);
    
        //读取输出管道
        流 := TMemoryStream.Create;
        尝试
          而 True 则做
          开始
            bTest := ReadFile(hPipeOutputRead, szBuffer, 256, dwNumberOfBytesRead, nil);
            如果不是 bTest 那么
            开始
              休息;
            结尾;
            OemToAnsi(szBuffer, szBuffer);
            Stream.Write(szBuffer, dwNumberOfBytesRead);
          结尾;
          流位置 := 0;
          Output.LoadFromStream(Stream);
        最后
          流。免费;
        结尾;
    
        //读取错误管道
        流 := TMemoryStream.Create;
        尝试
          而 True 则做
          开始
            bTest := ReadFile(hPipeErrorsRead, szBuffer, 256, dwNumberOfBytesRead, nil);
            如果不是 bTest 那么
            开始
              休息;
            结尾;
            OemToAnsi(szBuffer, szBuffer);
            Stream.Write(szBuffer, dwNumberOfBytesRead);
          结尾;
          流位置 := 0;
          Errors.LoadFromStream(Stream);
        最后
          流。免费;
        结尾;
    
        WaitForSingleObject(pi.hProcess, INFINITE);
        关闭句柄(pi.hProcess);
        CloseHandle(hPipeOutputRead);
        CloseHandle(hPipeErrorsRead);
      结尾;
    
    
      cmd字符串:=
        'cmd.exe /c "' + GetSystemPath(WindRoot) + 'system32\netsh.exe 接口 ' +
        ip + ' 删除地址 "' + NetworkInterfaceName + '" ' + VirtualFailoverIPAddress + '"';
    
      logstr('cmdstring:' + cmdstring);
      ExecConsoleApp(cmdstring, OutP, ErrorP);
    
      如果 OutP.Text <> '' 然后
      开始
        logstr('删除 IP 结果:' + OutP.Text);
      结尾
      别的
      开始
        logstr('删除 IP 错误:' + ErrorP.Text);
      结尾;
    
  4. 尝试直接运行netsh.exe而不是“cmd.exe /c C:\Windows\system32\netsh.exe...”,并得到相同的“The system找不到指定的文件。”错误。我还意外地发现,如果我发出错误的netsh命令,netsh实际上会返回一个错误,例如

netsh接口ip删除地址“LocalArea Connection”10.40.201.65

指定的 LocalArea Connection 接口无效。

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

系统找不到指定的文件。

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

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

蓬勃野心 2024-12-22 20:58:58

运行 netsh interface portproxy delete v4tov4 ... 时,如果我尝试删除的内容不存在,则会收到此错误。如果我尝试执行 netsh interface portproxy delete v4tov4 all,我也会收到此错误。然后,唯一对存在的项目有效的是,如果我指定两者端口和地址:netsh interface portproxy delete v4tov4listenaddress=0.0.0.0listenport=80。如果我只使用listenaddress或仅使用listenport,我会收到错误系统找不到指定的文件。即使该项目存在。使用进程监视器,如果您使用除这两个参数之外的任何其他参数,netsh.exe 似乎会将无效参数传递给 RegDeleteValue Win32 API。

When running netsh interface portproxy delete v4tov4 ... I get this error if the thing I'm trying to delete does not exist. I also get this error if I try to do netsh interface portproxy delete v4tov4 all. Then only thing works for items that are present is if I specify both the port and the address: netsh interface portproxy delete v4tov4 listenaddress=0.0.0.0 listenport=80. If I just use the listenaddress or just the listenport I get the error The system cannot find the file specified. even though the item is present. Using process monitor it appears netsh.exe will pass invalid parameter to RegDeleteValue Win32 API if you use anything else but both those parameters.

你试过这个吗?

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 可能无法反映正确的错误代码,因为您将实际 netsh.exe 进程的创建委托给 cmd

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.

亚希 2024-12-22 20:58:58

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

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

The "cannot find the file specified" error may also occur if an implicitly loaded DLL required by the executable is not available. In this situation, that is the most likely cause - some essential DLL is not being found when netsh.exe is being run in a non-interactive context.

Use Process Monitor (available for download from Microsoft's web site) to record the file system operations that are taking place during the attempt. Look for file not found errors either in the context of your service process or in the context of the netsh.exe process.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文