2016-10-09 7 views
0

서비스를 작성하고 응용 프로그램을 (GUI를 사용하여) 실행하려고합니다. 그래서 나는 아래와 같은 절차를 쓴다. 내 응용 프로그램이 시작되지만 여전히 서비스 세션에 있습니다! 그래서 나는 그것이 GUI라고 볼 수 없다.
도움주세요.내 응용 프로그램이 콘솔 대신 서비스 세션으로 이동

Procedure RunAppFromService(Path, FileName: string); 
var 
    zPath   : array[0..512] of char; 
    zAppName   : array[0..512] of char; 
    StartupInfo  : TStartupInfo; 
    ProcessInfo  : TProcessInformation; 
begin { WinExecAndWait32V2 } 
    StrPCopy(zPath, Path); 
    StrPCopy(zAppName, FileName); 

    FillChar(StartupInfo, Sizeof(StartupInfo), #0); 
    StartupInfo.cb := Sizeof(StartupInfo); 
    StartupInfo.lpDesktop := PChar('winsta0\Default'); 
    StartupInfo.dwFlags := STARTF_USESHOWWINDOW; 
    StartupInfo.wShowWindow := Visibility; 

    FillChar(ProcessInfo, Sizeof(ProcessInfo), #0); 

    CreateProcessAsUser(0, nil, 
    zAppName, { pointer to command line string } 
    nil, { pointer to process security attributes } 
    nil, { pointer to thread security attributes } 
    false, { handle inheritance flag } 
    CREATE_NEW_CONSOLE or { creation flags } 
    NORMAL_PRIORITY_CLASS, 
    nil, { pointer to new environment block } 
    zPath, { pointer to current directory name } 
    StartupInfo, { pointer to STARTUPINFO } 
    ProcessInfo); { pointer to PROCESS_INF } 
end; 
+0

이 질문은 이미 bazillion 번 요청되었습니다. 기본적인 연구를 해주세요. –

+0

덕분에, 나는 기본적인 연구를하고 이유를 찾을 수 없다. 네가 알고 있으면 나를 부탁해. –

+0

일부 웹 서치를 사용해보세요. 그것이 제가 말한 기본 연구입니다. 대화 형 데스크톱에서 프로세스를 만드는 방법을 설명하는 수많은 기사가 있습니다. –

답변

1

당신은 다음 토큰 현재 활성 사용자를 얻을 수 CreateEnvironmentBlockCreateProcessAsUserW에 전달하는 WtsGetActiveConsoleSessionIDWTSQueryUserToken를 호출해야합니다. 레미의 의견과 조언에 따라

function WTSQueryUserToken(SessionId: ULONG; var phToken: THandle): BOOL; stdcall; external 'Wtsapi32.dll'; 
function CreateEnvironmentBlock(var lpEnvironment: Pointer; 
    hToken: THandle; 
    bInherit: BOOL): BOOL; 
    stdcall; external 'Userenv.dll'; 
function DestroyEnvironmentBlock(pEnvironment: Pointer): BOOL; stdcall; external 'Userenv.dll'; 

function RunAppFromService(const Path, FileName: string): Boolean; 
var 
    zPath   : array[0..512] of char; 
    zAppName   : array[0..512] of char; 
    StartupInfo  : TStartupInfo; 
    ProcessInfo  : TProcessInformation; 
    hUserToken  : THandle; 
    p     : Pointer; 
begin { WinExecAndWait32V2 } 
    Result := False; 
    StrPCopy(zPath, Path); 
    StrPCopy(zAppName, FileName); 

    if NOT WTSQueryUserToken(WtsGetActiveConsoleSessionID, hUserToken) then exit; 

    if CreateEnvironmentBlock(P, hUserToken, True) then 
    begin 
     ZeroMemory(@StartupInfo, sizeof(StartupInfo)); 
     StartupInfo.lpDesktop := ('winsta0\default'); 
     StartupInfo.dwFlags  := STARTF_USESHOWWINDOW; 
     StartupInfo.wShowWindow := Visibility; 
     if CreateProcessAsUserW(
      hUserToken, 
      nil, 
      zAppName, 
      nil, 
      nil, 
      False, 
      CREATE_UNICODE_ENVIRONMENT, 
      P, 
      zPath, 
      StartupInfo, 
      ProcessInfo) then 
     begin 
     Result := True; 
     end; 
     CloseHandle(ProcessInfo.hProcess); 
     CloseHandle(ProcessInfo.hThread); 
     DestroyEnvironmentBlock(P); 
    end; 
    if hUserToken <> INVALID_HANDLE_VALUE then 
    CloseHandle(hUserToken); 
end; 

업데이트

참고 : WTSQueryUserToken() 서비스가 시스템 계정에서 실행중인 경우에만 작동합니다.

type 
    WTS_INFO_CLASS = (
    WTSInitialProgram, 
    WTSApplicationName, 
    WTSWorkingDirectory, 
    WTSOEMId, 
    WTSSessionId, 
    WTSUserName, 
    WTSWinStationName, 
    WTSDomainName, 
    WTSConnectState, 
    WTSClientBuildNumber, 
    WTSClientName, 
    WTSClientDirectory, 
    WTSClientProductId, 
    WTSClientHardwareId, 
    WTSClientAddress, 
    WTSClientDisplay, 
    WTSClientProtocolType, 
    WTSIdleTime, 
    WTSLogonTime, 
    WTSIncomingBytes, 
    WTSOutgoingBytes, 
    WTSIncomingFrames, 
    WTSOutgoingFrames, 
    WTSClientInfo, 
    WTSSessionInfo, 
    WTSSessionInfoEx, 
    WTSConfigInfo, 
    WTSValidationInfo, 
    WTSSessionAddressV4, 
    WTSIsRemoteSession 
); 
    WTS_CONNECTSTATE_CLASS = (
    WTSActive, 
    WTSConnected, 
    WTSConnectQuery, 
    WTSShadow, 
    WTSDisconnected, 
    WTSIdle, 
    WTSListen, 
    WTSReset, 
    WTSDown, 
    WTSInit 
); 

    PWTS_SESSION_INFO = ^WTS_SESSION_INFO; 
    WTS_SESSION_INFO = record 
    SessionId: DWORD; 
    pWinStationName: LPTSTR; 
    State: WTS_CONNECTSTATE_CLASS; 
    end; 

........ 

function WTSEnumerateSessions(hServer: THandle; Reserved: DWORD; Version: DWORD; var ppSessionInfo: PWTS_SESSION_INFO; var pCount: DWORD): BOOL; stdcall; external 'Wtsapi32.dll' name {$IFDEF UNICODE}'WTSEnumerateSessionsW'{$ELSE}'WTSEnumerateSessionsA'{$ENDIF}; 

procedure WTSFreeMemory(pMemory: Pointer); stdcall; external 'Wtsapi32.dll'; 

function WTSQueryUserToken(SessionId: ULONG; var phToken: THandle): BOOL; stdcall; external 'Wtsapi32.dll'; 

function CreateEnvironmentBlock(var lpEnvironment: Pointer; 
            hToken: THandle; 
            bInherit: BOOL): BOOL; 
            stdcall; external 'Userenv.dll'; 

function DestroyEnvironmentBlock(pEnvironment: Pointer): BOOL; stdcall; external 'Userenv.dll'; 

function RunAppFromService(const Path, FileName: string): Boolean; 
const 
    WTS_CURRENT_SERVER_HANDLE: THandle = 0; 
var 
    zPath    : array[0..512] of char; 
    zAppName   : array[0..512] of char; 
    StartupInfo  : TStartupInfo; 
    ProcessInfo  : TProcessInformation; 
    hUserToken  : THandle; 
    p     : Pointer; 
    Sessions, Session : PWTS_SESSION_INFO; 
    NumSessions  : DWORD; 
    I     : Integer; 
begin { WinExecAndWait32V2 } 
    Result := False; 
    StrPCopy(zPath, Path); 
    StrPCopy(zAppName, FileName); 
    if not WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, Sessions, NumSessions) then 
    exit;; 
    try 
    if NumSessions > 0 then 
    begin 
     Session := Sessions; 
     for I := 0 to NumSessions-1 do 
     begin 
     if Session.State = WTSActive then 
     begin 
      if WTSQueryUserToken(Session.SessionId, hUserToken) then begin 
       if CreateEnvironmentBlock(P, hUserToken, True) then 
       begin 
        ZeroMemory(@StartupInfo, sizeof(StartupInfo)); 
        StartupInfo.lpDesktop := ('winsta0\default'); 
        StartupInfo.dwFlags  := STARTF_USESHOWWINDOW; 
        StartupInfo.wShowWindow := Visibility; 
        if CreateProcessAsUserW(
         hUserToken, 
         nil, 
         zAppName, 
         nil, 
         nil, 
         False, 
         CREATE_UNICODE_ENVIRONMENT, 
         P, 
         zPath, 
         StartupInfo, 
         ProcessInfo) then 
        begin 
        Result := True; 
        end; 
        CloseHandle(ProcessInfo.hProcess); 
        CloseHandle(ProcessInfo.hThread); 
        DestroyEnvironmentBlock(P); 
       end; 
       if hUserToken <> INVALID_HANDLE_VALUE then 
       CloseHandle(hUserToken); 
      end; 
     end; 
     Inc(Session); 
     end; 
    end; 
    finally 
    WTSFreeMemory(Sessions); 
    end; 
end; 
+0

당신이 말하는 것은 95 % 사실입니다. 'WTSGetActiveConsoleSessionID()'는 항상 가장 좋은/올바른 세션 ID가 아닙니다. 로그인 된 대화 형 세션이 없을 수도 있지만 원격 세션이 로그인되어있을 수 있습니다. 대신 활성 세션을 찾으려면'WTSEnumerateSessions()'및'WTSQuerySessionInformation()'을 사용하는 것이 좋습니다. 또한 'WTSQueryUserToken()'은 서비스가 SYSTEM 계정에서 실행 중일 때만 작동한다는 점에 유의해야합니다. –

+0

@RemyLebeau 정정 해 주셔서 감사합니다 답변이 업데이트되었습니다. 나는 내가 올바르게 업데이트되기를 바랍니다. 왜냐하면 나는이 상황에서'WTSQuerySessionInformation'의 사용법/필요성을 얻지 못했기 때문입니다. – RepeatUntil

+0

'WTSQuerySessionInformation'은 활성화 된 세션을 필터링하는 데 사용될 수 있습니다. 예를 들어 특정 사용자로 앱을 실행하거나 특정 클라이언트 PC를 실행하려는 경우입니다. –