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

我已经研究了一天,但没有任何线索,希望有人可以提供帮助。谢谢。

附加说明-
  • 服务器是32位Win2k3。
  • 尝试过的域管理员,无法正常工作。
  • 代码段:
    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;
    
  • 尝试直接运行netsh.exe而不是“cmd.exe/c C:\Windows\system32\netsh.exe ...”,并得到相同的“系统找不到指定的文件”。错误。我还意外发现,如果我发出错误的netsh命令,netsh实际上会返回错误,例如

  • netsh接口(interface)ip删除地址“LocalArea连接” 10.40.201.65



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



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

    最佳答案

    你有试过吗?

    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可能无法反射(reflect)正确的错误代码,因为您将将手工netsh.exe进程的创建委派给cmd

    09-13 06:32