Exemplo n.º 1
0
        public static ProcessInTheJob CreateProcessInTheJob(
            string desktopName,
            string appPath,
            string cmdLine,
            IntPtr jobHandle)
        {
            var newJobHandle = IntPtr.Zero;

            if (jobHandle == IntPtr.Zero)
            {
                newJobHandle = jobHandle = WinApi.CreateJobObject(IntPtr.Zero, null);
                // TODO set limits etc
            }

            const uint NORMAL_PRIORITY_CLASS = 0x0020;
            const uint CREATE_SUSPENDED      = 0x00000004;

            var sInfo = new WinApi.STARTUPINFO();

            sInfo.cb        = Marshal.SizeOf(sInfo);
            sInfo.lpDesktop = desktopName;

            var pSec = new WinApi.SECURITY_ATTRIBUTES();
            var tSec = new WinApi.SECURITY_ATTRIBUTES();

            pSec.nLength = Marshal.SizeOf(pSec);
            tSec.nLength = Marshal.SizeOf(tSec);

            string commandLine = "";

            if (!string.IsNullOrEmpty(appPath) && !string.IsNullOrEmpty(cmdLine))
            {
                commandLine = $"{appPath} {cmdLine}";
            }
            else if (!string.IsNullOrEmpty(cmdLine))
            {
                commandLine = cmdLine;
            }

            var retValue = WinApi.CreateProcess(appPath, commandLine,
                                                ref pSec, ref tSec, false,
                                                NORMAL_PRIORITY_CLASS | CREATE_SUSPENDED,
                                                IntPtr.Zero, null, ref sInfo, out var pInfo);

            WriteLine("Process ID (PID): " + pInfo.dwProcessId);
            WriteLine("Process Handle : " + pInfo.hProcess);

            var r = WinApi.AssignProcessToJobObject(jobHandle, pInfo.hProcess);

            if (!r)
            {
                if (newJobHandle != IntPtr.Zero)
                {
                    WinApi.CloseHandle(newJobHandle);
                }
                return(new ProcessInTheJob(ProcessInTheJob.Status.COULD_NOT_ASSIGN_JOB, IntPtr.Zero, pInfo));
            }

            // Ensure that killing one process kills the others
            var extendedInfo = new WinApi.JOBOBJECT_EXTENDED_LIMIT_INFORMATION
            {
                BasicLimitInformation =
                {
                    LimitFlags = (short)WinApi.LimitFlags.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
                }
            };

            int    length          = Marshal.SizeOf(typeof(WinApi.JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
            IntPtr extendedInfoPtr = Marshal.AllocHGlobal(length);

            Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);

            if (!WinApi.SetInformationJobObject(jobHandle,
                                                WinApi.JOBOBJECTINFOCLASS.JobObjectExtendedLimitInformation,
                                                extendedInfoPtr, (uint)length))
            {
                throw new Exception(string.Format("Unable to set information.  Error: {0}", Marshal.GetLastWin32Error()));
            }

            Marshal.FreeHGlobal(extendedInfoPtr);

            WinApi.ResumeThread(pInfo.hThread);
            return(new ProcessInTheJob(ProcessInTheJob.Status.JOB_ASSIGNED, jobHandle, pInfo));
        }
Exemplo n.º 2
0
        static int Main(string[] cmdArgs)
        {
            var argMap = Args.Parse(cmdArgs, new HashSet <string> {
                "app", "desktop"
            });

            if (cmdArgs.Length == 0)
            {
                WriteLine("Usage: headless <appname> [--<option-name> <option-value...] -- <app args>");
                WriteLine("Available options are:");
                WriteLine("  --app <appname> - application to start. Alternative way to set an app.");
                WriteLine("  --desktop - desktop name. Leave unspecified for random name.");
                WriteLine("  --desktop=false - do not use virtual desktop");
                return(1);
            }

            WriteLine($"Starting headless '{string.Join(" ", cmdArgs)}'");

            var appName =
                (argMap.Named.TryGetValue("app", out var val) ? val
                                        : System.Environment.GetEnvironmentVariable("HEADLESS_APP")) ??
                (argMap.Positional.Count > 0 ? argMap.Positional[0] : null);

            var appPath = FindApplication(appName);

            if (appPath == null)
            {
                WriteLine($"ERR: application '{appName}' not found'");
                return(1);
            }
            WriteLine($"INFO: starting application {appPath}");

            var rand = new System.Random();

            // shaping desktop name
            string desktopName      = null;
            bool   useHiddenDesktop = false;

            if (argMap.Named.TryGetValue("desktop", out val) && !string.IsNullOrWhiteSpace(val))
            {
                if (val != "false")
                {
                    desktopName      = val;
                    useHiddenDesktop = true;
                }
            }
            else
            {
                desktopName      = $"Headless-{rand.Next(int.MaxValue)}";
                useHiddenDesktop = true;
            }

            var cleanupActions = new List <Action>();

            CancelKeyPress += (s, ev) =>
            {
                WriteLine("Ctrl+C pressed");
                Cleanup(cleanupActions);
                ev.Cancel = true;
            };

            // get desktop name from params
            var desktopHandle = IntPtr.Zero;

            if (useHiddenDesktop)
            {
                desktopHandle = DesktopUtils.CreateDesktop(desktopName);
                WriteLine($"INFO: opened desktop '{desktopName}'");

                cleanupActions.Add(() =>
                {
                    WriteLine($"INFO: closing desktop {desktopHandle}");
                    DesktopUtils.CloseDesktop(desktopHandle);
                });
            }

            var argsEscaped = from r in argMap.Reminder select $"\"{r}\"";
            var commandLine = String.Join(" ", argsEscaped);

            WriteLine($"INFO: Starting app with cmdline '{commandLine}'");

            // TODO desktop default
            var jobProcess = DesktopUtils.CreateProcessInTheJob(desktopName, appPath, commandLine, IntPtr.Zero);

            if (jobProcess.status == DesktopUtils.ProcessInTheJob.Status.COULD_NOT_ASSIGN_JOB)
            {
                WriteLine("ERROR: Failed to assign job");
                WinApi.ResumeThread(jobProcess.processInfo.hThread);
            }
            else
            {
                cleanupActions.Add(() =>
                {
                    WriteLine($"INFO: closing job {jobProcess.jobHandle}");
                    Console.Beep();
                    WinApi.CloseHandle(jobProcess.jobHandle);
                });
            }

            cleanupActions.Add(() =>
            {
                WriteLine($"INFO: closing process {jobProcess.processInfo.hProcess}");
                WinApi.CloseHandle(jobProcess.processInfo.hProcess);
            });

            if (useHiddenDesktop)
            {
                cleanupActions.Add(() =>
                {
                    WriteLine("INFO: closing desktop windows");
                    DesktopUtils.CloseDesktopWindows(desktopName);
                });
            }

            WriteLine($"INFO: app process id '{System.Diagnostics.Process.GetCurrentProcess().Id}'");

            WinApi.CloseHandle(jobProcess.processInfo.hThread);
            WinApi.WaitForSingleObject(jobProcess.processInfo.hProcess, WinApi.INFINITE);

            Cleanup(cleanupActions);

            return(0);
        }