【发布时间】:2014-03-25 09:33:18
【问题描述】:
背景
我们需要从 Windows 服务运行 GUI 应用程序,设置为以本地系统登录(并且不启用与桌面交互)。
GUI 应用程序采用一个命令行参数,执行特定任务,然后自行终止。它是一个 GUI 应用程序,因为它的某些组件需要父 TForm,因此控制台应用程序不起作用。用户不会看到任何对话框或任何 UI。事实上,它会将自己创建为一个没有任务栏图标的隐藏表单:
Application.Initialize;
Application.MainFormOnTaskbar := False; // <- No taskbar icon
Application.ShowMainForm := False; // <- Main form is hidden
Application.CreateForm(TForm1, Form1);
Application.Run;
GUI 应用程序可能会同时启动多次,每次都有自己的命令行参数。由于不能在服务的 Session 0 进程中直接生成 GUI 应用程序,因此我创建了一个 Administrator 用户帐户,以便服务可以登录 admin 用户并运行GUI 应用程序作为管理员用户。一旦我让它工作一次,我会让这个用户保持登录状态,这样服务就可以快速启动 GUI 应用程序,而无需每次生成 GUI 应用程序时的登录/注销开销。
我做了什么
我正在使用下面的代码,这些代码是由关于这个主题的几十个讨论组成的,尽管他们中的大多数人都希望登录的用户可以看到 GUI 应用程序。
function CreateEnvironmentBlock(var lpEnvironment: Pointer; hToken: THandle; bInherit: BOOL): BOOL; stdcall; external 'userenv.dll';
function DestroyEnvironmentBlock(lpEnvironment: Pointer): BOOL; stdcall; external 'userenv.dll';
var
_usertoken: THandle;
_si: _STARTUPINFOW;
_pi: _PROCESS_INFORMATION;
_env: Pointer;
_sid: Cardinal;
begin
if LogonUser(PChar(Username), PChar('localhost'), PChar(Password), LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, _usertoken) then
try
ZeroMemory(@_si, SizeOf(_si));
_si.cb := SizeOf(_si);
// _si.lpDesktop := 'WinSta0\Default'; // <- behaves the same with or without this
if CreateEnvironmentBlock(_env, _usertoken, False) then
try
if CreateProcessAsUser(_usertoken, nil, PChar(sCMD), nil, nil, False, CREATE_UNICODE_ENVIRONMENT, _env, nil, _si, _pi) then
begin
WaitForSingleObject(_pi.hProcess, 30000);
CloseHandle(_pi.hThread);
CloseHandle(_pi.hProcess);
end
else
_handle_error('CreateProcessAsUser() failed.');
finally
DestroyEnvironmentBlock(_env);
end
else
_handle_error('CreateEnvironmentBlock() failed.');
finally
CloseHandle(_usertoken);
end
else
_handle_error('LogonUser() failed.');
end;
当调用LogonUser() 时,Windows 事件查看器 [安全日志] 会显示一个条目。以下权限出现在日志条目中:
SeTcbPrivilege
SeSecurityPrivilege
SeTakeOwnershipPrivilege
SeLoadDriverPrivilege
SeBackupPrivilege
SeRestorePrivilege
SeDebugPrivilege
SeSystemEnvironmentPrivilege
SeImpersonatePrivilege
sCmd 设置为 "c:\path\myapp.exe" "参数"。当sCmd 未 正确设置时,CreateProcessAsUser() 将失败并出现错误 2 - 系统找不到指定的文件。一旦我解决了这个问题,CreateProcessAsUser()返回 True,但它从未真正启动 GUI 应用程序。
问题
我不确定我错过了什么。如果这是正确的方法,我将不胜感激让服务在登录的用户名/密码配置文件下启动 GUI 应用程序。或者,如果有更好的方法,我将不胜感激任何方向和见解。
【问题讨论】:
-
不要尝试从服务运行 GUI 应用程序。
-
除了大卫所说的之外,我还质疑组件需要 TForm 所有者。如果它们是非 GUI 的,您应该能够将 Nil 传递给构造函数,如果它们是 GUI,它们一开始就不应该出现在应用程序中。
-
你真的看错了。如果您编写的代码需要
TForm所有者的非可视代码,那么您只需要修复它。消除不必要的限制。 -
@DavidHeffernan - 我同意这不是一个好主意。但是该过程需要在服务器端运行。我们尝试将其构建为控制台应用程序,但第 3 方组件之一返回错误
Control has no parent window.我很想将其作为控制台应用程序运行,但由于 没有父窗口,它需要是 GUI 应用程序 问题。 -
有些事情是不可能的。您是否考虑过您可能无法在会话 0 中创建 GUI 的事实?你为什么要尝试将 GUI 放入会话 0 中?当您尝试这样做时,为什么要禁用“允许服务与桌面交互”?您想在服务中添加什么可视化组件?
标签: delphi user-interface windows-services windows-server-2008-r2 external-process