1

StartType = stSystem で、Delphi 7 で作成された Windows サービスがあります。

ここで、何かを作成するためにアプリケーションを起動する必要があります。このアプリケーションには、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

4 に答える 4

3

エラー 1314 はERROR_PRIVILEGE_NOT_HELD、呼び出しスレッドに実行に必要な権限がないことを意味しますCreateProcessAsUser()。ユーザーのデスクトップで新しいプロセスを起動するために、ユーザー トークンを偽装する必要はありません。を呼び出すときに、ユーザーの資格情報ではなく、サービスの資格情報をスレッドに使用させる必要がありますCreateProcessAsUser()。新しいプロセスがユーザーのアカウントとデスクトップ内で実行されることを確認するため、への呼び出しを取り除き、動作するかどうかを確認しImpersonateLoggedOnUser()ますCreateProcessAsUser()

更新ドキュメントを読んでください

通常、CreateProcessAsUser 関数を呼び出すプロセスには SE_INCREASE_QUOTA_NAME 権限が必要であり、トークンが割り当て可能でない場合は SE_ASSIGNPRIMARYTOKEN_NAME 権限が必要になる場合があります。この関数が ERROR_PRIVILEGE_NOT_HELD (1314) で失敗した場合は、代わりに CreateProcessWithLogonW 関数を使用してください。CreateProcessWithLogonW には特別な権限は必要ありませんが、指定されたユーザー アカウントは対話的にログオンできる必要があります。一般に、CreateProcessWithLogonW を使用して、別の資格情報でプロセスを作成することをお勧めします。

于 2012-10-30T18:56:38.163 に答える
1

バックグラウンド作業を行うサービスを作成する必要があり、GUI アプリケーションはサービスを呼び出すだけである必要があります。つまり、常にサービスが実行されています。

バックエンドに DataSnap を使用することを検討してください。Delphi の MVC アプローチは、他の言語のように純粋ではありません。コントローラーは便利な場所に移動します。データセットはほとんどが妥協であり、データを処理する唯一の本当に高速な方法は、DBexpress を使用し、クライアント上でコンポーネントを叩いてすべてを満足させることです。しかし、それは機能し、学ぶ価値があります。

サービスは GUI コントロールを持つことができません。TForm の子孫は許可されません。T サービスのみ。Delphi Projects / Service Application の下の新しいプロジェクト。データモジュールとほとんど同じユニット/フォームを持つプロジェクトを取得します。つまり、視覚的なコントロールは許可されていません。理由は明らかです。サービスを利用するには、レコードやオブジェクト設計などを学ぶ必要があります。Datasnap はそのための最善の策です。それはあなたのために大変な作業のほとんどを行います。

Dr. Bob は、XE2/3 に関する非常に優れた本を出版しています。オブジェクト指向設計に慣れていない場合は、最初にそれを徹底的に学ぶ必要があります。

于 2012-10-30T20:00:38.827 に答える
1

Vista 以降では、Windows サービスはセッション 0 で実行されます。対話ユーザーはセッション 1 以降に存在します。これは、Windows サービスがユーザー インターフェイスを表示できず、インタラクティブ セッションでプロセスを簡単に開始できないことを意味します。

現在、サービスからインタラクティブなプロセスを起動する方法があります。サービスからインタラクティブなプロセスを起動することに固執している場合は、その記事で知っておくべきことがすべて説明されています。しかし、そのような手法は非常にトリッキーであり、絶対にお勧めできません。サービスと対話型デスクトップの間で通信する別の方法を見つけることをお勧めします。

通常のアプローチは、標準のデスクトップ アプリを実行することHKLM\Software\Microsoft\Windows\CurrentVersion\Runです。また、何らかの形式の IPC を使用して、デスクトップ アプリとサービスの間で通信します。

于 2012-10-30T18:46:23.983 に答える
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);

ただし、無限に待機することはおそらくお勧めできません。

于 2012-10-31T14:13:47.450 に答える