Delphi LogonUser+;服务处的CreateProcessAsUser=错误1314

Delphi LogonUser+;服务处的CreateProcessAsUser=错误1314,delphi,Delphi,我有一个用Delphi7创建的windows服务,StartType=stSystem 现在我需要启动一个应用程序来为我制作一些东西。 此应用程序具有MainForm和其他GDI资源。 传递给应用程序的参数为某些控件(如编辑和备忘录)指定值,然后单击按钮 我正在尝试这个: var token: cardinal; si: TStartupInfo; pi: TProcessInformation; begin if not LogonUser('admintest', '', '

我有一个用Delphi7创建的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启动,并使用“Start”按钮创建一个MainForm。 *该按钮运行的代码与服务运行的代码相同,但不起作用,并且在createprocessasuser上生成了eror代码1314*

为什么?系统服务和管理员启动的普通应用程序之间有什么区别?

我的环境是Windows 7 Pro x64

我做错了什么? 我怎样才能解决这个问题?
是否有人可以发布示例?

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

现在,有很多方法可以解决这个问题。如果您死心塌地地想从您的服务启动一个交互式流程,那么这篇文章会告诉您所有需要知道的事情。但这样的技术非常棘手,绝对不值得推荐。建议您找到一种不同的方式在服务和交互式桌面之间进行通信


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

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

现在,有很多方法可以解决这个问题。如果您死心塌地地想从您的服务启动一个交互式流程,那么这篇文章会告诉您所有需要知道的事情。但这样的技术非常棘手,绝对不值得推荐。建议您找到一种不同的方式在服务和交互式桌面之间进行通信


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

错误1314是
错误权限\u未持有
,这意味着您的调用线程缺少运行
CreateProcessAsUser()
所需的权限。您不需要也不应该为了在用户桌面上启动新进程而模拟用户令牌。调用
CreateProcessAsUser()
时,应该让线程使用服务的凭据,而不是用户的凭据。它将确保新进程在用户的帐户和桌面内为您运行,因此请不要调用
ImpersonalLoggeDonUser()
,并查看
CreateProcessAsUser()
是否开始工作

更新

通常,调用CreateProcessAsUser函数的进程必须具有SE_增加\ u配额\ u名称权限,如果令牌不可分配,则可能需要SE_分配PrimaryToken \ u名称权限。如果此函数失败并出现错误\u PRIVILEGE\u NOT \u hold(1314),请改用CreateProcessWithLogonW函数。CreateProcessWithLogonW不需要特殊权限,但必须允许指定的用户帐户以交互方式登录。通常,最好使用CreateProcessWithLogonW创建具有备用凭据的进程


错误1314是
Error\u PRIVILEGE\u NOT \u hold
,这意味着您的调用线程缺少运行
CreateProcessAsUser()
所需的权限。您不需要也不应该为了在用户桌面上启动新进程而模拟用户令牌。调用
CreateProcessAsUser()
时,应该让线程使用服务的凭据,而不是用户的凭据。它将确保新进程在用户的帐户和桌面内为您运行,因此请不要调用
ImpersonalLoggeDonUser()
,并查看
CreateProcessAsUser()
是否开始工作

更新

通常,调用CreateProcessAsUser函数的进程必须具有SE_增加\ u配额\ u名称权限,如果令牌不可分配,则可能需要SE_分配PrimaryToken \ u名称权限。如果此函数失败并出现错误\u PRIVILEGE\u NOT \u hold(1314),请改用CreateProcessWithLogonW函数。CreateProcessWithLogonW不需要特殊权限,但必须允许指定的用户帐户以交互方式登录。通常,最好使用CreateProcessWithLogonW创建具有备用凭据的进程


您应该创建服务来做后台工作,GUI应用程序应该只调用该服务。你总是有一个服务在运行

考虑在后端使用DataSnap。MVC方法在Delphi中不像在其他语言中那样纯粹。控制器可以去任何方便的地方。数据集基本上是一种折衷方案,唯一真正快速处理数据的方法是使用DBexpress和客户端上的一系列组件,以保持数据的完整性。但它是有效的,值得学习

服务不能有gui控件。不允许使用TForm后代。只提供服务。Delphi项目/服务应用程序下的新项目。您将得到一个具有与数据模块几乎相同的单元/表单的项目。也就是说,不允许使用视觉控制。原因很明显。要使用服务,您需要学习记录、对象设计等。Datasnap是您最好的选择。是的
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);