我有一个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);
我已经研究了一天,但没有任何线索,希望有人可以提供帮助。谢谢。
附加说明-
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接口(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
。