2013-11-30 22:15:42 +08:00
|
|
|
#include <windows.h>
|
|
|
|
#include <tchar.h>
|
|
|
|
#include <Shlwapi.h>
|
|
|
|
#include "resource.h"
|
2013-12-07 22:17:35 +08:00
|
|
|
#include <vector>
|
2016-02-01 05:35:57 +08:00
|
|
|
|
2013-11-30 22:15:42 +08:00
|
|
|
|
|
|
|
#pragma comment(lib, "Shlwapi.lib")
|
|
|
|
|
2013-12-07 22:17:35 +08:00
|
|
|
#ifndef UNICODE
|
|
|
|
#error "Must be compiled with unicode support."
|
|
|
|
#endif
|
2013-11-30 22:15:42 +08:00
|
|
|
|
2013-12-07 22:17:35 +08:00
|
|
|
#define USE_TASKBAR_API (_WIN32_WINNT >= _WIN32_WINNT_WIN7)
|
|
|
|
|
2014-08-27 06:52:49 +08:00
|
|
|
#define XP (_WIN32_WINNT < _WIN32_WINNT_VISTA)
|
|
|
|
|
2013-12-07 22:17:35 +08:00
|
|
|
#define MB_TITLE L"Cmder Launcher"
|
2014-02-28 07:34:37 +08:00
|
|
|
#define SHELL_MENU_REGISTRY_PATH_BACKGROUND L"Directory\\Background\\shell\\Cmder"
|
|
|
|
#define SHELL_MENU_REGISTRY_PATH_LISTITEM L"Directory\\shell\\Cmder"
|
2013-12-07 22:17:35 +08:00
|
|
|
|
2015-09-05 04:16:30 +08:00
|
|
|
#define streqi(a, b) (_wcsicmp((a), (b)) == 0)
|
2013-12-07 22:17:35 +08:00
|
|
|
|
|
|
|
#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)
|
2013-11-30 22:15:42 +08:00
|
|
|
{
|
2013-12-07 22:17:35 +08:00
|
|
|
wchar_t * buffer;
|
2015-09-05 04:16:30 +08:00
|
|
|
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
2013-12-07 22:17:35 +08:00
|
|
|
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);
|
2013-11-30 22:15:42 +08:00
|
|
|
|
2013-12-07 22:17:35 +08:00
|
|
|
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<std::wstring, std::wstring> optpair;
|
|
|
|
|
|
|
|
|
|
|
|
optpair GetOption()
|
|
|
|
{
|
|
|
|
wchar_t * cmd = GetCommandLine();
|
|
|
|
int argc;
|
|
|
|
wchar_t ** argv = CommandLineToArgvW(cmd, &argc);
|
|
|
|
optpair pair;
|
|
|
|
|
|
|
|
if (argc == 1)
|
|
|
|
{
|
2016-01-12 05:09:23 +08:00
|
|
|
// no commandline argument...
|
2013-12-07 22:17:35 +08:00
|
|
|
pair = optpair(L"/START", L"");
|
|
|
|
}
|
|
|
|
else if (argc == 2 && argv[1][0] != L'/')
|
|
|
|
{
|
2016-01-12 05:09:23 +08:00
|
|
|
// only a single argument: this should be a path...
|
2013-12-07 22:17:35 +08:00
|
|
|
pair = optpair(L"/START", argv[1]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pair = optpair(argv[1], argc > 2 ? argv[2] : L"");
|
|
|
|
}
|
|
|
|
|
|
|
|
LocalFree(argv);
|
|
|
|
|
|
|
|
return pair;
|
|
|
|
}
|
|
|
|
|
2015-08-19 07:13:22 +08:00
|
|
|
bool FileExists(const wchar_t * filePath)
|
|
|
|
{
|
|
|
|
HANDLE hFile = CreateFile(filePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
|
|
|
|
|
|
|
if (hFile != INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
CloseHandle(hFile);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-06-17 22:29:06 +08:00
|
|
|
void StartCmder(std::wstring path, bool is_single_mode, std::wstring taskName = L"")
|
2013-12-07 22:17:35 +08:00
|
|
|
{
|
2013-11-30 22:15:42 +08:00
|
|
|
#if USE_TASKBAR_API
|
2013-12-07 22:17:35 +08:00
|
|
|
wchar_t appId[MAX_PATH] = { 0 };
|
2013-11-30 22:15:42 +08:00
|
|
|
#endif
|
2013-12-07 22:17:35 +08:00
|
|
|
wchar_t exeDir[MAX_PATH] = { 0 };
|
|
|
|
wchar_t icoPath[MAX_PATH] = { 0 };
|
|
|
|
wchar_t cfgPath[MAX_PATH] = { 0 };
|
2016-10-03 11:23:12 +08:00
|
|
|
wchar_t backupCfgPath[MAX_PATH] = { 0 };
|
2016-10-03 10:50:56 +08:00
|
|
|
wchar_t cpuCfgPath[MAX_PATH] = { 0 };
|
|
|
|
wchar_t userCfgPath[MAX_PATH] = { 0 };
|
2015-08-19 07:13:22 +08:00
|
|
|
wchar_t oldCfgPath[MAX_PATH] = { 0 };
|
2013-12-07 22:17:35 +08:00
|
|
|
wchar_t conEmuPath[MAX_PATH] = { 0 };
|
|
|
|
wchar_t args[MAX_PATH * 2 + 256] = { 0 };
|
2013-11-30 22:15:42 +08:00
|
|
|
|
|
|
|
GetModuleFileName(NULL, exeDir, sizeof(exeDir));
|
|
|
|
|
|
|
|
#if USE_TASKBAR_API
|
2013-12-07 22:17:35 +08:00
|
|
|
wcscpy_s(appId, exeDir);
|
2013-11-30 22:15:42 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
PathRemoveFileSpec(exeDir);
|
|
|
|
|
2013-12-07 22:17:35 +08:00
|
|
|
PathCombine(icoPath, exeDir, L"icons\\cmder.ico");
|
2015-11-01 06:29:10 +08:00
|
|
|
|
2016-10-03 10:50:56 +08:00
|
|
|
// Check for machine-specific then user config source file.
|
2016-10-03 07:34:40 +08:00
|
|
|
PathCombine(cpuCfgPath, exeDir, L"config\\ConEmu-%COMPUTERNAME%.xml");
|
|
|
|
ExpandEnvironmentStrings(cpuCfgPath, cpuCfgPath, sizeof(cpuCfgPath) / sizeof(cpuCfgPath[0]));
|
|
|
|
|
|
|
|
PathCombine(userCfgPath, exeDir, L"config\\user-ConEmu.xml");
|
|
|
|
|
2016-10-03 10:50:56 +08:00
|
|
|
if (PathFileExists(cpuCfgPath)) {
|
|
|
|
wcsncpy_s(oldCfgPath, cpuCfgPath, sizeof(cpuCfgPath));
|
2016-10-03 11:23:12 +08:00
|
|
|
wcsncpy_s(backupCfgPath, cpuCfgPath, sizeof(cpuCfgPath));
|
2016-10-03 10:50:56 +08:00
|
|
|
}
|
|
|
|
else if (PathFileExists(userCfgPath)) {
|
|
|
|
wcsncpy_s(oldCfgPath, userCfgPath,sizeof(userCfgPath));
|
2016-10-03 11:23:12 +08:00
|
|
|
wcsncpy_s(backupCfgPath, userCfgPath, sizeof(userCfgPath));
|
2016-10-03 10:50:56 +08:00
|
|
|
}
|
|
|
|
else {
|
2015-11-01 06:29:10 +08:00
|
|
|
PathCombine(oldCfgPath, exeDir, L"config\\ConEmu.xml");
|
2016-10-03 11:23:12 +08:00
|
|
|
wcsncpy_s(backupCfgPath, userCfgPath, sizeof(userCfgPath));
|
2015-03-18 04:39:27 +08:00
|
|
|
}
|
2015-11-01 06:29:10 +08:00
|
|
|
|
2016-10-03 10:50:56 +08:00
|
|
|
// Set path to vendored ConEmu config file
|
|
|
|
PathCombine(cfgPath, exeDir, L"vendor\\conemu-maximus5\\ConEmu.xml");
|
|
|
|
|
2016-06-30 07:55:13 +08:00
|
|
|
SYSTEM_INFO sysInfo;
|
|
|
|
GetNativeSystemInfo(&sysInfo);
|
|
|
|
if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
|
|
|
|
PathCombine(conEmuPath, exeDir, L"vendor\\conemu-maximus5\\ConEmu64.exe");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
PathCombine(conEmuPath, exeDir, L"vendor\\conemu-maximus5\\ConEmu.exe");
|
|
|
|
}
|
2013-11-30 22:15:42 +08:00
|
|
|
|
2015-08-19 07:13:22 +08:00
|
|
|
if (FileExists(oldCfgPath) && !FileExists(cfgPath))
|
|
|
|
{
|
|
|
|
if (!CopyFile(oldCfgPath, cfgPath, FALSE))
|
|
|
|
{
|
|
|
|
MessageBox(NULL,
|
|
|
|
(GetLastError() == ERROR_ACCESS_DENIED)
|
|
|
|
? L"Failed to copy ConEmu.xml file to new location! Restart cmder as administrator."
|
|
|
|
: L"Failed to copy ConEmu.xml file to new location!", MB_TITLE, MB_ICONSTOP);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
2016-10-03 11:23:12 +08:00
|
|
|
else if (!CopyFile(cfgPath, backupCfgPath, FALSE))
|
|
|
|
{
|
|
|
|
MessageBox(NULL,
|
|
|
|
(GetLastError() == ERROR_ACCESS_DENIED)
|
|
|
|
? L"Failed to backup ConEmu.xml file to ./config folder!"
|
|
|
|
: L"Failed to backup ConEmu.xml file to ./config folder!", MB_TITLE, MB_ICONSTOP);
|
|
|
|
exit(1);
|
2016-10-03 11:16:22 +08:00
|
|
|
}
|
2015-08-19 07:13:22 +08:00
|
|
|
|
2015-09-05 04:16:30 +08:00
|
|
|
if (is_single_mode)
|
2014-11-20 23:59:17 +08:00
|
|
|
{
|
2015-08-19 07:13:22 +08:00
|
|
|
swprintf_s(args, L"/single /Icon \"%s\" /Title Cmder", icoPath);
|
2014-11-20 23:59:17 +08:00
|
|
|
}
|
2015-09-05 04:16:30 +08:00
|
|
|
else
|
2014-11-20 23:59:17 +08:00
|
|
|
{
|
2015-08-19 07:13:22 +08:00
|
|
|
swprintf_s(args, L"/Icon \"%s\" /Title Cmder", icoPath);
|
2014-11-20 23:59:17 +08:00
|
|
|
}
|
2013-11-30 22:15:42 +08:00
|
|
|
|
2017-06-17 22:29:06 +08:00
|
|
|
if (!taskName.empty()) {
|
|
|
|
swprintf_s(args, L"%s -run {%s}", args, taskName.c_str());
|
|
|
|
}
|
|
|
|
|
2016-01-15 20:10:24 +08:00
|
|
|
SetEnvironmentVariable(L"CMDER_ROOT", exeDir);
|
2016-03-03 01:29:34 +08:00
|
|
|
if (!streqi(path.c_str(), L""))
|
2016-01-15 20:10:24 +08:00
|
|
|
{
|
2016-02-01 05:35:57 +08:00
|
|
|
if (!SetEnvironmentVariable(L"CMDER_START", path.c_str())) {
|
|
|
|
MessageBox(NULL, _T("Error trying to set CMDER_START to given path!"), _T("Error"), MB_OK);
|
|
|
|
}
|
2016-01-15 20:10:24 +08:00
|
|
|
}
|
2016-01-15 21:13:16 +08:00
|
|
|
// Ensure EnvironmentVariables are propagated.
|
2016-10-15 05:36:22 +08:00
|
|
|
|
2015-09-05 04:16:30 +08:00
|
|
|
|
2013-11-30 22:15:42 +08:00
|
|
|
STARTUPINFO si = { 0 };
|
|
|
|
si.cb = sizeof(STARTUPINFO);
|
|
|
|
#if USE_TASKBAR_API
|
|
|
|
si.lpTitle = appId;
|
|
|
|
si.dwFlags = STARTF_TITLEISAPPID;
|
|
|
|
#endif
|
|
|
|
PROCESS_INFORMATION pi;
|
2016-02-01 05:35:57 +08:00
|
|
|
if (!CreateProcess(conEmuPath, args, NULL, NULL, false, 0, NULL, NULL, &si, &pi)) {
|
|
|
|
MessageBox(NULL, _T("Unable to create the ConEmu Process!"), _T("Error"), MB_OK);
|
|
|
|
return;
|
|
|
|
}
|
2016-10-15 05:36:22 +08:00
|
|
|
|
|
|
|
LRESULT lr = SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)"Environment", SMTO_ABORTIFHUNG | SMTO_NOTIMEOUTIFNOTHUNG, 5000, NULL);
|
|
|
|
lr = SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM) L"Environment", SMTO_ABORTIFHUNG | SMTO_NOTIMEOUTIFNOTHUNG, 5000, NULL); // For Windows >= 8
|
|
|
|
|
2013-12-07 22:17:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-02-28 07:34:37 +08:00
|
|
|
void RegisterShellMenu(std::wstring opt, wchar_t* keyBaseName)
|
2013-12-07 22:17:35 +08:00
|
|
|
{
|
2014-02-28 07:34:37 +08:00
|
|
|
// First, get the paths we will use
|
|
|
|
|
|
|
|
wchar_t exePath[MAX_PATH] = { 0 };
|
|
|
|
wchar_t icoPath[MAX_PATH] = { 0 };
|
|
|
|
|
|
|
|
GetModuleFileName(NULL, exePath, sizeof(exePath));
|
|
|
|
|
|
|
|
wchar_t commandStr[MAX_PATH + 20] = { 0 };
|
|
|
|
swprintf_s(commandStr, L"\"%s\" \"%%V\"", exePath);
|
|
|
|
|
|
|
|
// Now that we have `commandStr`, it's OK to change `exePath`...
|
|
|
|
PathRemoveFileSpec(exePath);
|
|
|
|
|
|
|
|
PathCombine(icoPath, exePath, L"icons\\cmder.ico");
|
|
|
|
|
|
|
|
// Now set the registry keys
|
|
|
|
|
2013-12-07 22:17:35 +08:00
|
|
|
HKEY root = GetRootKey(opt);
|
|
|
|
|
|
|
|
HKEY cmderKey;
|
|
|
|
FAIL_ON_ERROR(
|
2014-02-28 07:34:37 +08:00
|
|
|
RegCreateKeyEx(root, keyBaseName, 0, NULL,
|
2013-12-07 22:17:35 +08:00
|
|
|
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));
|
|
|
|
|
2014-02-28 07:44:46 +08:00
|
|
|
FAIL_ON_ERROR(RegSetValueEx(cmderKey, L"Icon", 0, REG_SZ, (BYTE *)icoPath, wcslen(icoPath) * sizeof(wchar_t)));
|
2014-02-28 07:34:37 +08:00
|
|
|
|
2013-12-07 22:17:35 +08:00
|
|
|
HKEY command;
|
|
|
|
FAIL_ON_ERROR(
|
|
|
|
RegCreateKeyEx(cmderKey, L"command", 0, NULL,
|
|
|
|
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &command, NULL));
|
|
|
|
|
|
|
|
FAIL_ON_ERROR(RegSetValue(command, L"", REG_SZ, commandStr, NULL));
|
|
|
|
|
|
|
|
RegCloseKey(command);
|
|
|
|
RegCloseKey(cmderKey);
|
|
|
|
RegCloseKey(root);
|
|
|
|
}
|
|
|
|
|
2014-02-28 07:34:37 +08:00
|
|
|
void UnregisterShellMenu(std::wstring opt, wchar_t* keyBaseName)
|
2013-12-07 22:17:35 +08:00
|
|
|
{
|
|
|
|
HKEY root = GetRootKey(opt);
|
|
|
|
HKEY cmderKey;
|
|
|
|
FAIL_ON_ERROR(
|
2014-02-28 07:34:37 +08:00
|
|
|
RegCreateKeyEx(root, keyBaseName, 0, NULL,
|
2013-12-07 22:17:35 +08:00
|
|
|
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &cmderKey, NULL));
|
2014-08-27 06:52:49 +08:00
|
|
|
#if XP
|
2014-01-22 02:25:24 +08:00
|
|
|
FAIL_ON_ERROR(SHDeleteKey(cmderKey, NULL));
|
|
|
|
#else
|
2014-08-27 06:52:49 +08:00
|
|
|
FAIL_ON_ERROR(RegDeleteTree(cmderKey, NULL));
|
2014-01-22 02:25:24 +08:00
|
|
|
#endif
|
2013-12-07 22:17:35 +08:00
|
|
|
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"))
|
|
|
|
{
|
2014-11-20 23:59:17 +08:00
|
|
|
StartCmder(opt.second, false);
|
|
|
|
}
|
|
|
|
else if (streqi(opt.first.c_str(), L"/SINGLE"))
|
|
|
|
{
|
|
|
|
StartCmder(opt.second, true);
|
2013-12-07 22:17:35 +08:00
|
|
|
}
|
2017-06-17 22:29:06 +08:00
|
|
|
else if (streqi(opt.first.c_str(), L"/TASK"))
|
|
|
|
{
|
|
|
|
StartCmder(L"", false, opt.second);
|
|
|
|
}
|
2013-12-07 22:17:35 +08:00
|
|
|
else if (streqi(opt.first.c_str(), L"/REGISTER"))
|
|
|
|
{
|
2014-02-28 07:34:37 +08:00
|
|
|
RegisterShellMenu(opt.second, SHELL_MENU_REGISTRY_PATH_BACKGROUND);
|
|
|
|
RegisterShellMenu(opt.second, SHELL_MENU_REGISTRY_PATH_LISTITEM);
|
2013-12-07 22:17:35 +08:00
|
|
|
}
|
|
|
|
else if (streqi(opt.first.c_str(), L"/UNREGISTER"))
|
|
|
|
{
|
2014-02-28 07:34:37 +08:00
|
|
|
UnregisterShellMenu(opt.second, SHELL_MENU_REGISTRY_PATH_BACKGROUND);
|
|
|
|
UnregisterShellMenu(opt.second, SHELL_MENU_REGISTRY_PATH_LISTITEM);
|
2013-12-07 22:17:35 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-06-17 22:29:06 +08:00
|
|
|
MessageBox(NULL, L"Unrecognized parameter.\n\nValid options:\n /START <path>\n /SINGLE <path>\n /TASK <name>\n /REGISTER [USER/ALL]\n /UNREGISTER [USER/ALL]", MB_TITLE, MB_OK);
|
2013-12-07 22:17:35 +08:00
|
|
|
return 1;
|
|
|
|
}
|
2013-11-30 22:15:42 +08:00
|
|
|
|
|
|
|
return 0;
|
2014-01-22 02:25:24 +08:00
|
|
|
}
|