У меня есть 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 фактически выдаст ошибку, например,
netsh interface ip delete address "Подключение по локальной сети" 10.40.201.65
Указан неверный интерфейс LocalArea Connection.
Следующее возвращается, если я исправляю опечатку "LocalArea" в "Local Area".
netsh interface ip delete address "Подключение по локальной сети" 10.40.201.65
Система не может найти указанный файл.
Опять же, я должен повторить, что та же команда прекрасно работает, если я запускаю ее через командную строку, а не из моего приложения.