穿透Session 0 隔离(二)(How to use Windows service run a GUI application.)

转载自:http://www.cnblogs.com/gnielee/archive/2010/04/08/1707169.html

 上一篇我们已经对Session 0 隔离有了进一步认识,如果在开发过程中确实需要服务与桌面用户进行交互,可以通过远程桌面服务的API 绕过Session 0 的隔离完成交互操作。

     对于简单的交互,服务可以通过WTSSendMessage 函数,在用户Session 上显示消息窗口。对于一些复杂的UI 交互,必须调用CreateProcessAsUser 或其他方法(WCF、.NET远程处理等)进行跨Session 通信,在桌面用户上创建一个应用程序界面。

WTSSendMessage 函数

     如果服务只是简单的向桌面用户Session 发送消息窗口,则可以使用WTSSendMessage 函数实现。首先,在上一篇下载的代码中加入一个Interop.cs 类,并在类中加入如下代码:

public static IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;



public static void ShowMessageBox(string message, string title)

{

    int resp = 0;

    WTSSendMessage(

        WTS_CURRENT_SERVER_HANDLE, 

        WTSGetActiveConsoleSessionId(),

        title, title.Length, 

        message, message.Length, 

        0, 0, out resp, false);

}



[DllImport("kernel32.dll", SetLastError = true)]

public static extern int WTSGetActiveConsoleSessionId();



[DllImport("wtsapi32.dll", SetLastError = true)]

public static extern bool WTSSendMessage(

    IntPtr hServer,

    int SessionId,

    String pTitle,

    int TitleLength,

    String pMessage,

    int MessageLength,

    int Style,

    int Timeout,

    out int pResponse,

    bool bWait);

 

 
  

     在ShowMessageBox 函数中调用了WTSSendMessage 来发送信息窗口,这样我们就可以在Service 的OnStart 函数中使用,打开Service1.cs 加入下面代码:

protected override void OnStart(string[] args)

{

    Interop.ShowMessageBox("This a message from AlertService.",

                           "AlertService Message");

}

  

     编译程序后在服务管理器中重新启动AlertService 服务,从下图中可以看到消息窗口是在当前用户桌面显示的,而不是Session 0 中。

穿透Session 0 隔离(二)(How to use Windows service run a GUI application.)

CreateProcessAsUser 函数

     如果想通过服务向桌面用户Session 创建一个复杂UI 程序界面,则需要使用CreateProcessAsUser 函数为用户创建一个新进程用来运行相应的程序。打开Interop 类继续添加下面代码:

public static void CreateProcess(string app, string path)

{

    bool result;

    IntPtr hToken = WindowsIdentity.GetCurrent().Token;

    IntPtr hDupedToken = IntPtr.Zero;



    PROCESS_INFORMATION pi = new PROCESS_INFORMATION();

    SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();

    sa.Length = Marshal.SizeOf(sa);



    STARTUPINFO si = new STARTUPINFO();

    si.cb = Marshal.SizeOf(si);



    int dwSessionID = WTSGetActiveConsoleSessionId();

    result = WTSQueryUserToken(dwSessionID, out hToken);

    

    if (!result)

    {

        ShowMessageBox("WTSQueryUserToken failed", "AlertService Message");

    }



    result = DuplicateTokenEx(

          hToken,

          GENERIC_ALL_ACCESS,

          ref sa,

          (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,

          (int)TOKEN_TYPE.TokenPrimary,

          ref hDupedToken

       );



    if (!result)

    {

        ShowMessageBox("DuplicateTokenEx failed" ,"AlertService Message");

    }



    IntPtr lpEnvironment = IntPtr.Zero;

    result = CreateEnvironmentBlock(out lpEnvironment, hDupedToken, false);



    if (!result)

    {

        ShowMessageBox("CreateEnvironmentBlock failed", "AlertService Message");

    }



    result = CreateProcessAsUser(

                         hDupedToken,

                         app,

                         String.Empty,

                         ref sa, ref sa,

                         false, 0, IntPtr.Zero,

                         path, ref si, ref pi);



    if (!result)

    {

        int error = Marshal.GetLastWin32Error();

        string message = String.Format("CreateProcessAsUser Error: {0}", error);

        ShowMessageBox(message, "AlertService Message");

    }



    if (pi.hProcess != IntPtr.Zero)

        CloseHandle(pi.hProcess);

    if (pi.hThread != IntPtr.Zero)

        CloseHandle(pi.hThread);

    if (hDupedToken != IntPtr.Zero)

        CloseHandle(hDupedToken);

}



[StructLayout(LayoutKind.Sequential)]

public struct STARTUPINFO

{

    public Int32 cb;

    public string lpReserved;

    public string lpDesktop;

    public string lpTitle;

    public Int32 dwX;

    public Int32 dwY;

    public Int32 dwXSize;

    public Int32 dwXCountChars;

    public Int32 dwYCountChars;

    public Int32 dwFillAttribute;

    public Int32 dwFlags;

    public Int16 wShowWindow;

    public Int16 cbReserved2;

    public IntPtr lpReserved2;

    public IntPtr hStdInput;

    public IntPtr hStdOutput;

    public IntPtr hStdError;

}



[StructLayout(LayoutKind.Sequential)]

public struct PROCESS_INFORMATION

{

    public IntPtr hProcess;

    public IntPtr hThread;

    public Int32 dwProcessID;

    public Int32 dwThreadID;

}



[StructLayout(LayoutKind.Sequential)]

public struct SECURITY_ATTRIBUTES

{

    public Int32 Length;

    public IntPtr lpSecurityDescriptor;

    public bool bInheritHandle;

}



public enum SECURITY_IMPERSONATION_LEVEL

{

    SecurityAnonymous,

    SecurityIdentification,

    SecurityImpersonation,

    SecurityDelegation

}



public enum TOKEN_TYPE

{

    TokenPrimary = 1,

    TokenImpersonation

}



public const int GENERIC_ALL_ACCESS = 0x10000000;



[DllImport("kernel32.dll", SetLastError = true,

    CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]

public static extern bool CloseHandle(IntPtr handle);



[DllImport("advapi32.dll", SetLastError = true,

    CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]

public static extern bool CreateProcessAsUser(

    IntPtr hToken,

    string lpApplicationName,

    string lpCommandLine,

    ref SECURITY_ATTRIBUTES lpProcessAttributes,

    ref SECURITY_ATTRIBUTES lpThreadAttributes,

    bool bInheritHandle,

    Int32 dwCreationFlags,

    IntPtr lpEnvrionment,

    string lpCurrentDirectory,

    ref STARTUPINFO lpStartupInfo,

    ref PROCESS_INFORMATION lpProcessInformation);



[DllImport("advapi32.dll", SetLastError = true)]

public static extern bool DuplicateTokenEx(

    IntPtr hExistingToken,

    Int32 dwDesiredAccess,

    ref SECURITY_ATTRIBUTES lpThreadAttributes,

    Int32 ImpersonationLevel,

    Int32 dwTokenType,

    ref IntPtr phNewToken);



[DllImport("wtsapi32.dll", SetLastError=true)]

public static extern bool WTSQueryUserToken(

    Int32 sessionId, 

    out IntPtr Token);



[DllImport("userenv.dll", SetLastError = true)]

static extern bool CreateEnvironmentBlock(

    out IntPtr lpEnvironment, 

    IntPtr hToken, 

    bool bInherit);

     在CreateProcess 函数中同时也涉及到DuplicateTokenEx、WTSQueryUserToken、CreateEnvironmentBlock 函数的使用,有兴趣的朋友可通过MSDN 进行学习。完成CreateProcess 函数创建后,就可以真正的通过它来调用应用程序了,回到Service1.cs 修改一下OnStart 我们来打开一个CMD 窗口。如下代码:



protected override void OnStart(string[] args)

{

    Interop.CreateProcess("cmd.exe",@"C:\Windows\System32\");

}

  

     重新编译程序,启动AlertService 服务便可看到下图界面。至此,我们已经可以通过一些简单的方法对Session 0 隔离问题进行解决。大家也可以通过WCF 等技术完成一些更复杂的跨Session 通信方式,实现在Windows 7 及Vista 系统中服务与桌面用户的交互操作。

穿透Session 0 隔离(二)(How to use Windows service run a GUI application.)

参考资料

1. WTSSendMessage Function 
http://msdn.microsoft.com/en-us/library/aa383842(VS.85).aspx

2. CreateProcessAsUser Function 
http://msdn.microsoft.com/en-us/library/ms682429(v=VS.85).aspx

3. WTSSendMessage (wtsapi32) 
http://www.pinvoke.net/default.aspx/wtsapi32/WTSSendMessage.html

4. WTSQueryUserToken Function 
http://msdn.microsoft.com/en-us/library/aa383840(VS.85).aspx

5. http://www.pinvoke.net/

代码下载

 

你可能感兴趣的:(application)