Пример #1
0
//------------------------------------------------------------------------
///| Parsing the command line |///////////////////////////////////////////
//------------------------------------------------------------------------
// Returns:
//   true  - continue normal startup
//   false - exit process with iResult code
bool CConEmuStart::ParseCommandLine(LPCWSTR pszCmdLine, int& iResult)
{
	bool bRc = false;
	iResult = 100;

	_ASSERTE(pszCmdLine!=NULL);
	opt.cmdLine.Set(pszCmdLine ? pszCmdLine : L"");

	// pszCmdLine *may* or *may not* start with our executable or full path to our executable
	LPCWSTR pszTemp = opt.cmdLine;
	LPCWSTR cmdLineRest = SkipNonPrintable(opt.cmdLine);
	LPCWSTR pszName, pszArgStart;
	LPCWSTR psUnknown = NULL;
	CmdArg  szArg, szNext;
	CEStr   szExeName, szExeNameOnly;

	// Set %ConEmuArgs% env var
	// It may be useful if we need to restart ConEmu
	// from batch/script with the same arguments (selfupdate etc.)
	LPCWSTR pszCopyToEnvStart = NULL;

	// Have to get our exectuable name and name without extension
	szExeName.Set(PointToName(gpConEmu->ms_ConEmuExe));
	szExeNameOnly.Set(szExeName);
	wchar_t* pszDot = (wchar_t*)PointToExt(szExeNameOnly.ms_Val);
	_ASSERTE(pszDot);
	if (pszDot) *pszDot = 0;


	// Check the first argument in the command line (most probably it will be our executable path/name)
	if (!(pszTemp = NextArg(pszTemp, szArg)))
	{
		_ASSERTE(FALSE && "GetCommandLine() is empty");
		// Treat as empty command line, allow to start
		bRc = true; iResult = 0;
		goto wrap;
	}
	pszName = PointToName(szArg);
	if ((lstrcmpi(pszName, szExeName) == 0)
		|| (lstrcmpi(pszName, szExeNameOnly) == 0))
	{
		// OK, our executable was specified properly in the command line
		_ASSERTE(*pszTemp != L' ');
		cmdLineRest = SkipNonPrintable(pszTemp);
	}


	// Must be empty at the moment
	_ASSERTE(opt.runCommand.IsEmpty());

	// Does the command line contain our switches?
	// Or we need to append all switches to starting shell?
	if (cmdLineRest && *cmdLineRest)
	{
		pszTemp = cmdLineRest;
		if ((pszTemp = NextArg(pszTemp, szArg)))
		{
			if ((*szArg.ms_Val != L'/')
				&& (*szArg.ms_Val != L'-')
				/*&& !wcschr(szArg.ms_Val, L'/')*/
				)
			{
				// Save it for further use
				opt.runCommand.Set(cmdLineRest);
				// And do not process it (no switches at all)
				cmdLineRest = NULL;
				opt.params = -1;
			}
		}
	}

	struct RunAsAdmin
	{
		static bool Check(LPCWSTR asSwitch)
		{
			bool bRunAsAdmin = false; // isPressed(VK_SHIFT);
			return bRunAsAdmin;
		};
	};


	// Let parse the reset
	szArg.Empty();
	szNext.Empty();

	// Processing loop begin
	if (cmdLineRest && *cmdLineRest)
	{
		pszCopyToEnvStart = cmdLineRest;
		opt.cfgSwitches.Set(pszCopyToEnvStart);

		while ((cmdLineRest = NextArg(cmdLineRest, szArg, &pszArgStart)))
		{
			bool lbNotFound = false;

			TODO("Replace NeedNextArg with GetCfgParm?")
			#define NeedNextArg() \
				if (!(cmdLineRest = NextArg(cmdLineRest, szNext))) { iResult = CERR_CARGUMENT; goto wrap; }


			if (!szArg.IsPossibleSwitch())
			{
				// -- // continue; // Try next switch?

				// Show error on unknown switch
				psUnknown = pszArgStart;
				break;
			}

			// Main processing cycle
			{
				opt.params++;

				if (szArg.IsSwitch(L"-autosetup"))
				{
					BOOL lbTurnOn = TRUE;

					NeedNextArg();

					if (szNext.Compare(L"0") == 0)
					{
						lbTurnOn = FALSE;
					}
					else if (szNext.Compare(L"1") == 0)
					{
						NeedNextArg();

						DWORD dwAttr = GetFileAttributes(szNext);

						if (dwAttr == (DWORD)-1 || (dwAttr & FILE_ATTRIBUTE_DIRECTORY))
						{
							iResult = 102;
							goto wrap;
						}
					}
					else
					{
						iResult = CERR_CARGUMENT;
						goto wrap;
					}

					HKEY hk = NULL; DWORD dw;
					int nSetupRc = 100;

					if (0 != RegCreateKeyEx(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Command Processor"),
										   0, NULL, 0, KEY_ALL_ACCESS, NULL, &hk, &dw))
					{
						iResult = 103;
						goto wrap;
					}

					if (lbTurnOn)
					{
						size_t cchMax = szNext.GetLen();
						LPCWSTR pszArg1 = NULL;
						if (*cmdLineRest)
						{
							// May be ‘/GHWND=NEW’ or smth else
							pszArg1 = cmdLineRest;
							cchMax += _tcslen(pszArg1);
						}
						cchMax += 16; // + quotations, spaces and so on

						wchar_t* pszCmd = (wchar_t*)calloc(cchMax, sizeof(*pszCmd));
						swprintf_c(pszCmd, cchMax/*#SECURELEN*/, L"\"%s\"%s%s%s", szNext.ms_Val,
							pszArg1 ? L" \"" : L"", pszArg1 ? pszArg1 : L"", pszArg1 ? L"\"" : L"");


						if (0 == RegSetValueEx(hk, _T("AutoRun"), 0, REG_SZ, (LPBYTE)pszCmd,
											(DWORD)sizeof(*pszCmd)*(_tcslen(pszCmd)+1)))
							nSetupRc = 1;

						free(pszCmd);
					}
					else
					{
						if (0==RegDeleteValue(hk, _T("AutoRun")))
							nSetupRc = 1;
					}

					RegCloseKey(hk);
					// сбросить CreateInNewEnvironment для ConMan
					ResetConman();
					iResult = nSetupRc;
					goto wrap;
				}
				else if (szArg.OneOfSwitches(L"-bypass", L"-apparent", L"-system:", L"-interactive:", L"-demote"))
				{
					// -bypass
					// Этот ключик был придуман для прозрачного запуска консоли
					// в режиме администратора
					// (т.е. чтобы окно UAC нормально всплывало, но не мелькало консольное окно)
					// Но не получилось, пока требуются хэндлы процесса, а их не получается
					// передать в НЕ приподнятый процесс (исходный ConEmu GUI).

					// -apparent
					// Same as -bypass, but run the process as SW_SHOWNORMAL

					// -demote
					// Запуск процесса (ком.строка после "/demote") в режиме простого юзера,
					// когда текущий процесс уже запущен "под админом". "Понизить" текущие
					// привилегии просто так нельзя, поэтому запуск идет через TaskSheduler.

					// -system
					// Non-interactive process, started as System account
					// It's used when starting consoles, our server works fine as non-interactive

					// -interactive
					// Used when ConEmu.exe is started under System account,
					// but we need to give starting process interactive capabilities.

					_ASSERTE(opt.runCommand.IsEmpty());
					pszTemp = cmdLineRest;
					if ((pszTemp = NextArg(pszTemp, szNext))
						&& szNext.OneOfSwitches(L"-run",L"-cmd"))
					{
						opt.runCommand.Set(pszTemp);
					}
					else
					{
						opt.runCommand.Set(cmdLineRest);
					}

					if (opt.runCommand.IsEmpty())
					{
						CEStr lsMsg(L"Invalid command line. '", szArg, L"' exists, command line is empty");
						DisplayLastError(lsMsg, -1);
						goto wrap;
					}

					// Information
					#ifdef _DEBUG
					STARTUPINFO siOur = {sizeof(siOur)};
					GetStartupInfo(&siOur);
					#endif

					STARTUPINFO si = {sizeof(si)};
					PROCESS_INFORMATION pi = {};
					si.dwFlags = STARTF_USESHOWWINDOW;
					// Only `-demote` and `-apparent` switches were implemented to start application visible
					// All others are intended to run our server process, without blinking of course
					if (szArg.OneOfSwitches(L"-demote", L"-apparent"))
						si.wShowWindow = SW_SHOWNORMAL;
					else
						si.wShowWindow = SW_HIDE;

					wchar_t szCurDir[MAX_PATH+1] = L"";
					GetCurrentDirectory(countof(szCurDir), szCurDir);

					BOOL b;
					DWORD nErr = 0;

					// if we were started from TaskScheduler, it would be nice to wait a little
					// to let parent (creator of the scheduler task) know we were started successfully
					bool bFromScheduler = false;

					// Log the command to be started
					{
						CEStr lsLog(
							L"Starting process",
							L": ", szArg,
							L" `", opt.runCommand.ms_Val, L"`");
						LogString(lsLog);
					}

					if (szArg.IsSwitch(L"-demote"))
					{
						b = CreateProcessDemoted(opt.runCommand.ms_Val, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL,
							szCurDir, &si, &pi, &nErr);
					}
					else if (szArg.IsSwitch(L"-system:"))
					{
						DWORD nSessionID = wcstoul(szArg.ms_Val+wcslen(L"-system:"), NULL, 10);
						b = CreateProcessSystem(nSessionID, opt.runCommand.ms_Val, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL,
							szCurDir, &si, &pi);
					}
					else if (szArg.IsSwitch(L"-interactive:"))
					{
						DWORD nSessionID = wcstoul(szArg.ms_Val+wcslen(L"-interactive:"), NULL, 10);
						b = CreateProcessInteractive(nSessionID, NULL, opt.runCommand.ms_Val, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL,
							szCurDir, &si, &pi, &nErr);
						bFromScheduler = true;
					}
					else // -bypass, -apparent
					{
						b = CreateProcess(NULL, opt.runCommand.ms_Val, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL,
							NULL, &si, &pi);
						nErr = b ? 0 : GetLastError();
						bFromScheduler = true;
					}

					// Log the result
					{
						CEStr lsLog; wchar_t szExtra[32] = L"";
						if (b)
						{
							if (pi.dwProcessId)
								swprintf_c(szExtra, L", PID=%u", pi.dwProcessId);
							lsLog = lstrmerge(
								L"Process was created successfully",
								szExtra);
						}
						else
						{
							swprintf_c(szExtra, L", ErrorCode=%u", nErr);
							lsLog = lstrmerge(
								L"Failed to start process",
								szExtra);
						}
						LogString(lsLog);
					}

					// If the error was not shown yet
					if (nErr) DisplayLastError(opt.runCommand, nErr);

					// if we were started from TaskScheduler, it would be nice to wait a little
					// to let parent (creator of the scheduler task) know we were started successfully
					if (bFromScheduler)
					{
						LogString(L"Sleeping for 5 seconds");
						Sleep(5*1000);
					}

					// Success?
					if (b)
					{
						iResult = 0;
					}

					// Done, close handles, if they were opened
					SafeCloseHandle(pi.hProcess);
					SafeCloseHandle(pi.hThread);

					goto wrap;
				}
				else if (szArg.IsSwitch(L"-multi"))
				{
					gpConEmu->AppendExtraArgs(szArg);
					gpConEmu->opt.MultiConValue = true;
				}
				else if (szArg.IsSwitch(L"-NoMulti"))
				{
					gpConEmu->AppendExtraArgs(szArg);
					gpConEmu->opt.MultiConValue = false;
				}
				else if (szArg.IsSwitch(L"-visible"))
				{
					gpConEmu->opt.VisValue = true;
				}
				else if (szArg.OneOfSwitches(L"-ct", L"-cleartype", L"-ct0", L"-ct1", L"-ct2"))
				{
					switch (szArg[3])
					{
					case L'0':
						gpConEmu->opt.ClearTypeVal = NONANTIALIASED_QUALITY; break;
					case L'1':
						gpConEmu->opt.ClearTypeVal = ANTIALIASED_QUALITY; break;
					default:
						gpConEmu->opt.ClearTypeVal = CLEARTYPE_NATURAL_QUALITY;
					}
				}
				// Interface language
				else if (szArg.IsSwitch(L"-lng"))
				{
					NeedNextArg();

					if (!gpConEmu->opt.Language.Exists)
					{
						gpConEmu->opt.Language = (LPCWSTR)szNext;
						gpConEmu->AppendExtraArgs(L"-lng", szNext);
					}
				}
				// Optional specific "ConEmu.l10n"
				else if (szArg.IsSwitch(L"-lngfile"))
				{
					NeedNextArg();

					if (!gpConEmu->opt.LanguageFile.Exists)
					{
						gpConEmu->opt.LanguageFile = (LPCWSTR)szNext;
						gpConEmu->AppendExtraArgs(L"-lngfile", szNext);
					}
				}
				// Change font name
				else if (szArg.IsSwitch(L"-Font"))
				{
					NeedNextArg();

					if (!gpConEmu->opt.FontVal.Exists)
					{
						gpConEmu->opt.FontVal = (LPCWSTR)szNext;
						gpConEmu->AppendExtraArgs(L"-font", szNext);
					}
				}
				// Change font height
				else if (szArg.IsSwitch(L"-FontSize") || szArg.IsSwitch(L"-Size"))
				{
					NeedNextArg();

					if (!gpConEmu->opt.SizeVal.Exists)
					{
						gpConEmu->opt.SizeVal.SetInt(szNext);
					}
				}
				// ADD fontname; by Mors
				else if (szArg.IsSwitch(L"-FontFile"))
				{
					CESwitch szFile(sw_Str);
					if (!GetCfgParm(cmdLineRest, szFile, MAX_PATH))
					{
						goto wrap;
					}
					gpConEmu->AppendExtraArgs(L"-FontFile", szFile.GetStr());
					gpFontMgr->RegisterFont(szFile.GetStr(), TRUE);
				}
				// Register all fonts from specified directory
				else if (szArg.IsSwitch(L"-FontDir"))
				{
					CESwitch szDir(sw_Str);
					if (!GetCfgParm(cmdLineRest, szDir, MAX_PATH))
					{
						goto wrap;
					}
					gpConEmu->AppendExtraArgs(L"-FontDir", szDir.GetStr());
					gpFontMgr->RegisterFontsDir(szDir.GetStr());
				}
				else if (szArg.IsSwitch(L"-fs"))
				{
					gpConEmu->opt.WindowModeVal = wmFullScreen;
				}
				else if (szArg.IsSwitch(L"-max"))
				{
					gpConEmu->opt.WindowModeVal = wmMaximized;
				}
				else if (szArg.OneOfSwitches(L"-min", L"-MinTSA", L"-StartTSA"))
				{
					gpConEmu->WindowStartMinimized = true;
					if (!szArg.IsSwitch(L"-min"))
					{
						gpConEmu->WindowStartTsa = true;
						gpConEmu->WindowStartNoClose = szArg.IsSwitch(L"-MinTSA");
					}
				}
				else if (szArg.OneOfSwitches(L"-tsa", L"-tray"))
				{
					gpConEmu->ForceMinimizeToTray = true;
				}
				else if (szArg.IsSwitch(L"-detached"))
				{
					gpConEmu->m_StartDetached = crb_On;
					opt.Detached = true;
				}
				else if (szArg.IsSwitch(L"-NoAutoClose"))
				{
					opt.NoAutoClose = true;
				}
				else if (szArg.IsSwitch(L"-here"))
				{
					gpConEmu->mb_ConEmuHere = true;
					gpConEmu->StoreWorkDir();
				}
				else if (szArg.IsSwitch(L"-update"))
				{
					gpConEmu->opt.AutoUpdateOnStart = true;
				}
				else if (szArg.IsSwitch(L"-NoUpdate"))
				{
					// This one has more weight than AutoUpdateOnStart
					gpConEmu->opt.DisableAutoUpdate = true;
				}
				else if (szArg.IsSwitch(L"-NoHooksWarn"))
				{
					// Don't try to warn users about known problems with third-party detours
					gpConEmu->opt.NoHooksWarn = true;
				}
				else if (szArg.OneOfSwitches(L"-NoKeyHook", L"-NoKeyHooks", L"-NoKeybHook", L"-NoKeybHooks"))
				{
					gpConEmu->DisableKeybHooks = true;
				}
				else if (szArg.IsSwitch(L"-NoCloseConfirm"))
				{
					gpConEmu->DisableCloseConfirm = true;
				}
				else if (szArg.IsSwitch(L"-NoMacro"))
				{
					gpConEmu->DisableAllMacro = true;
				}
				else if (szArg.OneOfSwitches(L"-NoHotkey", L"-NoHotkeys"))
				{
					gpConEmu->DisableAllHotkeys = true;
				}
				else if (szArg.OneOfSwitches(L"-NoDefTrm", L"-NoDefTerm"))
				{
					gpConEmu->DisableSetDefTerm = true;
				}
				else if (szArg.OneOfSwitches(L"-NoRegFont", L"-NoRegFonts"))
				{
					gpConEmu->DisableRegisterFonts = true;
				}
				else if (szArg.OneOfSwitches(L"-inside", L"-inside="))
				{
					bool bRunAsAdmin = RunAsAdmin::Check(szArg.ms_Val);
					bool bSyncDir = false;
					LPCWSTR pszSyncFmt = NULL;

					gpConEmu->mb_ConEmuHere = true;
					gpConEmu->StoreWorkDir();

					// Both `-inside:...` and `-inside=...` are supported
					if (szArg.IsSwitch(L"-inside="))
					{
						bSyncDir = true;
						pszSyncFmt = szArg.ms_Val+8; // \eCD /d %1 - \e - ESC, \b - BS, \n - ENTER, %1 - "dir", %2 - "bash dir"
					}

					CConEmuInside::InitInside(bRunAsAdmin, bSyncDir, pszSyncFmt, 0, NULL);
				}
				else if (szArg.IsSwitch(L"-InsidePID"))
				{
					NeedNextArg();

					bool bRunAsAdmin = RunAsAdmin::Check(szArg.ms_Val);

					wchar_t* pszEnd;
					// Здесь указывается PID, в который нужно внедриться.
					DWORD nInsideParentPID = wcstol(szNext, &pszEnd, 10);
					if (nInsideParentPID)
					{
						CConEmuInside::InitInside(bRunAsAdmin, false, NULL, nInsideParentPID, NULL);
					}
				}
				else if (szArg.IsSwitch(L"-InsideWnd"))
				{
					NeedNextArg();

					LPCWSTR pszHWnd = szNext.ms_Val;

					if (pszHWnd[0] == L'0' && (pszHWnd[1] == L'x' || pszHWnd[1] == L'X'))
						pszHWnd += 2;
					else if (pszHWnd[0] == L'x' || pszHWnd[0] == L'X')
						pszHWnd ++;

					bool bRunAsAdmin = RunAsAdmin::Check(szArg.ms_Val);

					wchar_t* pszEnd;
					// Здесь указывается HWND, в котором нужно создаваться.
					HWND hParent = (HWND)(DWORD_PTR)wcstoul(pszHWnd, &pszEnd, 16);
					if (hParent && IsWindow(hParent))
					{
						CConEmuInside::InitInside(bRunAsAdmin, false, NULL, 0, hParent);
					}
				}
				else if (szArg.IsSwitch(L"-icon"))
				{
					NeedNextArg();

					if (!gpConEmu->opt.IconPrm.Exists && !szNext.IsEmpty())
					{
						gpConEmu->opt.IconPrm = true;
						gpConEmu->mps_IconPath = ExpandEnvStr(szNext);
					}
				}
				else if (szArg.IsSwitch(L"-dir"))
				{
					NeedNextArg();

					if (!szNext.IsEmpty())
					{
						// Например, "%USERPROFILE%"
						CEStr szExpand;
						if (wcschr(szNext, L'%') && ((szExpand = ExpandEnvStr(szNext)) != NULL))
						{
							gpConEmu->StoreWorkDir(szExpand);
						}
						else
						{
							gpConEmu->StoreWorkDir(szNext);
						}
					}
				}
				else if (szArg.IsSwitch(L"-UpdateJumpList"))
				{
					// Copy current Task list to Win7 Jump list (Taskbar icon)
					gpConEmu->mb_UpdateJumpListOnStartup = true;
				}
				else if (szArg.OneOfSwitches(L"-log", L"-log0", L"-log1", L"-log2", L"-log3", L"-log4"))
				{
					if (szArg.OneOfSwitches(L"-log", L"-log0"))
						gpConEmu->opt.AdvLogging.SetInt(1);
					else
						gpConEmu->opt.AdvLogging.SetInt((BYTE)(szArg[4] - L'0')); // 1..4
					// Do create logging service
					DEBUGSTRSTARTUP(L"Creating log file");
					gpConEmu->CreateLog();
				}
				else if (szArg.OneOfSwitches(L"-Single", L"-Reuse"))
				{
					// "/reuse" switch to be remastered
					gpConEmu->AppendExtraArgs(szArg);
					gpSetCls->SingleInstanceArg = sgl_Enabled;
				}
				else if (szArg.IsSwitch(L"-NoSingle"))
				{
					gpConEmu->AppendExtraArgs(szArg);
					gpSetCls->SingleInstanceArg = sgl_Disabled;
				}
				else if (szArg.IsSwitch(L"-DesktopMode"))
				{
					gpConEmu->opt.DesktopMode = true;
				}
				else if (szArg.OneOfSwitches(L"-Quake", L"-QuakeAuto", L"-NoQuake"))
				{
					if (szArg.IsSwitch(L"-Quake"))
						gpConEmu->opt.QuakeMode = 1;
					else if (szArg.IsSwitch(L"-QuakeAuto"))
						gpConEmu->opt.QuakeMode = 2;
					else
					{
						gpConEmu->opt.QuakeMode = 0;
						if (gpSetCls->SingleInstanceArg == sgl_Default)
							gpSetCls->SingleInstanceArg = sgl_Disabled;
					}
				}
				else if (szArg.OneOfSwitches(L"-FrameWidth", L"-Frame"))
				{
					NeedNextArg();

					if (!gpConEmu->opt.FrameWidth.Exists)
						gpConEmu->opt.FrameWidth.SetInt(szNext);
				}
				else if (szArg.OneOfSwitches(L"-ShowHide", L"-ShowHideTSA"))
				{
					gpSetCls->SingleInstanceArg = sgl_Enabled;
					gpSetCls->SingleInstanceShowHide = szArg.IsSwitch(L"-ShowHide")
						? sih_ShowMinimize : sih_ShowHideTSA;
				}
				else if (szArg.OneOfSwitches(L"-Reset", L"-ResetDefault", L"-Basic"))
				{
					gpConEmu->opt.ResetSettings = true;
					if (szArg.IsSwitch(L"-ResetDefault"))
					{
						gpSetCls->isFastSetupDisabled = true;
					}
					else if (szArg.IsSwitch(L"-Basic"))
					{
						gpSetCls->isFastSetupDisabled = true;
						gpSetCls->isResetBasicSettings = true;
					}
				}
				else if (szArg.OneOfSwitches(L"-NoCascade", L"-DontCascade"))
				{
					gpConEmu->AppendExtraArgs(szArg);
					gpSetCls->isDontCascade = true;
				}
				else if (szArg.OneOfSwitches(L"-WndX", L"-WndY", L"-WndW", L"-WndWidth", L"-WndH", L"-WndHeight"))
				{
					wchar_t ch = szArg[4];
					CharUpperBuff(&ch, 1);

					CESwitch psz(sw_Str); bool bParm = false;
					if (!GetCfgParm(cmdLineRest, bParm, psz, 32))
					{
						goto wrap;
					}
					gpConEmu->opt.SizePosPrm = true;

					// Direct X/Y implies /nocascade
					if (ch == _T('X') || ch == _T('Y'))
					{
						// TODO: isDontCascade must be in our opt struct !!!
						gpSetCls->isDontCascade = true;
					}

					switch (ch)
					{
					case _T('X'): gpConEmu->opt.sWndX.SetStr(psz.Str, sw_Str); break;
					case _T('Y'): gpConEmu->opt.sWndY.SetStr(psz.Str, sw_Str); break;
					case _T('W'): gpConEmu->opt.sWndW.SetStr(psz.Str, sw_Str); break;
					case _T('H'): gpConEmu->opt.sWndH.SetStr(psz.Str, sw_Str); break;
					}
				}
				else if (szArg.IsSwitch(L"-Monitor"))
				{
					CESwitch psz(sw_Str); bool bParm = false;
					if (!GetCfgParm(cmdLineRest, bParm, psz, 64))
					{
						goto wrap;
					}

					if ((gpConEmu->opt.Monitor.Mon = MonitorFromParam(psz.Str)) != NULL)
					{
						gpConEmu->opt.Monitor.Exists = true;
						gpConEmu->opt.Monitor.Type = sw_Int;
						gpStartEnv->hStartMon = gpConEmu->opt.Monitor.Mon;
					}
				}
				else if (szArg.IsSwitch(L"-Theme"))
				{
					const wchar_t* kDefaultTheme = L"DarkMode_Explorer";
					bool bParm = false;
					if (!cmdLineRest || (*cmdLineRest == L'-' || *cmdLineRest == L'/')
						|| !GetCfgParm(cmdLineRest, bParm, gpConEmu->opt.WindowTheme, 128))
					{
						gpConEmu->opt.WindowTheme.SetStr(kDefaultTheme);
					}
				}
				else if (szArg.OneOfSwitches(L"-Buffer", L"-BufferHeight"))
				{
					NeedNextArg();

					if (!gpConEmu->opt.BufferHeightVal.Exists)
					{
						gpConEmu->opt.BufferHeightVal.SetInt(szNext);

						if (gpConEmu->opt.BufferHeightVal.GetInt() < 0)
						{
							//setParent = true; -- Maximus5 - нефиг, все ручками
							gpConEmu->opt.BufferHeightVal = -gpConEmu->opt.BufferHeightVal.GetInt();
						}

						if (gpConEmu->opt.BufferHeightVal.GetInt() < LONGOUTPUTHEIGHT_MIN)
							gpConEmu->opt.BufferHeightVal = LONGOUTPUTHEIGHT_MIN;
						else if (gpConEmu->opt.BufferHeightVal.GetInt() > LONGOUTPUTHEIGHT_MAX)
							gpConEmu->opt.BufferHeightVal = LONGOUTPUTHEIGHT_MAX;
					}
				}
				else if (szArg.IsSwitch(L"-Config"))
				{
					// -- используем последний из параметров, если их несколько
					if (!GetCfgParm(cmdLineRest, gpConEmu->opt.ConfigVal, 127))
					{
						goto wrap;
					}
				}
				else if (szArg.IsSwitch(L"-Palette"))
				{
					// -- используем последний из параметров, если их несколько
					if (!GetCfgParm(cmdLineRest, gpConEmu->opt.PaletteVal, MAX_PATH))
					{
						goto wrap;
					}
				}
				else if (szArg.IsSwitch(L"-LoadRegistry"))
				{
					gpConEmu->AppendExtraArgs(szArg);
					gpConEmu->opt.ForceUseRegistryPrm = true;
				}
				else if (szArg.OneOfSwitches(L"-LoadCfgFile", L"-LoadXmlFile"))
				{
					// -- используем последний из параметров, если их несколько
					if (!GetCfgParm(cmdLineRest, gpConEmu->opt.LoadCfgFile, MAX_PATH, true))
					{
						goto wrap;
					}
				}
				else if (szArg.OneOfSwitches(L"-SaveCfgFile", L"-SaveXmlFile"))
				{
					// -- используем последний из параметров, если их несколько
					if (!GetCfgParm(cmdLineRest, gpConEmu->opt.SaveCfgFile, MAX_PATH, true))
					{
						goto wrap;
					}
				}
				else if (szArg.IsSwitch(L"-GuiMacro"))
				{
					// -- выполняется только последний
					if (!GetCfgParm(cmdLineRest, gpConEmu->opt.ExecGuiMacro, 0x8000, false))
					{
						goto wrap;
					}
				}
				else if (szArg.IsSwitch(L"-UpdateSrcSet"))
				{
					// -- используем последний из параметров, если их несколько
					if (!GetCfgParm(cmdLineRest, gpConEmu->opt.UpdateSrcSet, MAX_PATH*4, false))
					{
						goto wrap;
					}
				}
				else if (szArg.IsSwitch(L"-AnsiLog"))
				{
					// -- используем последний из параметров, если их несколько
					if (!GetCfgParm(cmdLineRest, gpConEmu->opt.AnsiLogPath, MAX_PATH-40, true))
					{
						goto wrap;
					}
				}
				else if (szArg.IsSwitch(L"-SetDefTerm"))
				{
					gpConEmu->opt.SetUpDefaultTerminal = true;
				}
				else if (szArg.IsSwitch(L"-ZoneId"))
				{
					gpConEmu->opt.FixZoneId = true;
				}
				else if (szArg.IsSwitch(L"-Exit"))
				{
					gpConEmu->opt.ExitAfterActionPrm = true;
				}
				else if (szArg.IsSwitch(L"-QuitOnClose"))
				{
					gpConEmu->mb_ForceQuitOnClose = true;
				}
				else if (szArg.IsSwitch(L"-Title"))
				{
					bool bOk = false;
					CESwitch pszTitle(sw_Str);
					if (!GetCfgParm(cmdLineRest, bOk, pszTitle, 127))
					{
						goto wrap;
					}
					gpConEmu->SetTitleTemplate(pszTitle.GetStr());
				}
				else if (szArg.IsSwitch(L"-Settings"))
				{
					gpConEmu->mb_SettingsRequested = true;
				}
				else if (szArg.IsSwitch(L"-FindBugMode"))
				{
					gpConEmu->mb_FindBugMode = true;
				}
				else if (szArg.OneOfSwitches(L"-debug", L"-debugw", L"-debugi"))
				{
					// These switches were already processed
				}
				else if (szArg.OneOfSwitches(L"-?", L"-h", L"-help"))
				{
					if (gpLng) gpLng->Reload();
					ConEmuAbout::OnInfo_About();
					iResult = -1;
					goto wrap;
				}
				// Final `-run ...` or `-runlist ...` (old names `-cmd ...` or `-cmdlist ...`)
				else if (
					szArg.OneOfSwitches(L"-run", L"-runlist", L"-cmd", L"-cmdlist")
					)
				{
					if (opt.cfgSwitches.ms_Val)
					{
						_ASSERTE(pszArgStart>=pszCopyToEnvStart); // If there is only "-run cmd" in arguments
						_ASSERTE((INT_PTR)(pszArgStart - pszCopyToEnvStart) <= opt.cfgSwitches.GetLen());
						opt.cfgSwitches.ms_Val[pszArgStart - pszCopyToEnvStart] = 0;
					}

					opt.runCommand.Set(SkipNonPrintable(cmdLineRest));
					opt.isScript = szArg.OneOfSwitches(L"-runlist", L"-cmdlist");
					break;
				}
				else
				{
					// Show error on unknown switch
					psUnknown = pszArgStart;
					break;
				}
			}
			// Main processing cycle end

			// Avoid assertions in NextArg
			szArg.Empty(); szNext.Empty();
		} // while (NextArg(&cmdLineRest, szArg, &pszArgStart) == 0)
	}
Пример #2
0
//------------------------------------------------------------------------
///| Parsing the command line |///////////////////////////////////////////
//------------------------------------------------------------------------
// Returns:
//   true  - continue normal startup
//   false - exit process with iResult code
bool CConEmuStart::ParseCommandLine(LPCWSTR pszCmdLine, int& iResult)
{
	bool bRc = false;
	iResult = 100;

	_ASSERTE(pszCmdLine!=NULL);
	opt.cmdLine.Set(pszCmdLine ? pszCmdLine : L"");

	// pszCmdLine *may* or *may not* start with our executable or full path to our executable
	LPCWSTR pszTemp = opt.cmdLine;
	LPCWSTR cmdLineRest = SkipNonPrintable(opt.cmdLine);
	LPCWSTR pszName, pszArgStart;
	LPCWSTR psUnknown = NULL;
	CEStr   szArg, szNext;
	CEStr   szExeName, szExeNameOnly;

	// Set %ConEmuArgs% env var
	// It may be usefull if we need to restart ConEmu
	// from batch/script with the same arguments (selfupdate etc.)
	LPCWSTR pszCopyToEnvStart = NULL;

	// Have to get our exectuable name and name without extension
	szExeName.Set(PointToName(gpConEmu->ms_ConEmuExe));
	szExeNameOnly.Set(szExeName);
	wchar_t* pszDot = (wchar_t*)PointToExt(szExeNameOnly.ms_Val);
	_ASSERTE(pszDot);
	if (pszDot) *pszDot = 0;


	// Check the first argument in the command line (most probably it will be our executable path/name)
	if (NextArg(&pszTemp, szArg) != 0)
	{
		_ASSERTE(FALSE && "GetCommandLine() is empty");
		// Treat as empty command line, allow to start
		bRc = true; iResult = 0;
		goto wrap;
	}
	pszName = PointToName(szArg);
	if ((lstrcmpi(pszName, szExeName) == 0)
		|| (lstrcmpi(pszName, szExeNameOnly) == 0))
	{
		// OK, our executable was specified properly in the command line
		_ASSERTE(*pszTemp != L' ');
		cmdLineRest = SkipNonPrintable(pszTemp);
	}


	// Must be empty at the moment
	_ASSERTE(opt.runCommand.IsEmpty());

	// Does the command line contain our switches?
	// Or we need to append all switches to starting shell?
	if (cmdLineRest && *cmdLineRest)
	{
		pszTemp = cmdLineRest;
		if (NextArg(&pszTemp, szArg) == 0)
		{
			if ((*szArg.ms_Val != L'/')
				&& (*szArg.ms_Val != L'-')
				/*&& !wcschr(szArg.ms_Val, L'/')*/
				)
			{
				// Save it for further use
				opt.runCommand.Set(cmdLineRest);
				// And do not process it (no switches at all)
				cmdLineRest = NULL;
				opt.params = -1;
			}
		}
	}


	// Let parse the reset
	szArg.Empty();
	szNext.Empty();

	// Processing loop begin
	if (cmdLineRest && *cmdLineRest)
	{
		pszCopyToEnvStart = cmdLineRest;
		opt.cfgSwitches.Set(pszCopyToEnvStart);

		while (NextArg(&cmdLineRest, szArg, &pszArgStart) == 0)
		{
			bool lbNotFound = false;

			// ':' removed from checks because otherwise it will not warn
			// on invalid usage of "-new_console:a" for example
			if (szArg.ms_Val[0] == L'-' && szArg.ms_Val[1] && !wcspbrk(szArg.ms_Val+1, L"\\//|.&<>^"))
			{
				// Seems this is to be the "switch" too
				// Use both notations ('-' and '/')
				*szArg.ms_Val = L'/';
			}

			LPCWSTR curCommand = szArg.ms_Val;

			#define NeedNextArg() \
				if (NextArg(&cmdLineRest, szNext) != 0) { iResult = 101; goto wrap; } \
				curCommand = szNext.ms_Val;


			if (*curCommand != L'/')
			{
				continue; // Try next switch?
			}
			else
			{
				opt.params++;

				if (!klstricmp(curCommand, _T("/autosetup")))
				{
					BOOL lbTurnOn = TRUE;

					NeedNextArg();

					if (*curCommand == _T('0'))
					{
						lbTurnOn = FALSE;
					}
					else
					{
						NeedNextArg();

						DWORD dwAttr = GetFileAttributes(curCommand);

						if (dwAttr == (DWORD)-1 || (dwAttr & FILE_ATTRIBUTE_DIRECTORY))
						{
							iResult = 102;
							goto wrap;
						}
					}

					HKEY hk = NULL; DWORD dw;
					int nSetupRc = 100;

					if (0 != RegCreateKeyEx(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Command Processor"),
										   0, NULL, 0, KEY_ALL_ACCESS, NULL, &hk, &dw))
					{
						iResult = 103;
						goto wrap;
					}

					if (lbTurnOn)
					{
						size_t cchMax = _tcslen(curCommand);
						LPCWSTR pszArg1 = NULL;
						if (*cmdLineRest)
						{
							// May be ‘/GHWND=NEW’ or smth else
							pszArg1 = cmdLineRest;
							cchMax += _tcslen(pszArg1);
						}
						cchMax += 16; // + quotations, spaces and so on

						wchar_t* pszCmd = (wchar_t*)calloc(cchMax, sizeof(*pszCmd));
						_wsprintf(pszCmd, SKIPLEN(cchMax) L"\"%s\"%s%s%s", curCommand,
							pszArg1 ? L" \"" : L"", pszArg1 ? pszArg1 : L"", pszArg1 ? L"\"" : L"");


						if (0 == RegSetValueEx(hk, _T("AutoRun"), 0, REG_SZ, (LPBYTE)pszCmd,
											(DWORD)sizeof(*pszCmd)*(_tcslen(pszCmd)+1)))
							nSetupRc = 1;

						free(pszCmd);
					}
					else
					{
						if (0==RegDeleteValue(hk, _T("AutoRun")))
							nSetupRc = 1;
					}

					RegCloseKey(hk);
					// сбросить CreateInNewEnvironment для ConMan
					ResetConman();
					iResult = nSetupRc;
					goto wrap;
				}
				else if (!klstricmp(curCommand, _T("/bypass"))
					|| !klstricmp(curCommand, _T("/apparent"))
					|| !klstricmp(curCommand, _T("/system"))
					|| !klstricmp(curCommand, _T("/interactive"))
					|| !klstricmp(curCommand, _T("/demote")))
				{
					// -bypass
					// Этот ключик был придуман для прозрачного запуска консоли
					// в режиме администратора
					// (т.е. чтобы окно UAC нормально всплывало, но не мелькало консольное окно)
					// Но не получилось, пока требуются хэндлы процесса, а их не получается
					// передать в НЕ приподнятый процесс (исходный ConEmu GUI).

					// -apparent
					// Same as -bypass, but run the process as SW_SHOWNORMAL

					// -demote
					// Запуск процесса (ком.строка после "/demote") в режиме простого юзера,
					// когда текущий процесс уже запущен "под админом". "Понизить" текущие
					// привилегии просто так нельзя, поэтому запуск идет через TaskSheduler.

					// -system
					// Non-interactive process, started as System account
					// It's used when starting consoles, our server works fine as non-interactive

					// -interactive
					// Used when ConEmu.exe is started under System account,
					// but we need to give starting process interactive capabilities.

					_ASSERTE(opt.runCommand.IsEmpty());
					pszTemp = cmdLineRest;
					if ((NextArg(&pszTemp, szNext) == 0)
						&& (szNext.ms_Val[0] == L'-' || szNext.ms_Val[0] == L'/')
						&& (lstrcmpi(szNext.ms_Val+1, L"cmd") == 0))
					{
						opt.runCommand.Set(pszTemp);
					}
					else
					{
						opt.runCommand.Set(cmdLineRest);
					}

					if (opt.runCommand.IsEmpty())
					{
						CEStr lsMsg(L"Invalid cmd line. '", curCommand, L"' exists, command line is empty");
						DisplayLastError(lsMsg, -1);
						goto wrap;
					}

					// Information
					#ifdef _DEBUG
					STARTUPINFO siOur = {sizeof(siOur)};
					GetStartupInfo(&siOur);
					#endif

					STARTUPINFO si = {sizeof(si)};
					PROCESS_INFORMATION pi = {};
					si.dwFlags = STARTF_USESHOWWINDOW;
					// Only `-demote` and `-apparent` switches were implemented to start application visible
					// All others are intended to run our server process, without blinking of course
					if ((0 == klstricmp(curCommand, _T("/demote")))
						|| (0 == klstricmp(curCommand, _T("/apparent"))))
						si.wShowWindow = SW_SHOWNORMAL;
					else
						si.wShowWindow = SW_HIDE;

					wchar_t szCurDir[MAX_PATH+1] = L"";
					GetCurrentDirectory(countof(szCurDir), szCurDir);

					BOOL b;
					DWORD nErr = 0;

					// if we were started from TaskScheduler, it would be nice to wait a little
					// to let parent (creator of the scheduler task) know we were started successfully
					bool bFromScheduler = false;

					// Log the command to be started
					{
						CEStr lsLog(
							L"Starting process",
							L": ", curCommand,
							L" `", opt.runCommand.ms_Val, L"`");
						LogString(lsLog);
					}

					if (!klstricmp(curCommand, _T("/demote")))
					{
						b = CreateProcessDemoted(opt.runCommand.ms_Val, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL,
							szCurDir, &si, &pi, &nErr);
					}
					else if (!klstricmp(curCommand, _T("/system")))
					{
						b = CreateProcessSystem(opt.runCommand.ms_Val, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL,
							szCurDir, &si, &pi);
					}
					else if (!klstricmp(curCommand, _T("/interactive")))
					{
						b = CreateProcessInteractive((DWORD)-1, NULL, opt.runCommand.ms_Val, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL,
							szCurDir, &si, &pi, &nErr);
						bFromScheduler = true;
					}
					else // -bypass, -apparent
					{
						b = CreateProcess(NULL, opt.runCommand.ms_Val, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL,
							NULL, &si, &pi);
						nErr = b ? 0 : GetLastError();
						bFromScheduler = true;
					}

					// Log the result
					{
						CEStr lsLog; wchar_t szExtra[32] = L"";
						if (b)
						{
							if (pi.dwProcessId)
								_wsprintf(szExtra, SKIPCOUNT(szExtra) L", PID=%u", pi.dwProcessId);
							lsLog = lstrmerge(
								L"Process was created successfully",
								szExtra);
						}
						else
						{
							_wsprintf(szExtra, SKIPCOUNT(szExtra) L", ErrorCode=%u", nErr);
							lsLog = lstrmerge(
								L"Failed to start process",
								szExtra);
						}
						LogString(lsLog);
					}

					// If the error was not shown yet
					if (nErr) DisplayLastError(opt.runCommand, nErr);

					// if we were started from TaskScheduler, it would be nice to wait a little
					// to let parent (creator of the scheduler task) know we were started successfully
					if (bFromScheduler)
					{
						LogString(L"Sleeping for 5 seconds");
						Sleep(5*1000);
					}

					// Success?
					if (b)
					{
						iResult = 0;
					}

					// Done, close handles, if they were opened
					SafeCloseHandle(pi.hProcess);
					SafeCloseHandle(pi.hThread);

					goto wrap;
				}
				else if (!klstricmp(curCommand, _T("/multi")))
				{
					gpConEmu->AppendExtraArgs(curCommand);
					gpConEmu->opt.MultiConValue = true;
				}
				else if (!klstricmp(curCommand, _T("/nomulti")))
				{
					gpConEmu->AppendExtraArgs(curCommand);
					gpConEmu->opt.MultiConValue = false;
				}
				else if (!klstricmp(curCommand, _T("/visible")))
				{
					gpConEmu->opt.VisValue = true;
				}
				else if (!klstricmp(curCommand, _T("/ct")) || !klstricmp(curCommand, _T("/cleartype"))
					|| !klstricmp(curCommand, _T("/ct0")) || !klstricmp(curCommand, _T("/ct1")) || !klstricmp(curCommand, _T("/ct2")))
				{
					switch (curCommand[3])
					{
					case L'0':
						gpConEmu->opt.ClearTypeVal = NONANTIALIASED_QUALITY; break;
					case L'1':
						gpConEmu->opt.ClearTypeVal = ANTIALIASED_QUALITY; break;
					default:
						gpConEmu->opt.ClearTypeVal = CLEARTYPE_NATURAL_QUALITY;
					}
				}
				// Interface language
				else if (!klstricmp(curCommand, _T("/lng")))
				{
					NeedNextArg();

					if (!gpConEmu->opt.Language.Exists)
					{
						gpConEmu->opt.Language = curCommand;
						gpConEmu->AppendExtraArgs(L"/lng", curCommand);
					}
				}
				// Optional specific "ConEmu.l10n"
				else if (!klstricmp(curCommand, _T("/lngfile")))
				{
					NeedNextArg();

					if (!gpConEmu->opt.LanguageFile.Exists)
					{
						gpConEmu->opt.LanguageFile = curCommand;
						gpConEmu->AppendExtraArgs(L"/lngfile", curCommand);
					}
				}
				// имя шрифта
				else if (!klstricmp(curCommand, _T("/font")))
				{
					NeedNextArg();

					if (!gpConEmu->opt.FontVal.Exists)
					{
						gpConEmu->opt.FontVal = curCommand;
						gpConEmu->AppendExtraArgs(L"/font", curCommand);
					}
				}
				// Высота шрифта
				else if (!klstricmp(curCommand, _T("/size")))
				{
					NeedNextArg();

					if (!gpConEmu->opt.SizeVal.Exists)
					{
						gpConEmu->opt.SizeVal.SetInt(curCommand);
					}
				}
				// ADD fontname; by Mors
				else if (!klstricmp(curCommand, _T("/fontfile")))
				{
					CESwitch szFile(sw_Str);
					if (!GetCfgParm(cmdLineRest, szFile, MAX_PATH))
					{
						goto wrap;
					}
					gpConEmu->AppendExtraArgs(L"/fontfile", szFile.GetStr());
					gpFontMgr->RegisterFont(szFile.GetStr(), TRUE);
				}
				// Register all fonts from specified directory
				else if (!klstricmp(curCommand, _T("/fontdir")))
				{
					CESwitch szDir(sw_Str);
					if (!GetCfgParm(cmdLineRest, szDir, MAX_PATH))
					{
						goto wrap;
					}
					gpConEmu->AppendExtraArgs(L"/fontdir", szDir.GetStr());
					gpFontMgr->RegisterFontsDir(szDir.GetStr());
				}
				else if (!klstricmp(curCommand, _T("/fs")))
				{
					gpConEmu->opt.WindowModeVal = wmFullScreen;
				}
				else if (!klstricmp(curCommand, _T("/max")))
				{
					gpConEmu->opt.WindowModeVal = wmMaximized;
				}
				else if (!klstricmp(curCommand, _T("/min"))
					|| !klstricmp(curCommand, _T("/mintsa"))
					|| !klstricmp(curCommand, _T("/starttsa")))
				{
					gpConEmu->WindowStartMinimized = true;
					if (klstricmp(curCommand, _T("/min")) != 0)
					{
						gpConEmu->WindowStartTsa = true;
						gpConEmu->WindowStartNoClose = (klstricmp(curCommand, _T("/mintsa")) == 0);
					}
				}
				else if (!klstricmp(curCommand, _T("/tsa")) || !klstricmp(curCommand, _T("/tray")))
				{
					gpConEmu->ForceMinimizeToTray = true;
				}
				else if (!klstricmp(curCommand, _T("/detached")))
				{
					gpConEmu->m_StartDetached = crb_On;
				}
				else if (!klstricmp(curCommand, _T("/here")))
				{
					gpConEmu->mb_ConEmuHere = true;
					gpConEmu->StoreWorkDir();
				}
				else if (!klstricmp(curCommand, _T("/update")))
				{
					gpConEmu->opt.AutoUpdateOnStart = true;
				}
				else if (!klstricmp(curCommand, _T("/noupdate")))
				{
					// This one has more weight than AutoUpdateOnStart
					gpConEmu->opt.DisableAutoUpdate = true;
				}
				else if (!klstricmp(curCommand, _T("/nokeyhook"))
					|| !klstricmp(curCommand, _T("/nokeyhooks"))
					|| !klstricmp(curCommand, _T("/nokeybhook"))
					|| !klstricmp(curCommand, _T("/nokeybhooks")))
				{
					gpConEmu->DisableKeybHooks = true;
				}
				else if (!klstricmp(curCommand, _T("/nocloseconfirm")))
				{
					gpConEmu->DisableCloseConfirm = true;
				}
				else if (!klstricmp(curCommand, _T("/nomacro")))
				{
					gpConEmu->DisableAllMacro = true;
				}
				else if (!klstricmp(curCommand, _T("/nohotkey"))
					|| !klstricmp(curCommand, _T("/nohotkeys")))
				{
					gpConEmu->DisableAllHotkeys = true;
				}
				else if (!klstricmp(curCommand, _T("/nodeftrm"))
					|| !klstricmp(curCommand, _T("/nodefterm")))
				{
					gpConEmu->DisableSetDefTerm = true;
				}
				else if (!klstricmp(curCommand, _T("/noregfont"))
					|| !klstricmp(curCommand, _T("/noregfonts")))
				{
					gpConEmu->DisableRegisterFonts = true;
				}
				else if (!klstricmp(curCommand, _T("/inside"))
					|| !lstrcmpni(curCommand, _T("/inside="), 8))
				{
					bool bRunAsAdmin = isPressed(VK_SHIFT);
					bool bSyncDir = false;
					LPCWSTR pszSyncFmt = NULL;

					gpConEmu->mb_ConEmuHere = true;
					gpConEmu->StoreWorkDir();

					if (curCommand[7] == _T('='))
					{
						bSyncDir = true;
						pszSyncFmt = curCommand+8; // \eCD /d %1 - \e - ESC, \b - BS, \n - ENTER, %1 - "dir", %2 - "bash dir"
					}

					CConEmuInside::InitInside(bRunAsAdmin, bSyncDir, pszSyncFmt, 0, NULL);
				}
				else if (!klstricmp(curCommand, _T("/insidepid")))
				{
					NeedNextArg();

					bool bRunAsAdmin = isPressed(VK_SHIFT);

					wchar_t* pszEnd;
					// Здесь указывается PID, в который нужно внедриться.
					DWORD nInsideParentPID = wcstol(curCommand, &pszEnd, 10);
					if (nInsideParentPID)
					{
						CConEmuInside::InitInside(bRunAsAdmin, false, NULL, nInsideParentPID, NULL);
					}
				}
				else if (!klstricmp(curCommand, _T("/insidewnd")))
				{
					NeedNextArg();

					if (curCommand[0] == L'0' && (curCommand[1] == L'x' || curCommand[1] == L'X'))
						curCommand += 2;
					else if (curCommand[0] == L'x' || curCommand[0] == L'X')
						curCommand ++;

					bool bRunAsAdmin = isPressed(VK_SHIFT);

					wchar_t* pszEnd;
					// Здесь указывается HWND, в котором нужно создаваться.
					HWND hParent = (HWND)(DWORD_PTR)wcstoul(curCommand, &pszEnd, 16);
					if (hParent && IsWindow(hParent))
					{
						CConEmuInside::InitInside(bRunAsAdmin, false, NULL, 0, hParent);
					}
				}
				else if (!klstricmp(curCommand, _T("/icon")))
				{
					NeedNextArg();

					if (!gpConEmu->opt.IconPrm.Exists && *curCommand)
					{
						gpConEmu->opt.IconPrm = true;
						gpConEmu->mps_IconPath = ExpandEnvStr(curCommand);
					}
				}
				else if (!klstricmp(curCommand, _T("/dir")))
				{
					NeedNextArg();

					if (*curCommand)
					{
						// Например, "%USERPROFILE%"
						wchar_t* pszExpand = NULL;
						if (wcschr(curCommand, L'%') && ((pszExpand = ExpandEnvStr(curCommand)) != NULL))
						{
							gpConEmu->StoreWorkDir(pszExpand);
							SafeFree(pszExpand);
						}
						else
						{
							gpConEmu->StoreWorkDir(curCommand);
						}
					}
				}
				else if (!klstricmp(curCommand, _T("/updatejumplist")))
				{
					// Copy current Task list to Win7 Jump list (Taskbar icon)
					gpConEmu->mb_UpdateJumpListOnStartup = true;
				}
				else if (!klstricmp(curCommand, L"/log") || !klstricmp(curCommand, L"/log0")
					|| !klstricmp(curCommand, L"/log1") || !klstricmp(curCommand, L"/log2")
					|| !klstricmp(curCommand, L"/log3") || !klstricmp(curCommand, L"/log4"))
				{
					if (!klstricmp(curCommand, L"/log") || !klstricmp(curCommand, L"/log0"))
						gpConEmu->opt.AdvLogging.SetInt(1);
					else
						gpConEmu->opt.AdvLogging.SetInt((BYTE)(curCommand[4] - L'0')); // 1..4
					// Do create logging service
					DEBUGSTRSTARTUP(L"Creating log file");
					gpConEmu->CreateLog();
				}
				else if (!klstricmp(curCommand, _T("/single")) || !klstricmp(curCommand, _T("/reuse")))
				{
					// "/reuse" switch to be remastered
					gpConEmu->AppendExtraArgs(curCommand);
					gpSetCls->SingleInstanceArg = sgl_Enabled;
				}
				else if (!klstricmp(curCommand, _T("/nosingle")))
				{
					gpConEmu->AppendExtraArgs(curCommand);
					gpSetCls->SingleInstanceArg = sgl_Disabled;
				}
				else if (!klstricmp(curCommand, _T("/DesktopMode")))
				{
					gpConEmu->opt.DesktopMode = true;
				}
				else if (!klstricmp(curCommand, _T("/quake"))
					|| !klstricmp(curCommand, _T("/quakeauto"))
					|| !klstricmp(curCommand, _T("/noquake")))
				{
					if (!klstricmp(curCommand, _T("/quake")))
						gpConEmu->opt.QuakeMode = 1;
					else if (!klstricmp(curCommand, _T("/quakeauto")))
						gpConEmu->opt.QuakeMode = 2;
					else
					{
						gpConEmu->opt.QuakeMode = 0;
						if (gpSetCls->SingleInstanceArg == sgl_Default)
							gpSetCls->SingleInstanceArg = sgl_Disabled;
					}
				}
				else if (!klstricmp(curCommand, _T("/showhide")) || !klstricmp(curCommand, _T("/showhideTSA")))
				{
					gpSetCls->SingleInstanceArg = sgl_Enabled;
					gpSetCls->SingleInstanceShowHide = !klstricmp(curCommand, _T("/showhide"))
						? sih_ShowMinimize : sih_ShowHideTSA;
				}
				else if (!klstricmp(curCommand, _T("/reset"))
					|| !klstricmp(curCommand, _T("/resetdefault"))
					|| !klstricmp(curCommand, _T("/basic")))
				{
					gpConEmu->opt.ResetSettings = true;
					if (!klstricmp(curCommand, _T("/resetdefault")))
					{
						gpSetCls->isFastSetupDisabled = true;
					}
					else if (!klstricmp(curCommand, _T("/basic")))
					{
						gpSetCls->isFastSetupDisabled = true;
						gpSetCls->isResetBasicSettings = true;
					}
				}
				else if (!klstricmp(curCommand, _T("/nocascade"))
					|| !klstricmp(curCommand, _T("/dontcascade")))
				{
					gpConEmu->AppendExtraArgs(curCommand);
					gpSetCls->isDontCascade = true;
				}
				else if (!klstricmp(curCommand, _T("/WndX")) || !klstricmp(curCommand, _T("/WndY"))
					|| !klstricmp(curCommand, _T("/WndW")) || !klstricmp(curCommand, _T("/WndWidth"))
					|| !klstricmp(curCommand, _T("/WndH")) || !klstricmp(curCommand, _T("/WndHeight")))
				{
					TCHAR ch = curCommand[4];
					CharUpperBuff(&ch, 1);

					CESwitch psz(sw_Str); bool bParm = false;
					if (!GetCfgParm(cmdLineRest, bParm, psz, 32))
					{
						goto wrap;
					}
					gpConEmu->opt.SizePosPrm = true;

					// Direct X/Y implies /nocascade
					if (ch == _T('X') || ch == _T('Y'))
					{
						// TODO: isDontCascade must be in our opt struct !!!
						gpSetCls->isDontCascade = true;
					}

					switch (ch)
					{
					case _T('X'): gpConEmu->opt.sWndX.SetStr(psz.Str, sw_Str); break;
					case _T('Y'): gpConEmu->opt.sWndY.SetStr(psz.Str, sw_Str); break;
					case _T('W'): gpConEmu->opt.sWndW.SetStr(psz.Str, sw_Str); break;
					case _T('H'): gpConEmu->opt.sWndH.SetStr(psz.Str, sw_Str); break;
					}
				}
				else if (!klstricmp(curCommand, _T("/Monitor")))
				{
					CESwitch psz(sw_Str); bool bParm = false;
					if (!GetCfgParm(cmdLineRest, bParm, psz, 64))
					{
						goto wrap;
					}

					if ((gpConEmu->opt.Monitor.Mon = MonitorFromParam(psz.Str)) != NULL)
					{
						gpConEmu->opt.Monitor.Exists = true;
						gpConEmu->opt.Monitor.Type = sw_Int;
						gpStartEnv->hStartMon = gpConEmu->opt.Monitor.Mon;
					}
				}
				else if (!klstricmp(curCommand, _T("/Buffer")) || !klstricmp(curCommand, _T("/BufferHeight")))
				{
					NeedNextArg();

					if (!gpConEmu->opt.BufferHeightVal.Exists)
					{
						gpConEmu->opt.BufferHeightVal.SetInt(curCommand);

						if (gpConEmu->opt.BufferHeightVal.GetInt() < 0)
						{
							//setParent = true; -- Maximus5 - нефиг, все ручками
							gpConEmu->opt.BufferHeightVal = -gpConEmu->opt.BufferHeightVal.GetInt();
						}

						if (gpConEmu->opt.BufferHeightVal.GetInt() < LONGOUTPUTHEIGHT_MIN)
							gpConEmu->opt.BufferHeightVal = LONGOUTPUTHEIGHT_MIN;
						else if (gpConEmu->opt.BufferHeightVal.GetInt() > LONGOUTPUTHEIGHT_MAX)
							gpConEmu->opt.BufferHeightVal = LONGOUTPUTHEIGHT_MAX;
					}
				}
				else if (!klstricmp(curCommand, _T("/Config")))
				{
					// -- используем последний из параметров, если их несколько
					if (!GetCfgParm(cmdLineRest, gpConEmu->opt.ConfigVal, 127))
					{
						goto wrap;
					}
				}
				else if (!klstricmp(curCommand, _T("/Palette")))
				{
					// -- используем последний из параметров, если их несколько
					if (!GetCfgParm(cmdLineRest, gpConEmu->opt.PaletteVal, MAX_PATH))
					{
						goto wrap;
					}
				}
				else if (!klstricmp(curCommand, _T("/LoadRegistry")))
				{
					gpConEmu->AppendExtraArgs(curCommand);
					gpConEmu->opt.ForceUseRegistryPrm = true;
				}
				else if (!klstricmp(curCommand, _T("/LoadCfgFile")) || !klstricmp(curCommand, _T("/LoadXmlFile")))
				{
					// -- используем последний из параметров, если их несколько
					if (!GetCfgParm(cmdLineRest, gpConEmu->opt.LoadCfgFile, MAX_PATH, true))
					{
						goto wrap;
					}
				}
				else if (!klstricmp(curCommand, _T("/SaveCfgFile")) || !klstricmp(curCommand, _T("/SaveXmlFile")))
				{
					// -- используем последний из параметров, если их несколько
					if (!GetCfgParm(cmdLineRest, gpConEmu->opt.SaveCfgFile, MAX_PATH, true))
					{
						goto wrap;
					}
				}
				else if (!klstricmp(curCommand, _T("/GuiMacro")))
				{
					// -- выполняется только последний
					if (!GetCfgParm(cmdLineRest, gpConEmu->opt.ExecGuiMacro, 0x8000, false))
					{
						goto wrap;
					}
				}
				else if (!klstricmp(curCommand, _T("/UpdateSrcSet")))
				{
					// -- используем последний из параметров, если их несколько
					if (!GetCfgParm(cmdLineRest, gpConEmu->opt.UpdateSrcSet, MAX_PATH*4, false))
					{
						goto wrap;
					}
				}
				else if (!klstricmp(curCommand, _T("/AnsiLog")))
				{
					// -- используем последний из параметров, если их несколько
					if (!GetCfgParm(cmdLineRest, gpConEmu->opt.AnsiLogPath, MAX_PATH-40, true))
					{
						goto wrap;
					}
				}
				else if (!klstricmp(curCommand, _T("/SetDefTerm")))
				{
					gpConEmu->opt.SetUpDefaultTerminal = true;
				}
				else if (!klstricmp(curCommand, _T("/ZoneId")))
				{
					gpConEmu->opt.FixZoneId = true;
				}
				else if (!klstricmp(curCommand, _T("/Exit")))
				{
					gpConEmu->opt.ExitAfterActionPrm = true;
				}
				else if (!klstricmp(curCommand, _T("/QuitOnClose")))
				{
					gpConEmu->mb_ForceQuitOnClose = true;
				}
				else if (!klstricmp(curCommand, _T("/Title")))
				{
					bool bOk = false;
					CESwitch pszTitle(sw_Str);
					if (!GetCfgParm(cmdLineRest, bOk, pszTitle, 127))
					{
						goto wrap;
					}
					gpConEmu->SetTitleTemplate(pszTitle.GetStr());
				}
				else if (!klstricmp(curCommand, _T("/FindBugMode")))
				{
					gpConEmu->mb_FindBugMode = true;
				}
				else if (!klstricmp(curCommand, _T("/debug"))
					|| !klstricmp(curCommand, _T("/debugw"))
					|| !klstricmp(curCommand, _T("/debugi")))
				{
					// These switches were already processed
				}
				else if (!klstricmp(curCommand, _T("/?")) || !klstricmp(curCommand, _T("/h")) || !klstricmp(curCommand, _T("/help")))
				{
					if (gpLng) gpLng->Reload();
					ConEmuAbout::OnInfo_About();
					iResult = -1;
					goto wrap;
				}
				// Final `-cmd ...` or `-cmdlist ...`
				else if (
					!klstricmp(curCommand, _T("/cmd"))
					|| !klstricmp(curCommand, _T("/cmdlist"))
					)
				{
					if (opt.cfgSwitches.ms_Val)
					{
						_ASSERTE(pszArgStart>pszCopyToEnvStart);
						_ASSERTE((INT_PTR)(pszArgStart - pszCopyToEnvStart) <= opt.cfgSwitches.GetLen());
						opt.cfgSwitches.ms_Val[pszArgStart - pszCopyToEnvStart] = 0;
					}

					opt.runCommand.Set(SkipNonPrintable(cmdLineRest));
					opt.isScript = (klstricmp(curCommand, L"/cmdlist") == 0);
					break;
				}
				else
				{
					// Show error on unknown switch
					psUnknown = pszArgStart;
					break;
				}
			} // (*curCommand == L'/')

			// Avoid assertions in NextArg
			szArg.Empty(); szNext.Empty();
		} // while (NextArg(&cmdLineRest, szArg, &pszArgStart) == 0)
	}
	// Processing loop end

	if (psUnknown)
	{
		DEBUGSTRSTARTUP(L"Unknown switch, exiting!");
		if (gpSet->isLogging())
		{
			// For direct logging we do not use lng resources
			CEStr lsLog(L"\r\n", L"Unknown switch specified: ", psUnknown, L"\r\n\r\n");
			gpConEmu->LogString(lsLog, false, false);
		}

		CEStr szNewConWarn;
		LPCWSTR pszTestSwitch =
			(psUnknown[0] == L'-' || psUnknown[0] == L'/')
				? ((psUnknown[1] == L'-' || psUnknown[1] == L'/')
					? (psUnknown+2) : (psUnknown+1))
				: psUnknown;
		if ((lstrcmpni(pszTestSwitch, L"new_console", 11) == 0)
			|| (lstrcmpni(pszTestSwitch, L"cur_console", 11) == 0))
		{
			szNewConWarn = lstrmerge(L"\r\n\r\n",
				CLngRc::getRsrc(lng_UnknownSwitch4/*"Switch -new_console must be specified *after* /cmd or /cmdlist"*/)
				);
		}

		CEStr lsMsg(
			CLngRc::getRsrc(lng_UnknownSwitch1/*"Unknown switch specified:"*/),
			L"\r\n\r\n",
			psUnknown,
			szNewConWarn,
			L"\r\n\r\n",
			CLngRc::getRsrc(lng_UnknownSwitch2/*"Visit website to get thorough switches description:"*/),
			L"\r\n"
			CEGUIARGSPAGE
			L"\r\n\r\n",
			CLngRc::getRsrc(lng_UnknownSwitch3/*"Or run ‘ConEmu.exe -?’ to get the brief."*/)
			);

		MBoxA(lsMsg);
		goto wrap;
	}

	// Set "ConEmuArgs" and "ConEmuArgs2"
	ProcessConEmuArgsVar();

	// Continue normal startup
	bRc = true;
wrap:
	return bRc;
}