public override void Apply(Prison prison)
        {
            if (prison == null)
            {
                throw new ArgumentNullException("prison");
            }

            if (this.windowStation != IntPtr.Zero)
            {
                return;
            }

            NativeMethods.SECURITY_ATTRIBUTES secAttributes = new NativeMethods.SECURITY_ATTRIBUTES();
            secAttributes.nLength = Marshal.SizeOf(secAttributes);

            this.windowStation = OpenWindowStation(prison.User.UserName);

            if (this.windowStation == IntPtr.Zero)
            {
                // TODO SECURITY: change security attributes. the default will give everyone access to the object including other prisons
                this.windowStation = CreateWindowStation(prison.User.UserName);
            }

            lock (windowStationContextLock)
            {
                IntPtr currentWindowStation = GetProcessWindowStation();

                try
                {
                    SetProcessWindowStation(this.windowStation);

                    // TODO SECURITY: change security attributes. the default will give everyone access to the object including other prisons
                    CreateDesktop();

                    prison.desktopName = string.Format(CultureInfo.InvariantCulture, @"{0}\Default", prison.User.UserName);
                }
                finally
                {
                    SetProcessWindowStation(currentWindowStation);
                }
            }
        }
        private Native.NativeMethods.PROCESS_INFORMATION NativeCreateProcessAsUser(bool interactive, string filename, string arguments, string curDir, string envBlock, PipeStream stdinPipeName, PipeStream stdoutPipeName, PipeStream stderrPipeName)
        {
            var startupInfo = new Native.NativeMethods.STARTUPINFO();
            var processInfo = new Native.NativeMethods.PROCESS_INFORMATION();

            startupInfo = new Native.NativeMethods.STARTUPINFO();

            if (this.prison.RuleEnabled(RuleTypes.WindowStation))
            {
                startupInfo.lpDesktop = this.prison.desktopName;
            }

            NativeMethods.ProcessCreationFlags creationFlags = NativeMethods.ProcessCreationFlags.ZERO_FLAG;

            // Exclude flags
            creationFlags &=
                ~NativeMethods.ProcessCreationFlags.CREATE_PRESERVE_CODE_AUTHZ_LEVEL &
                ~NativeMethods.ProcessCreationFlags.CREATE_BREAKAWAY_FROM_JOB;

            // Include flags
            creationFlags |=
                NativeMethods.ProcessCreationFlags.CREATE_DEFAULT_ERROR_MODE |
                NativeMethods.ProcessCreationFlags.CREATE_NEW_PROCESS_GROUP |
                NativeMethods.ProcessCreationFlags.CREATE_SUSPENDED |
                NativeMethods.ProcessCreationFlags.CREATE_UNICODE_ENVIRONMENT |
                NativeMethods.ProcessCreationFlags.CREATE_NEW_CONSOLE;

            // TODO: extra steps for interactive to work:
            // http://blogs.msdn.com/b/winsdk/archive/2013/05/01/how-to-launch-a-process-interactively-from-a-windows-service.aspx
            if (interactive)
            {
                startupInfo.lpDesktop = string.Empty;
            }
            else
            {
                // creationFlags |= Native.ProcessCreationFlags.CREATE_NO_WINDOW;

                // startupInfo.dwFlags |= 0x00000100; // STARTF_USESTDHANDLES

                // Dangerous and maybe insecure to give a handle like that an untrusted processes
                // startupInfo.hStdInput = Native.GetStdHandle(Native.STD_INPUT_HANDLE);
                // startupInfo.hStdOutput = Native.GetStdHandle(Native.STD_OUTPUT_HANDLE);
                // startupInfo.hStdError = Native.GetStdHandle(Native.STD_ERROR_HANDLE);

                if (stdinPipeName != null || stdoutPipeName != null || stderrPipeName != null)
                {
                    startupInfo.dwFlags |= 0x00000100; // STARTF_USESTDHANDLES
                }

                if (stdinPipeName != null)
                {
                    startupInfo.hStdInput = GetHandleFromPipe(stdinPipeName);
                }

                if (stdoutPipeName != null)
                {
                    startupInfo.hStdOutput = GetHandleFromPipe(stdoutPipeName);
                }

                if (stderrPipeName != null)
                {
                    startupInfo.hStdError = GetHandleFromPipe(stderrPipeName);
                }
            }

            if (string.IsNullOrWhiteSpace(curDir))
            {
                curDir = this.prison.PrisonHomePath;
            }

            NativeMethods.SECURITY_ATTRIBUTES processAttributes = new NativeMethods.SECURITY_ATTRIBUTES();
            NativeMethods.SECURITY_ATTRIBUTES threadAttributes = new NativeMethods.SECURITY_ATTRIBUTES();
            processAttributes.nLength = Marshal.SizeOf(processAttributes);
            threadAttributes.nLength = Marshal.SizeOf(threadAttributes);

            if (!NativeMethods.CreateProcessAsUser(
                hToken: this.prison.User.LogonToken.DangerousGetHandle(),
                lpApplicationName: filename,
                lpCommandLine: arguments,
                lpProcessAttributes: ref processAttributes,
                lpThreadAttributes: ref threadAttributes,
                bInheritHandles: true,
                dwCreationFlags: creationFlags,
                lpEnvironment: envBlock,
                lpCurrentDirectory: curDir,
                lpStartupInfo: ref startupInfo,
                lpProcessInformation: out processInfo))
            {
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }

            return processInfo;
        }