Exemple #1
0
        static void Main(string[] args)
        {
            // 종료시 메시지를 표시하기 위한 스트링빌더입니다.
            StringBuilder sbLog = new StringBuilder();

            // 본 프로그램은 조용히 실행시키는게 목적이므로 오류가 없는 한 조용히 넘기려고 합니다.
            // 오류가 잡히면 메시지창을 표시하게 하고자 합니다.
            bool hasError = false;

            //디버그 컴파일 상태인 경우 안내 문구를 추가합니다.
            if (IS_DEBUG)
            {
                sbLog.AppendLine("Program compiled in DEBUG mode. MessageBox will appear everytime.");
                hasError = true; // 또한 메시지박스가 무조건 표시되도록 합니다.
            }

            //if (IS_DEBUG)
            //{
            //    uint exitCode = 0xF8A432EB;// (-123456789)를 32비트 안에 표현
            //
            //    int intExitCode = 1<<(32-1) | (int)(exitCode & 0x7FFFFFFF);
            //
            //    sbLog.AppendLine(intExitCode);
            //}

            if (args.Length <= 0)
            {
                //오류가 있습니다.
                hasError = true;
                //아무 명령도 오지 않았으므로 도움말을 표시합니다.
                sbLog.AppendLine(getHelp("There is nothing to do."));
            }
            else
            {
                int nCmdStart = 0;

                bool haveToWaitForFinish = STR_CMD_WAIT.Equals(STR_CMD_DEFAULT);

                if (args[nCmdStart].StartsWith("/"))
                {
                    // 첫 매개변수가 /로 시작합니다. LAUNCH인지 WAIT인지 살핍니다.
                    String cmdToLaunchOrWait = args[nCmdStart];

                    // StringComparison.OrdinalIgnoreCase는 문화적 차이나 지역화에 신경쓰지 않는 비교방식을 채용합니다.
                    if (STR_CMD_LAUNCH.Equals(cmdToLaunchOrWait, StringComparison.OrdinalIgnoreCase))
                    {
                        haveToWaitForFinish = false;
                    }
                    else if (STR_CMD_WAIT.Equals(cmdToLaunchOrWait, StringComparison.OrdinalIgnoreCase))
                    {
                        haveToWaitForFinish = true;
                    }
                    else
                    {
                        //명령어가 하나도 맞지 않는 경우 기본 명령으로 회귀.
                        sbLog.AppendLine("WARNING: No matching commands found for " + cmdToLaunchOrWait + ". defaulting to " + STR_CMD_DEFAULT);
                    }

                    // 다음 매개변수로 진행합니다.
                    nCmdStart++;
                }
                else
                {
                    // 첫 매개변수가 /로 시작하지 않습니다. 그대로 사용합니다.
                    // 기본값은 STR_CMD_DEFAULT에 정의되어 있습니다.
                }

                // 현재 매개변수 위치 이후의 매개변수, 실제로 실행할 명령만 셉니다.
                sbLog.AppendLine("# of args to launch: " + (args.Length - nCmdStart));

                if (args.Length >= 1 + nCmdStart)
                {
                    //매개변수로 전달받은 프로그램의 이름입니다.
                    String strTargetProgramName = args[0 + nCmdStart];

                    if (!(strTargetProgramName.Contains(@":\") || strTargetProgramName.Contains(":/")))
                    {
                        //만약 절대 경로가 아니라면 절대 경로로 변환해줍니다.
                        //http://stackoverflow.com/a/4796339

                        //현재 프로세스의 경로를 활용합니다.
                        string strProcessPath         = System.IO.Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName);
                        string strAbsoluteProgramPath = System.IO.Path.Combine(strProcessPath, strTargetProgramName);
                        strTargetProgramName = System.IO.Path.GetFullPath((new Uri(strAbsoluteProgramPath)).LocalPath);
                    }

                    //이 프로그램의 매개변수입니다.
                    String strArgsToHandover = "";

                    //프로그램을 실행시킬 준비를 합니다.
                    if (IF_DEPRECATED_HACK)
                    {
                        // TODO: 히든모드야 누구든 잘하고 잘되지만, PREVENT FOCUS UTIL의 경우 WaitForExit이 가능할지 못할지는 모르겠다.. ㅠㅠ
                        System.Diagnostics.ProcessStartInfo targetProgram = new System.Diagnostics.ProcessStartInfo(strTargetProgramName);
                        targetProgram.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;


                        sbLog.AppendLine("Preparing to launch in minized mode: " + strTargetProgramName);

                        if (args.Length >= 2 + nCmdStart)
                        {
                            // 매개변수의 갯수가 2개 이상인경우 다음 매개변수를 모두 합칩니다.
                            StringBuilder sbArgs = new StringBuilder();
                            for (int cur = 1 + nCmdStart; cur < args.Length; cur++)
                            {
                                //첫번째가 아닌 경우 사이에 빈 칸을 넣어줍니다.
                                if (cur != 1 + nCmdStart)
                                {
                                    sbArgs.Append(' ');
                                }
                                sbArgs.Append(args[cur]);
                            }

                            // 문자열을 완성시켜 전달해줍니다.
                            strArgsToHandover = sbArgs.ToString();

                            targetProgram.Arguments = strArgsToHandover;
                            sbLog.AppendLine(STR_INDENT + "with parameters: " + strArgsToHandover);
                        }

                        try
                        {
                            // 프로세스를 가동 준비합니다.
                            System.Diagnostics.Process procTargetProgram = new System.Diagnostics.Process {
                                StartInfo = targetProgram
                            };

                            // Exit 이벤트를 걸어서 현재 스레드를 기다리지 않게 합니다. 그러나 여기에선 기다려야합니다. 주석처리
                            //https://msdn.microsoft.com/en-us/library/system.diagnostics.process.exited.aspx
                            //procTargetProgram.EnableRaisingEvents = true;
                            //procTargetProgram.Exited += new EventHandler(myProcess_Exited);

                            // 프로세스를 시작합니다.
                            procTargetProgram.Start();
                            sbLog.AppendLine("Launched " + strTargetProgramName + ' ' + strArgsToHandover);

                            //기다려야한다면 기다립니다.
                            if (haveToWaitForFinish)
                            {
                                procTargetProgram.WaitForExit();
                                sbLog.AppendLine("Finished. Exit code was " + procTargetProgram.ExitCode);
                            }
                        }
                        catch (Exception e)
                        {
                            hasError = true;
                            sbLog.AppendLine("ERROR launching " + strTargetProgramName + ' ' + strArgsToHandover);
                            sbLog.AppendLine(STR_INDENT + e.Message);
                        }
                    }
                    else
                    {
                        sbLog.AppendLine("Preparing to launch in minized mode: " + strTargetProgramName);

                        if (args.Length >= 2 + nCmdStart)
                        {
                            // 매개변수의 갯수가 2개 이상인경우 다음 매개변수를 모두 합칩니다.
                            StringBuilder sbArgs = new StringBuilder();
                            for (int cur = 1 + nCmdStart; cur < args.Length; cur++)
                            {
                                //첫번째가 아닌 경우 사이에 빈 칸을 넣어줍니다.
                                if (cur != 1 + nCmdStart)
                                {
                                    sbArgs.Append(' ');
                                }
                                sbArgs.Append(args[cur]);
                            }

                            // 문자열을 완성시켜 전달해줍니다.
                            strArgsToHandover = sbArgs.ToString();
                            sbLog.AppendLine(STR_INDENT + "with parameters: " + strArgsToHandover);
                        }

                        // 프로그램이 존재하는지 확인합니다.
                        if (!System.IO.File.Exists(strTargetProgramName))
                        {
                            hasError = true;
                            sbLog.AppendLine("ERROR, Program not found.");
                            sbLog.AppendLine(STR_INDENT + "Check program's path and try again.");
                            sbLog.AppendLine(STR_INDENT + "Surrounding program's path with double-quote marks may fix this error.");
                        }
                        else
                        {
                            // 프로그램이 존재하므로 시작시킵니다.
                            // unManaged 코드에서는 예외를 처리할 수 없기 때문에 경우의 수를 직접 찾아야합니다.
                            ProcessLaunchInfo pliLaunch = PreventFocusUtil.startProcessNoActivate(strTargetProgramName, strArgsToHandover, haveToWaitForFinish);
                            if (pliLaunch.bLaunched)
                            {
                                if (haveToWaitForFinish)
                                {
                                    sbLog.AppendLine("Finished. Exit code was " + pliLaunch.exitCode);
                                }
                                else
                                {
                                    sbLog.AppendLine("Launched.");
                                }
                            }
                            else
                            {
                                hasError = true;
                                sbLog.AppendLine("ERROR launching " + strTargetProgramName + ' ' + strArgsToHandover);
                                sbLog.AppendLine(STR_INDENT + "Exit code was " + pliLaunch.exitCode);
                                sbLog.AppendLine(STR_INDENT + "Check required privileges and other things");
                                sbLog.AppendLine(STR_INDENT + "that may prevent program from launching.");
                            }
                        }
                    }
                }
            }

            //모든 명령이 끝났습니다.

            if (hasError)
            {
                //오류가 있었습니다. 메시지 상자를 표시합니다.
                System.Windows.Forms.MessageBox.Show(sbLog.ToString());
            }
            else
            {
                //오류가 없었다면 그냥 종료합니다.
            }
        }
Exemple #2
0
        public static ProcessLaunchInfo startProcessNoActivate(string program, string arguments, bool haveToWaitFinishes)
        {
            // 프로그램의 작업 디렉토리를 확인합니다.
            string workDir = System.IO.Path.GetDirectoryName(program);

            if (workDir.Length <= 0)
            {
                // 작업 디렉토리 문자열의 길이가 0인 경우
                // 호출 디렉토리와 같은 곳에 있다는 뜻이므로
                // 실행 불능 오류 발생하는 빈 칸 대신 null을 넣습니다.
                workDir = null;
            }

            // 실행 정보를 보관했다가 실행 이후 활용하려합니다.
            ProcessLaunchInfo pliProcess = new ProcessLaunchInfo();

            STARTUPINFO si = new STARTUPINFO();

            si.cb      = Marshal.SizeOf(si);
            si.dwFlags = STARTF_USESHOWWINDOW;
            // 이 부분이 중요합니다. 최소화시킨 상태에서 포커스도 갖지 않도록 호출하게 합니다.
            si.wShowWindow = SW_SHOWMINNOACTIVE;

            PROCESS_INFORMATION pi = new PROCESS_INFORMATION();

            // 새 프로세스와 그의 주 스레드를 생성합니다.
            // 호출하는 프로세스의 보안 컨텍스트를 상속받습니다.
            // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx
            pliProcess.bLaunched = CreateProcess(
                program,        // 실행시킬 프로그램 이름
                arguments,      // 실행시킬 명령행 (최대 MAX_PATH:260 자)
                IntPtr.Zero,    // 보안 속성(SECURITY_ATTRIBUTES) 구조를 가리키는 포인터. 프로세스용
                IntPtr.Zero,    // 보안 속성(SECURITY_ATTRIBUTES) 구조를 가리키는 포인터. 스레드용
                true,           // 호출하는 핸들의 상속 여부
                0,              // 프로세스 생성 플래그. 여기에서 프로세스 우선순위도 지정 가능
                IntPtr.Zero,    // 실행 환경을 나타내는 포인터. 유니코드/ANSI 등의 인코딩의 영향을 줌. (NULL인 경우 호출 프로세스의 실행환경 상속)
                workDir,        // 작업 디렉토리. (NULL인 경우 호출 프로세스의 위치를 작업 디렉토리로 사용)
                ref si,         // 실행 방법에 대한 정보
                out pi          // 프로세스에 대한 정보
                );

            // 종료를 기다려야하는 경우 기다려줍니다.
            if (haveToWaitFinishes)
            {
                ProcessWaitHandle waitable = new ProcessWaitHandle(pi.hProcess);
                waitable.WaitOne();
            }

            // pInvoke를 통해 프로세스의 exitCode를 가져옵니다.
            //http://www.pinvoke.net/default.aspx/kernel32.getexitcodeprocess
            uint exitCode = 0;

            GetExitCodeProcess(pi.hProcess, out exitCode);

            CloseHandle(pi.hProcess);
            CloseHandle(pi.hThread);

            // ExitCode 속성은 부호 있는 32비트 정수입니다.
            // 이 속성이 음수 값을 반환하지 않게 하려면
            // 0x80000000 보다 크거나 같은 수를 쓰지 마십시오.
            //https://msdn.microsoft.com/en-us/library/system.environment.exitcode(v=vs.110).aspx
            if (exitCode >= 0x80000000)
            {
                // 2의 보수로 이루어진 음수 표현 체계에 맞게 보정해줍니다.
                // 최상위 비트를 1로 만든 다음,
                // unsigned int인 exitCode에서 0x7FFFFFFF로 걸러낸 값을 int에 맞게 우겨넣습니다.
                pliProcess.exitCode = 1 << (32 - 1) | (int)(exitCode & 0x7FFFFFFF);
            }
            else
            {
                pliProcess.exitCode = (int)exitCode;
            }

            return(pliProcess);
        }