6

我有一个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

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

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

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

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

补充说明——

  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

指定的接口 LocalArea Connection 无效。

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

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

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

4

2 回答 2

2

你试过这个吗?

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

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

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

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

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

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

于 2011-11-17T13:01:05.597 回答
0

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

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

于 2011-11-19T05:14:17.700 回答