Получил «Система не может найти указанный файл», когда я запускаю NETSH из CreateProcess, но он работает нормально в командной строке? - PullRequest
5 голосов
/ 17 ноября 2011

У меня есть 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 сервере, но после этого он не работал независимо от того, сколько раз я пытался. В одной из попыток я также попытался назначить вход в систему как администратор для службы, но безрезультатно. Ни один из них не возится со справкой по безопасности файлов.

У меня нет сервера 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 "Подключение по локальной сети" 10.40.201.65

Указан неверный интерфейс LocalArea Connection.

Следующее возвращается, если я исправляю опечатку "LocalArea" в "Local Area". netsh interface ip delete address "Подключение по локальной сети" 10.40.201.65

Система не может найти указанный файл.

Опять же, я должен повторить, что та же команда прекрасно работает, если я запускаю ее через командную строку, а не из моего приложения.

Ответы [ 2 ]

2 голосов
/ 17 ноября 2011

Вы пробовали это?

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

Конечно, было бы лучше определить путь C:\Windows\system32 во время выполнения, поскольку это может быть на другом драйвере или в другом каталоге.

Когда вы запускаете его таким образом, вы можете получить сообщение об ошибке из Windows с помощью вызова GetLastError сразу после CreateProcess.

Процедура ExecConsoleApp имеет недостатки, поскольку она не возвращает GetLastError илидаже любой признак того, что CreateProcess не удалось.

Вы должны исправить это в первую очередь.Возможно добавьте raise EExecConsoleAppCreateProcessFailed.Create(SysErrorMessage(GetLastError)) перед Exit к коду.

Вы не должны использовать cmd.exe /c в качестве префикса.Это избыточно и делает диагностику ошибок более сложной.GetLastError может не отражать правильный код ошибки, поскольку вы делегируете создание точного netsh.exe процесса cmd.

0 голосов
/ 19 ноября 2011

Ошибка «не удается найти указанный файл» также может возникать, если неявно загруженная библиотека DLL, требуемая для исполняемого файла, недоступна.В этой ситуации это наиболее вероятная причина - некоторые важные DLL не обнаруживаются при запуске netsh.exe в неинтерактивном контексте.

Использование Process Monitor (доступно для загрузки с веб-сайта Microsoft)записывать операции файловой системы, которые происходят во время попытки.Ищите файлы не найдены ошибки в контексте процесса службы или в контексте процесса netsh.exe.

...