我有一个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:Windowssystem32netsh.exe interface delete address "Wireless Network Connection" 192.168.0.36"
ExecConsoleApp
将返回一个错误:
系统找不到指定的文件
但是如果我在命令提示符中运行它,它运行得很好。
奇怪的是,我记得它在2003年服务器上的第一次尝试工作,但在那之后,不管我尝试了多少次,它都失败了。在其中一次尝试中,我还尝试以管理员用户的身份登录该服务,但无济于事。摆弄文件安全也没有帮助。
我没有一个Win 2003服务器在办公室测试,但我已经在XP和Win7和ExecConsoleApp
上进行了测试,虽然在XP上,我不得不修改ExecConsoleApp
从system32wbem
执行,以便它工作:
Res := CreateProcess(nil, PChar(CommandLine), nil, nil, True,
// **** Attention: Amended by to point current directory to system32wbem, 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) + 'system32wbem'), 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) + 'system32netsh.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 system32netsh.exe…",并得到相同的"系统无法找到指定的文件"错误。我还意外地发现,如果我要发出一个错误的netsh命令,netsh实际上会返回一个错误,例如
netsh interface ip delete address "LocalArea Connection" 10.40.201.65
指定的LocalArea连接无效。
如果我将"LocalArea"更正为"LocalArea",则返回以下内容。删除"本地连接"10.40.201.65
系统找不到指定的文件。
再一次,我必须重复,如果我通过命令提示符而不是从我的应用程序发出相同的命令,则可以完全正常地工作。
你试过吗?
if not CreateProcess(PChar('C:Windowssystem32netsh.exe'), PChar(Arguments), ...) then
begin
// Do somehting with `GetLastError`
end;
当然最好在运行时检测C:Windowssystem32
的路径,因为它可能在另一个驱动程序或另一个目录中。
当你以这种方式运行它时,你可以在CreateProcess之后使用GetLastError
调用从Windows获得错误消息。
ExecConsoleApp
过程是有缺陷的,因为它没有返回GetLastError
,甚至没有任何CreateProcess
失败的迹象。
你应该先解决这个问题。可以在Exit
之前添加raise EExecConsoleAppCreateProcessFailed.Create(SysErrorMessage(GetLastError))
。
你不应该使用cmd.exe /c
作为前缀。这是冗余的,它使错误诊断更加困难。GetLastError
可能不会反映正确的错误代码,因为您将实际netsh.exe
进程的创建委托给了cmd
。
如果可执行文件所需的隐式加载的DLL不可用,也可能发生"cannot find The file specified"错误。在这种情况下,这是最有可能的原因-当netsh.exe在非交互式上下文中运行时,没有找到一些重要的DLL。
使用Process Monitor(可从Microsoft网站下载)记录在尝试过程中发生的文件系统操作。
当运行netsh interface portproxy delete v4tov4 ...
时,如果我试图删除的东西不存在,我会得到此错误。如果我尝试做netsh interface portproxy delete v4tov4 all
,我也会得到这个错误。然后只有的事情适用于存在的项目,如果我指定端口和地址:netsh interface portproxy delete v4tov4 listenaddress=0.0.0.0 listenport=80
。如果我只使用listenaddress
或listenport
,即使项目存在,我也会得到The system cannot find the file specified.
错误。使用进程监视器,似乎netsh.exe
将传递无效参数RegDeleteValue
Win32 API,如果你使用其他任何东西,但这两个参数。