diff --git a/launcher/.gitignore b/launcher/.gitignore new file mode 100644 index 0000000..0036705 --- /dev/null +++ b/launcher/.gitignore @@ -0,0 +1,181 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates +*.filters + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +x64/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets +!packages/*/build/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +#NUNIT +*.VisualState.xml +TestResult.xml + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding addin-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +*.ncrunch* +_NCrunch_* +.*crunch*.local.xml + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.Publish.xml +*.azurePubxml + +# NuGet Packages Directory +## TODO: If you have NuGet Package Restore enabled, uncomment the next line +#packages/ +## TODO: If the tool you use requires repositories.config, also uncomment the next line +#!packages/repositories.config + +# Windows Azure Build Output +csx/ +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.[Pp]ublish.xml +*.pfx +*.publishsettings + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +App_Data/*.mdf +App_Data/*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# ========================= +# Windows detritus +# ========================= + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ diff --git a/launcher/CmderLauncher.sln b/launcher/CmderLauncher.sln new file mode 100644 index 0000000..e4934d3 --- /dev/null +++ b/launcher/CmderLauncher.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.21005.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CmderLauncher", "CmderLauncher.vcxproj", "{4A8485A5-B7DD-4C44-B7F6-3E2765DD0CD3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4A8485A5-B7DD-4C44-B7F6-3E2765DD0CD3}.Debug|Win32.ActiveCfg = Debug|Win32 + {4A8485A5-B7DD-4C44-B7F6-3E2765DD0CD3}.Debug|Win32.Build.0 = Debug|Win32 + {4A8485A5-B7DD-4C44-B7F6-3E2765DD0CD3}.Release|Win32.ActiveCfg = Release|Win32 + {4A8485A5-B7DD-4C44-B7F6-3E2765DD0CD3}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/launcher/CmderLauncher.vcxproj b/launcher/CmderLauncher.vcxproj new file mode 100644 index 0000000..557e1ac --- /dev/null +++ b/launcher/CmderLauncher.vcxproj @@ -0,0 +1,98 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {4A8485A5-B7DD-4C44-B7F6-3E2765DD0CD3} + Win32Proj + CmderLauncher + + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + true + Cmder + + + false + Cmder + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + Windows + true + + + + + Level3 + + + MinSpace + true + false + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + Size + + + Windows + true + true + true + + + cp $(TargetPath) $(SolutionDir)..\$(TargetFileName) + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/launcher/src/CmderLauncher.cpp b/launcher/src/CmderLauncher.cpp new file mode 100644 index 0000000..677ee13 --- /dev/null +++ b/launcher/src/CmderLauncher.cpp @@ -0,0 +1,231 @@ +#include +#include +#include +#include "resource.h" +#include + +#pragma comment(lib, "Shlwapi.lib") + +#ifndef UNICODE +#error "Must be compiled with unicode support." +#endif + +#define USE_TASKBAR_API (_WIN32_WINNT >= _WIN32_WINNT_WIN7) + +#define MB_TITLE L"Cmder Launcher" +#define SHELL_MENU_REGISTRY_PATH L"Directory\\Background\\shell\\Cmder" + +#define streqi(a, b) (_wcsicmp((a), (b)) == 0) + +#define WIDEN2(x) L ## x +#define WIDEN(x) WIDEN2(x) +#define __WFUNCTION__ WIDEN(__FUNCTION__) + +#define FAIL_ON_ERROR(x) { DWORD ec; if ((ec = (x)) != ERROR_SUCCESS) { ShowErrorAndExit(ec, __WFUNCTION__, __LINE__); } } + +void ShowErrorAndExit(DWORD ec, const wchar_t * func, int line) +{ + wchar_t * buffer; + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, ec, 0, (LPWSTR) &buffer, 0, NULL) == 0) + { + buffer = L"Unknown error. FormatMessage failed."; + } + + wchar_t message[1024]; + swprintf_s(message, L"%s\nFunction: %s\nLine: %d", buffer, func, line); + LocalFree(buffer); + + MessageBox(NULL, message, MB_TITLE, MB_OK | MB_ICONERROR); + exit(1); +} + +typedef struct _option +{ + std::wstring name; + bool hasVal; + std::wstring value; + bool set; +} option; + +typedef std::pair optpair; + + +optpair GetOption() +{ + wchar_t * cmd = GetCommandLine(); + int argc; + wchar_t ** argv = CommandLineToArgvW(cmd, &argc); + optpair pair; + + if (argc == 1) + { + pair = optpair(L"/START", L""); + } + else if (argc == 2 && argv[1][0] != L'/') + { + pair = optpair(L"/START", argv[1]); + } + else + { + pair = optpair(argv[1], argc > 2 ? argv[2] : L""); + } + + LocalFree(argv); + + return pair; +} + +void StartCmder(std::wstring path) +{ +#if USE_TASKBAR_API + wchar_t appId[MAX_PATH] = { 0 }; +#endif + wchar_t exeDir[MAX_PATH] = { 0 }; + wchar_t icoPath[MAX_PATH] = { 0 }; + wchar_t cfgPath[MAX_PATH] = { 0 }; + wchar_t conEmuPath[MAX_PATH] = { 0 }; + wchar_t args[MAX_PATH * 2 + 256] = { 0 }; + + GetModuleFileName(NULL, exeDir, sizeof(exeDir)); + +#if USE_TASKBAR_API + wcscpy_s(appId, exeDir); +#endif + + PathRemoveFileSpec(exeDir); + + PathCombine(icoPath, exeDir, L"icons\\cmder.ico"); + PathCombine(cfgPath, exeDir, L"config\\ConEmu.xml"); + PathCombine(conEmuPath, exeDir, L"vendor\\conemu-maximus5\\ConEmu.exe"); + + swprintf_s(args, L"/Icon \"%s\" /Title Cmder /LoadCfgFile \"%s\"", icoPath, cfgPath); + + SetEnvironmentVariable(L"CMDER_ROOT", exeDir); + SetEnvironmentVariable(L"CMDER_START", path.c_str()); + + STARTUPINFO si = { 0 }; + si.cb = sizeof(STARTUPINFO); +#if USE_TASKBAR_API + si.lpTitle = appId; + si.dwFlags = STARTF_TITLEISAPPID; +#endif + + PROCESS_INFORMATION pi; + + CreateProcess(conEmuPath, args, NULL, NULL, false, 0, NULL, NULL, &si, &pi); +} + +bool IsUserOnly(std::wstring opt) +{ + bool userOnly; + + if (streqi(opt.c_str(), L"ALL")) + { + userOnly = false; + } + else if (streqi(opt.c_str(), L"USER")) + { + userOnly = true; + } + else + { + MessageBox(NULL, L"Unrecognized option for /REGISTER or /UNREGISTER. Must be either ALL or USER.", MB_TITLE, MB_OK); + exit(1); + } + + return userOnly; +} + +HKEY GetRootKey(std::wstring opt) +{ + HKEY root; + + if (IsUserOnly(opt)) + { + FAIL_ON_ERROR(RegCreateKeyEx(HKEY_CURRENT_USER, L"Software\\Classes", 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &root, NULL)); + } + else + { + root = HKEY_CLASSES_ROOT; + } + + return root; +} + +void RegisterShellMenu(std::wstring opt) +{ + HKEY root = GetRootKey(opt); + + HKEY cmderKey; + FAIL_ON_ERROR( + RegCreateKeyEx(root, SHELL_MENU_REGISTRY_PATH, 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &cmderKey, NULL)); + + FAIL_ON_ERROR(RegSetValue(cmderKey, L"", REG_SZ, L"Cmder Here", NULL)); + FAIL_ON_ERROR(RegSetValueEx(cmderKey, L"NoWorkingDirectory", 0, REG_SZ, (BYTE *)L"", 2)); + + HKEY command; + FAIL_ON_ERROR( + RegCreateKeyEx(cmderKey, L"command", 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &command, NULL)); + + wchar_t exePath[MAX_PATH] = { 0 }; + + GetModuleFileName(NULL, exePath, sizeof(exePath)); + + wchar_t commandStr[MAX_PATH + 20] = { 0 }; + swprintf_s(commandStr, L"\"%s\" \"%%V\"", exePath); + + FAIL_ON_ERROR(RegSetValue(command, L"", REG_SZ, commandStr, NULL)); + + RegCloseKey(command); + RegCloseKey(cmderKey); + RegCloseKey(root); +} + +void UnregisterShellMenu(std::wstring opt) +{ + HKEY root = GetRootKey(opt); + HKEY cmderKey; + FAIL_ON_ERROR( + RegCreateKeyEx(root, SHELL_MENU_REGISTRY_PATH, 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &cmderKey, NULL)); + FAIL_ON_ERROR(RegDeleteTree(cmderKey, NULL)); + FAIL_ON_ERROR(RegDeleteKey(root, SHELL_MENU_REGISTRY_PATH)); + RegCloseKey(cmderKey); + RegCloseKey(root); +} + +int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, + _In_opt_ HINSTANCE hPrevInstance, + _In_ LPTSTR lpCmdLine, + _In_ int nCmdShow) +{ + UNREFERENCED_PARAMETER(hPrevInstance); + UNREFERENCED_PARAMETER(lpCmdLine); + UNREFERENCED_PARAMETER(nCmdShow); + + optpair opt = GetOption(); + + if (streqi(opt.first.c_str(), L"/START")) + { + StartCmder(opt.second); + } + else if (streqi(opt.first.c_str(), L"/REGISTER")) + { + RegisterShellMenu(opt.second); + } + else if (streqi(opt.first.c_str(), L"/UNREGISTER")) + { + UnregisterShellMenu(opt.second); + } + else + { + MessageBox(NULL, L"Unrecognized parameter.\n\nValid options:\n /START \n /REGISTER [USER/ALL]\n /UNREGISTER [USER/ALL]", MB_TITLE, MB_OK); + return 1; + } + + return 0; +} \ No newline at end of file diff --git a/launcher/src/Resource.rc b/launcher/src/Resource.rc new file mode 100644 index 0000000..42eec58 Binary files /dev/null and b/launcher/src/Resource.rc differ diff --git a/launcher/src/resource.h b/launcher/src/resource.h new file mode 100644 index 0000000..5746935 Binary files /dev/null and b/launcher/src/resource.h differ diff --git a/vendor/init.bat b/vendor/init.bat index 3bd9367..e4daa8c 100644 --- a/vendor/init.bat +++ b/vendor/init.bat @@ -40,4 +40,7 @@ :: Set home path @set HOME=%USERPROFILE% + +@if defined CMDER_START cd /d "%CMDER_START%" + @echo Welcome to cmder!