隐藏

交互式 Windows 服务 (CSCreateProcessAsUserFromService)

发布:2018/7/2 22:52:38作者:管理员 来源:本站 浏览次数:1223

简介

本示例演示了如何在使用 C#.Net 编写的服务应用程序的登录用户会话中以交互方式创建/启动进程。

只是为了演示目的,子进程(本示例中的 Notepad.exe)的路径/名称已在父级服务应用程序中进行了硬编码,但是可以引用本示例来启动任何其他交互式子应用程序。示例的主要实现位于名为“CSCreateProcessAsUserFromService”的类中,可以通过 P/Invoke 调用到某些本机 Windows API 来启动进程。

运行示例

1) 在 Visual Studio 2010 中打开“CSCreateProcessAsUserFromService.sln”文件。

2) 构建项目并获取服务应用程序 exe,如 CSCreateProcessAsUserFromService.exe。

3) 项目构建完成后,使用命令行服务控制器实用工具“sc”创建/启动/停止/删除 Windows 服务。

“sc”命令的语法(务必以管理员身份运行)–

sc <服务器> [命令] [服务名称] <选项 1> <选项 2>...

例如 –

创建名为“My Sample Service”的 Windows 服务 –
sc create “My Sample Service” binpath= <CSCreateProcessAsUserFromService.exe 的完整路径>

启动“My Sample Service”服务将会启动子进程 –
sc start “My Sample Service”

停止“My Sample Service”服务 –
sc stop "My Sample Service"

删除名为“My Sample Service”的 Windows 服务 –
sc delete "My Sample Service"

使用代码

1) 子进程的启动已在“CSCreateProcessAsUserFromService”类中实现。

2) 在服务的“OnStart”中,我们已创建了一个线程来启动/创建子进程。在单独的线程中创建子进程始终是很好的做法,因为它不会阻止服务主线程。阻止服务主线程可能会导致服务控制管理器 (SCM) 的超时错误。

protected override void OnStart(string[] args) 
{ 
    // As creating a child process might be a time consuming operation, 
    // its better to do that in a separate thread than blocking the main thread. 
    System.Threading.Thread ProcessCreationThread = new System.Threading.Thread(MyThreadFunc); 
    ProcessCreationThread.Start(); 
}
3) 线程过程传递到要启动的应用程序的名称中。在本示例中,我们已尝试启动 Notepad.exe。但是可以指定任何其他应用程序名称。
C#
// This thread function would launch a child process   // in the interactive session of the logged-on user.  public static void MyThreadFunc() 
{ 
    CreateProcessAsUserWrapper.LaunchChildProcess("C:\\Windows\\notepad.exe"); 
} 
 

 

4)“CSCreateProcessAsUserFromService”类首先通过调用 WTSEnumerateSessions 枚举所有正在运行/可用的会话。

5) 获取所有会话后,我们尝试检查哪个会话是活动会话,即登录用户的会话。

6) 然后,我们通过调用 WTSQueryUserToken 来获取由会话 ID 指定的登录用户的主访问令牌。

7) 然后,将从 WTSQueryUserToken 接收到的用户令牌以及子应用程序的名称传递到函数 CreateProcessAsUser,以便在登录用户的会话中以交互方式启动子进程。

 

C#
public static void LaunchChildProcess(string ChildProcName) 
{ 
    IntPtr ppSessionInfo = IntPtr.Zero; 
    UInt32 SessionCount = 0; 
  
    if (WTSEnumerateSessions( 
        (IntPtr)WTS_CURRENT_SERVER_HANDLE,  // Current RD Session Host Server handle would be zero. 
        0,                                  // This reserved parameter must be zero. 
        1,                                  // The version of the enumeration request must be 1. 
        ref ppSessionInfo,                  // This would point to an array of session info. 
        ref SessionCount                    // This would indicate the length of the above array. 
        )) 
    { 
        for (int nCount = 0; nCount < SessionCount; nCount++) 
        { 
            // Extract each session info and check if it is the  
            // "Active Session" of the current logged-on user. 
            WTS_SESSION_INFO tSessionInfo = (WTS_SESSION_INFO)Marshal.PtrToStructure( 
                ppSessionInfo + nCount * Marshal.SizeOf(typeof(WTS_SESSION_INFO)), 
                typeof(WTS_SESSION_INFO) 
                ); 
  
            if (WTS_CONNECTSTATE_CLASS.WTSActive == tSessionInfo.State) 
            { 
                IntPtr hToken = IntPtr.Zero; 
                if (WTSQueryUserToken(tSessionInfo.SessionID, out hToken)) 
                { 
                    // Launch the child process interactively  
                    // with the token of the logged-on user. 
                    PROCESS_INFORMATION tProcessInfo; 
                    STARTUPINFO tStartUpInfo = new STARTUPINFO(); 
                    tStartUpInfo.cb = Marshal.SizeOf(typeof(STARTUPINFO)); 
  
                    bool ChildProcStarted = CreateProcessAsUser( 
                        hToken,             // Token of the logged-on user. 
                        ChildProcName,      // Name of the process to be started. 
                        null,               // Any command line arguments to be passed. 
                        IntPtr.Zero,        // Default Process' attributes. 
                        IntPtr.Zero,        // Default Thread's attributes. 
                        false,              // Does NOT inherit parent's handles. 
                        0,                  // No any specific creation flag. 
                        null,               // Default environment path. 
                        null,               // Default current directory. 
                        ref tStartUpInfo,   // Process Startup Info.  
                        out tProcessInfo    // Process information to be returned. 
                        ); 
  
                    if (ChildProcStarted) 
                    { 
                        // The child process creation is successful! 
  
                        // If the child process is created, it can be controlled via the out  
                        // param "tProcessInfo". For now, as we don't want to do any thing  
                        // with the child process, closing the child process' handles  
                        // to prevent the handle leak. 
                        CloseHandle(tProcessInfo.hThread); 
                        CloseHandle(tProcessInfo.hProcess); 
                    } 
                    else 
                    { 
                        // CreateProcessAsUser failed! 
                    } 
  
                    // Whether child process was created or not, close the token handle  
                    // and break the loop as processing for current active user has been done. 
                    CloseHandle(hToken); 
                    break; 
                } 
                else 
                { 
                    // WTSQueryUserToken failed! 
                } 
            } 
            else 
            { 
                // This Session is not active! 
            } 
        } 
  
        // Free the memory allocated for the session info array. 
        WTSFreeMemory(ppSessionInfo); 
    } 
    else 
    { 
        // WTSEnumerateSessions failed! 
    } 
}