Windows サービスから GUI アプリケーションを実行する

 Windows サービス(セッション 0)からユーザが操作できるように GUI アプリケーション(セッション 2)を起動する。
 コードは C# ですが C/C++ でもほぼ同じコードです。

■コード(.CS)

/// <summary>
/// カレント・セッションのユーザでプロセスを起動する
/// </summary>
/// <param name="strCommandLine"></param>
/// <returns></returns>
public static bool RunUserProcess( string strCommandLine )
{
    UInt32 nSessionID = WTSGetActiveConsoleSessionId();
 
    IntPtr hUserToken;
 
    if( WTSQueryUserToken( nSessionID, out hUserToken ) == 0 )
    {
        return false;
    }
 
 
    STARTUPINFO startup = new STARTUPINFO();
 
    startup.cb = Marshal.SizeOf(startup);
 
 
    PROCESS_INFORMATION pi;
 
 
    int nResult = CreateProcessAsUser(
                        hUserToken,
                        IntPtr.Zero,
                        strCommandLine,
                        IntPtr.Zero,
                        IntPtr.Zero,
                        0,
                        0,
                        IntPtr.Zero,
                        System.IO.Directory.GetCurrentDirectory(),
                        ref startup,
                        out pi );
 
 
    CloseHandle( pi.hThread );
    CloseHandle( pi.hProcess );
 
 
    CloseHandle( hUserToken );
 
    return (nResult != 0);
}
 
 
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
struct STARTUPINFO
{
    public int cb;
    public IntPtr lpReserved;
    public IntPtr lpDesktop;
    public IntPtr lpTitle;
    public uint dwX;
    public uint dwY;
    public uint dwXSize;
    public uint dwYSize;
    public uint dwXCountChars;
    public uint dwYCountChars;
    public uint dwFillAttribute;
    public uint dwFlags;
    public ushort wShowWindow;
    public ushort cbReserved2;
    public IntPtr lpReserved2;
    public IntPtr hStdInput;
    public IntPtr hStdOutput;
    public IntPtr hStdError;
}
 
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
struct PROCESS_INFORMATION
{
    public IntPtr hProcess;
    public IntPtr hThread;
    public uint dwProcessId;
    public uint dwThreadId;
}
 
 
[DllImport(@"ADVAPI32.dll", CharSet=CharSet.Unicode)]
extern static Int32 CreateProcessAsUser(
    IntPtr hUserToken,
    IntPtr strApplicationName, // NULL を渡すため IntPtr にしました。
    string strCommandLine,
    IntPtr lpProcessAttributes, // NULL を渡すため IntPtr にしました。
    IntPtr lpThreadAttributes, // NULL を渡すため IntPtr にしました。
    Int32 bInheritHandles,
    UInt32 dwCreationFlags,
    IntPtr lpEnvironment, // NULL を渡すため IntPtr にしました。
    string lpCurrentDirectory,
    ref STARTUPINFO lpStartupInfo,
    out PROCESS_INFORMATION lpProcessInformation );
 
[DllImport(@"Kernel32.dll")]
public extern static Int32 CloseHandle( IntPtr handle );
 
[DllImport(@"Kernel32.dll")]
public extern static UInt32 WTSGetActiveConsoleSessionId();
 
[DllImport(@"WTSAPI32.dll")]
public extern static Int32 WTSQueryUserToken( UInt32 nSessionID, out IntPtr hUserToken );