У меня есть служба 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);
Я исследовал в течение дня, но никаких подсказок, надеюсь, кто-то может помочь. Спасибо.
Дополнительные замечания -
На сервере 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 на самом деле вернет ошибку, например.
IP-адрес интерфейса netsh для удаления «Подключение по локальной сети» 10.40.201.65
Указан недопустимый интерфейс LocalArea Connection.
Следующее возвращается, если я исправлю опечатку «LocalArea» на «Локальная область». IP-адрес интерфейса netsh для удаления «Подключение по локальной сети» 10.40.201.65
Система не может найти указанный файл.
Опять же, я должен повторить, что та же команда отлично работает, если я ввожу ее через командную строку, а не из своего приложения.
si
? Что такоеWindRoot
? Что такоеCommandLine
? Безусловно, лучше всего предоставить полное консольное приложение в виде одного файла .dpr, воспроизводящего вашу проблему. Как только вы это сделаете, я гарантирую, что мы сможем решить вашу проблему в два раза быстрее. - person David Heffernan   schedule 18.11.2011