blob: 16c6ce07d81d7e0bae605cc24d2359139439bdb1 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* Author: Tomas Holy
*/
#ifndef KEY_WOW64_64KEY
#define KEY_WOW64_64KEY 0x0100
#endif
#include "utilsfuncs.h"
#include "argnames.h"
#include <tlhelp32.h>
#include <windows.h>
using namespace std;
bool disableFolderVirtualization(HANDLE hProcess) {
OSVERSIONINFO osvi = {0};
osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
if (GetVersionEx(&osvi) && osvi.dwMajorVersion == 6) // check it is Win VISTA
{
HANDLE hToken;
if (OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, &hToken)) {
DWORD tokenInfoVal = 0;
if (!SetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS) 24, &tokenInfoVal, sizeof (DWORD))) {
// invalid token information class (24) is OK, it means there is no folder virtualization on current system
if (GetLastError() != ERROR_INVALID_PARAMETER) {
logErr(true, true, "Failed to set token information.");
return false;
}
}
CloseHandle(hToken);
} else {
logErr(true, true, "Failed to open process token.");
return false;
}
}
return true;
}
bool getStringFromRegistry(HKEY rootKey, const char *keyName, const char *valueName, string &value) {
return getStringFromRegistryEx(rootKey, keyName, valueName, value, false);
}
bool getStringFromRegistry64bit(HKEY rootKey, const char *keyName, const char *valueName, string &value) {
return getStringFromRegistryEx(rootKey, keyName, valueName, value, true);
}
bool getStringFromRegistryEx(HKEY rootKey, const char *keyName, const char *valueName, string &value, bool read64bit) {
logMsg("getStringFromRegistry()\n\tkeyName: %s\n\tvalueName: %s", keyName, valueName);
HKEY hKey = 0;
if (RegOpenKeyEx(rootKey, keyName, 0, KEY_READ | (read64bit ? KEY_WOW64_64KEY : 0), &hKey) == ERROR_SUCCESS) {
DWORD valSize = 4096;
DWORD type = 0;
char val[4096] = "";
if (RegQueryValueEx(hKey, valueName, 0, &type, (BYTE *) val, &valSize) == ERROR_SUCCESS
&& type == REG_SZ) {
logMsg("%s: %s", valueName, val);
RegCloseKey(hKey);
value = val;
return true;
} else {
logErr(true, false, "RegQueryValueEx() failed.");
}
RegCloseKey(hKey);
} else {
logErr(true, false, "RegOpenKeyEx() failed.");
}
return false;
}
bool getDwordFromRegistry(HKEY rootKey, const char *keyName, const char *valueName, DWORD &value) {
logMsg("getDwordFromRegistry()\n\tkeyName: %s\n\tvalueName: %s", keyName, valueName);
HKEY hKey = 0;
if (RegOpenKeyEx(rootKey, keyName, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
DWORD valSize = sizeof(DWORD);
DWORD type = 0;
if (RegQueryValueEx(hKey, valueName, 0, &type, (BYTE *) &value, &valSize) == ERROR_SUCCESS
&& type == REG_DWORD) {
logMsg("%s: %u", valueName, value);
RegCloseKey(hKey);
return true;
} else {
logErr(true, false, "RegQueryValueEx() failed.");
}
RegCloseKey(hKey);
} else {
logErr(true, false, "RegOpenKeyEx() failed.");
}
return false;
}
bool dirExists(const char *path) {
WIN32_FIND_DATA fd = {0};
HANDLE hFind = 0;
hFind = FindFirstFile(path, &fd);
if (hFind == INVALID_HANDLE_VALUE) {
logMsg("Dir \"%s\" does not exist", path);
return false;
}
logMsg("Dir \"%s\" exists", path);
FindClose(hFind);
return (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
}
bool fileExists(const char *path) {
WIN32_FIND_DATA fd = {0};
HANDLE hFind = 0;
hFind = FindFirstFile(path, &fd);
if (hFind == INVALID_HANDLE_VALUE) {
logMsg("File \"%s\" does not exist", path);
return false;
}
logMsg("File \"%s\" exists", path);
FindClose(hFind);
return true;
}
bool normalizePath(char *path, int len) {
char tmp[MAX_PATH] = "";
int i = 0;
while (path[i] && i < MAX_PATH - 1) {
tmp[i] = path[i] == '/' ? '\\' : path[i];
i++;
}
tmp[i] = '\0';
return _fullpath(path, tmp, len) != NULL;
}
bool createPath(const char *path) {
logMsg("Creating directory \"%s\"", path);
char dir[MAX_PATH] = "";
const char *sep = strchr(path, '\\');
while (sep) {
strncpy(dir, path, sep - path);
if (!CreateDirectory(dir, 0) && GetLastError() != ERROR_ALREADY_EXISTS) {
logErr(true, false, "Failed to create directory %s", dir);
return false;
}
sep = strchr(sep + 1, '\\');
}
return true;
}
char * getCurrentModulePath(char *path, int pathLen) {
MEMORY_BASIC_INFORMATION mbi;
static int dummy;
VirtualQuery(&dummy, &mbi, sizeof (mbi));
HMODULE hModule = (HMODULE) mbi.AllocationBase;
GetModuleFileName(hModule, path, pathLen);
return path;
}
char * skipWhitespaces(char *str) {
while (*str != '\0' && (*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r')) {
str++;
}
return str;
}
char * trimWhitespaces(char *str) {
char *end = str + strlen(str) - 1;
while (end >= str && (*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r')) {
*end = '\0';
end--;
}
return end;
}
char* getSysError(char *str, int strSize) {
int err = GetLastError();
LPTSTR lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) & lpMsgBuf,
0,
NULL
);
LPTSTR tmp = strchr(lpMsgBuf, '\r');
if (tmp != NULL) {
*tmp = '\0';
}
_snprintf(str, strSize, " %s (%u)", lpMsgBuf, err);
LocalFree(lpMsgBuf);
return str;
}
string gLogFileName;
void logV(bool appendSysError, bool showMsgBox, const char *format, va_list args) {
char msg[4096] = "";
vsnprintf(msg, 4096, format, args);
if (appendSysError) {
char sysErr[512] = "";
getSysError(sysErr, 512);
strncat(msg, sysErr, 4096 - strlen(msg));
}
if (!gLogFileName.empty()) {
FILE *file = fopen(gLogFileName.c_str(), "a");
if (file) {
fprintf(file, "%s\n", msg);
fclose(file);
}
}
if (showMsgBox) {
::MessageBox(NULL, msg, "Error", MB_OK | MB_ICONSTOP);
}
}
void logErr(bool appendSysError, bool showMsgBox, const char *format, ...) {
va_list args;
va_start(args, format);
logV(appendSysError, showMsgBox, format, args);
}
void logMsg(const char *format, ...) {
va_list args;
va_start(args, format);
logV(false, false, format, args);
}
bool restarting(int argc, char *argv[]) {
for (int i = 0; i < argc; i++) {
if (strcmp(ARG_NAME_LA_START_APP, argv[i]) == 0 || strcmp(ARG_NAME_LA_START_AU, argv[i]) == 0) {
return true;
}
}
return false;
}
bool checkLoggingArg(int argc, char *argv[], bool delFile) {
for (int i = 0; i < argc; i++) {
if (strcmp(ARG_NAME_LAUNCHER_LOG, argv[i]) == 0) {
if (i + 1 == argc) {
logErr(false, true, "Argument is missing for \"%s\" option.", argv[i]);
return false;
}
gLogFileName = argv[++i];
// if we are restarting, keep log file
if (delFile && !restarting(argc, argv)) {
DeleteFile(gLogFileName.c_str());
}
break;
}
}
return true;
}
void setConsoleCodepage() {
/* The Windows console (cmd) has its own code page setting that's usually different from the
system and user code page, e.g. on US Windows the console will use code page 437 while the
rest of the system uses 1252. Setting the console code page here to UTF-8 makes Unicode
characters printed from the application appear correctly. Since the launcher itself also runs
with UTF-8 as its code page (specified in the application manifest), this also makes log
messages from the launchers appear correctly, e.g. when printing paths that may have Unicode
characters in them. Note that if we attached to an existing console, the modified code page
setting will persist after the launcher exits. */
SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);
}
bool setupProcess(int &argc, char *argv[], DWORD &parentProcID, const char *attachMsg) {
#define CHECK_ARG \
if (i+1 == argc) {\
logErr(false, true, "Argument is missing for \"%s\" option.", argv[i]);\
return false;\
}
parentProcID = 0;
DWORD cmdLineArgPPID = 0;
for (int i = 0; i < argc; i++) {
if (strcmp(ARG_NAME_CONSOLE, argv[i]) == 0) {
CHECK_ARG;
if (strcmp("new", argv[i + 1]) == 0){
AllocConsole();
setConsoleCodepage();
} else if (strcmp("suppress", argv[i + 1]) == 0) {
// nothing, no console should be attached
} else {
logErr(false, true, "Invalid argument for \"%s\" option.", argv[i]);
return false;
}
// remove options
for (int k = i + 2; k < argc; k++) {
argv[k-2] = argv[k];
}
argc -= 2;
return true;
} else if (strcmp(ARG_NAME_LA_PPID, argv[i]) == 0) {
CHECK_ARG;
char *end = 0;
cmdLineArgPPID = strtoul(argv[++i], &end, 10);
if (cmdLineArgPPID == 0 && *end != '\0') {
logErr(false, true, "Invalid parameter for option %s", ARG_NAME_LA_PPID);
return false;
}
logMsg("Command line arg PPID: %u", cmdLineArgPPID);
break;
}
}
#undef CHECK_ARG
// default, attach to parent process console if exists
// AttachConsole exists since WinXP, so be nice and do it dynamically
typedef BOOL (WINAPI *LPFAC)(DWORD dwProcessId);
HINSTANCE hKernel32 = GetModuleHandle("kernel32");
if (hKernel32) {
LPFAC attachConsole = (LPFAC) GetProcAddress(hKernel32, "AttachConsole");
if (attachConsole) {
if (cmdLineArgPPID) {
if (!attachConsole(cmdLineArgPPID)) {
logErr(true, false, "AttachConsole of PPID: %u failed.", cmdLineArgPPID);
}
} else {
if (!attachConsole((DWORD) -1)) {
logErr(true, false, "AttachConsole of PP failed.");
} else {
getParentProcessID(parentProcID);
setConsoleCodepage();
if (attachMsg) {
printToConsole(attachMsg);
}
}
}
} else {
logErr(true, false, "GetProcAddress() for AttachConsole failed.");
}
}
return true;
}
bool isConsoleAttached() {
typedef HWND (WINAPI *GetConsoleWindowT)();
HINSTANCE hKernel32 = GetModuleHandle("kernel32");
if (hKernel32) {
GetConsoleWindowT getConsoleWindow = (GetConsoleWindowT) GetProcAddress(hKernel32, "GetConsoleWindow");
if (getConsoleWindow) {
if (getConsoleWindow() != NULL) {
logMsg("Console is attached.");
return true;
}
} else {
logErr(true, false, "GetProcAddress() for GetConsoleWindow failed.");
}
}
return false;
}
bool printToConsole(const char *msg) {
FILE *console = fopen("CON", "a");
if (!console) {
return false;
}
fprintf(console, "%s", msg);
fclose(console);
return false;
}
bool getParentProcessID(DWORD &id) {
typedef HANDLE (WINAPI * CreateToolhelp32SnapshotT)(DWORD, DWORD);
typedef BOOL (WINAPI * Process32FirstT)(HANDLE, LPPROCESSENTRY32);
typedef BOOL (WINAPI * Process32NextT)(HANDLE, LPPROCESSENTRY32);
HINSTANCE hKernel32 = GetModuleHandle("kernel32");
if (!hKernel32) {
return false;
}
CreateToolhelp32SnapshotT createToolhelp32Snapshot = (CreateToolhelp32SnapshotT) GetProcAddress(hKernel32, "CreateToolhelp32Snapshot");
Process32FirstT process32First = (Process32FirstT) GetProcAddress(hKernel32, "Process32First");
Process32NextT process32Next = (Process32NextT) GetProcAddress(hKernel32, "Process32Next");
if (createToolhelp32Snapshot == NULL || process32First == NULL || process32Next == NULL) {
logErr(true, false, "Failed to obtain Toolhelp32 functions.");
return false;
}
HANDLE hSnapshot = createToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE) {
logErr(true, false, "Failed to obtain process snapshot.");
return false;
}
PROCESSENTRY32 entry = {0};
entry.dwSize = sizeof (PROCESSENTRY32);
if (!process32First(hSnapshot, &entry)) {
CloseHandle(hSnapshot);
return false;
}
DWORD curID = GetCurrentProcessId();
logMsg("Current process ID: %u", curID);
do {
if (entry.th32ProcessID == curID) {
id = entry.th32ParentProcessID;
logMsg("Parent process ID: %u", id);
CloseHandle(hSnapshot);
return true;
}
} while (process32Next(hSnapshot, &entry));
CloseHandle(hSnapshot);
return false;
}
bool isWow64()
{
BOOL IsWow64 = FALSE;
typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
LPFN_ISWOW64PROCESS fnIsWow64Process;
fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress(GetModuleHandle(TEXT("kernel32")),"IsWow64Process");
if (NULL != fnIsWow64Process)
{
if (!fnIsWow64Process(GetCurrentProcess(),&IsWow64))
{
// handle error
}
}
return IsWow64;
}
int convertAnsiToUtf8(const char *ansi, char *utf8, int utf8Len) {
const int len = 32*1024;
WCHAR tmp[len] = L"";
if (MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, ansi, -1, tmp, len) == 0)
return -1;
if (WideCharToMultiByte(CP_UTF8, 0, tmp, -1, utf8, utf8Len, NULL, NULL) == 0)
return -1;
return 0;
}