LogonUser + CreateProcessAsUser at Service =错误1314

时间:2012-10-30 18:42:11

标签: delphi

我有一个用Delphi 7创建的Windows服务,StartType = stSystem。

现在我需要启动一个应用程序来为我做一些事情。 此应用程序具有MainForm和其他GDI资源。 传递给应用程序的参数为某些控件(如编辑和备忘录)分配值,然后单击按钮....

我正在尝试这个:

var
  token: cardinal;
  si: TStartupInfo;
  pi: TProcessInformation;
begin
  if not LogonUser('admintest', '', 'secret123', LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, token) then
    RaiseLastOSError;

  try
    if not ImpersonateLoggedOnUser(token) then
      RaiseLastOSError;

    fillchar(si, sizeof(si), 0);
    si.cb := sizeof(si);
    si.lpDesktop := PChar('winsta0\default');
    if not CreateProcessAsUser(token, nil, '"c:\...\myapp.exe" -doCrazyThings', nil, nil, false, NORMAL_PRIORITY_CLASS or CREATE_NEW_CONSOLE, nil, nil, si, pi) then
      RaiseLastOSError;

    CloseHandle(pi.hThread);
    waitForSingleObject(pi.hProcess, INFINITE);
    CloseHandle(pi.hProcess);
  finally
    CLoseHandle(token);
  end;
end;

当我将服务可执行文件作为普通应用程序(-noservice)运行时,它作为Forms.Application启动,并使用“开始”按钮创建一个MainForm。 * 该按钮运行与服务运行相同的代码,但它不起作用,它在createprocessasuser上搜索错误代码1314。 *

为什么呢? SYSTEM服务与管理员启动的普通应用程序之间有什么区别?

我的环境是Windows 7 Pro x64

我做错了什么? 我怎么解决这个问题? 有人可以发一个例子吗?

4 个答案:

答案 0 :(得分:3)

错误1314是ERROR_PRIVILEGE_NOT_HELD,这意味着您的调用线程缺少运行CreateProcessAsUser()所需的权限。您不需要也不应该冒充用户令牌以在用户桌面上启动新进程。在调用CreateProcessAsUser()时,您应该让线程使用服务的凭据而不是用户的凭据。它将确保新进程在您的用户帐户和桌面内运行,因此请取消对ImpersonateLoggedOnUser()的调用,并查看CreateProcessAsUser()是否开始工作。

更新read the documentation

  

通常,调用CreateProcessAsUser函数的进程必须具有SE_INCREASE_QUOTA_NAME权限,并且如果令牌不可分配,则可能需要SE_ASSIGNPRIMARYTOKEN_NAME权限。如果此函数因ERROR_PRIVILEGE_NOT_HELD(1314)而失败,请改用CreateProcessWithLogonW函数。 CreateProcessWithLogonW不需要特殊权限,但必须允许指定的用户帐户以交互方式登录。通常,最好使用CreateProcessWithLogonW来创建具有备用凭据的进程。

答案 1 :(得分:1)

在Vista及更高版本中,Windows服务在会话0中运行。交互式用户存在于会话1及更高版本中。这意味着Windows服务无法显示用户界面,实际上无法在交互式会话中轻松启动进程。

现在,有launch interactive processes from a service的方法。如果您已经开始从服务启动交互式流程,那么该文章将告诉您需要了解的所有信息。但是这种技术非常棘手,绝对不值得推荐。建议您在服务和交互式桌面之间找到​​不同的通信方式。

正常的方法是运行标准桌面应用程序,可能是使用HKLM\Software\Microsoft\Windows\CurrentVersion\Run开始的。并使用某种形式的IPC在桌面应用程序和服务之间进行通信。

答案 2 :(得分:1)

您应该创建服务来执行后台工作,GUI应用程序应该只调用该服务。 IE总是有服务运行。

考虑使用DataSnap的东西作为后端。在Delphi中,MVC方法并不像在其他语言中那样纯粹。控制器可以方便地到达任何地方。数据集主要是折衷方案,唯一真正快速的数据处理方式是使用DBexpress和客户端上的组件,以保证数据的快乐。但它有效,值得学习。

服务无法控制。没有TForm后代允许。仅限TService。 Delphi项目/服务应用程序下的新项目。您获得的单元/表单项目与数据模块几乎相同。即,不允许视觉控制。原因很明显。您需要学习记录,对象设计等以使用服务。 Datasnap是你最好的选择。它为你做了大部分艰苦的工作。

博士。对于XE2 / 3,鲍勃有一本非常好的书。如果您不熟悉面向对象设计,那么首先需要彻底了解它。

答案 3 :(得分:0)

这是我用来做这种事情的代码

procedure CreateNewProcess;
var
  CmdLine: AnsiString;
  ErrorCode: Cardinal;
  ConnSessID: Cardinal;
  Token: Cardinal;
  App: AnsiString;
  FProcessInfo: _PROCESS_INFORMATION;
  FStartupInfo: _STARTUPINFOA;
begin
  ZeroMemory(@FStartupInfo, SizeOf(FStartupInfo));
  FStartupInfo.cb := SizeOf(FStartupInfo);
  FStartupInfo.lpDesktop := nil;

  ConnSessID := WTSGetActiveConsoleSessionId;

  if WTSQueryUserToken(ConnSessID, Token) then
  begin
    if CreateProcessAsUser(Token, PAnsiChar(App), PAnsiChar(CmdLine),
      nil, nil, false, 0, nil, nil, FStartupInfo, FProcessInfo) = False
    then
    begin
      ErrorCode := GetLastError;
      try
        RaiseLastOSError(ErrorCode);
      except on E: Exception do
        EventLog.LogError(e.ClassName +': '+ e.Message);
      end;
    end;
  end;
end;

希望这有帮助

如果您希望服务在继续添加此

之前等待新进程终止
    if CreateProcessAsUser(Token, PAnsiChar(App), PAnsiChar(CmdLine),
      nil, nil, false, 0, nil, nil, FStartupInfo, FProcessInfo) = False
    then
    begin
      ErrorCode := GetLastError;
      try
        RaiseLastOSError(ErrorCode);
      except on E: Exception do
        EventLog.LogError(e.ClassName +': '+ e.Message);
      end;
    end
    else
      WaitForSingleObject(FProcessInfo.hProcess, INFINITE);

虽然无限等待可能不可取。

相关问题