mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Windows: Fix command line arguments not working for the portable.exe launchers
This commit is contained in:
parent
4ca2847b66
commit
e9d1f2c25c
@ -336,14 +336,14 @@ def build_portable(env):
|
|||||||
shutil.rmtree(base)
|
shutil.rmtree(base)
|
||||||
os.makedirs(base)
|
os.makedirs(base)
|
||||||
root = d(a(__file__))
|
root = d(a(__file__))
|
||||||
src = j(root, 'portable.c')
|
src = j(root, 'portable.cpp')
|
||||||
obj = j(env.obj_dir, b(src) + '.obj')
|
obj = j(env.obj_dir, b(src) + '.obj')
|
||||||
cflags = '/c /EHsc /MT /W3 /Ox /nologo /D_UNICODE /DUNICODE'.split()
|
cflags = '/c /EHsc /MT /W3 /Ox /nologo /D_UNICODE /DUNICODE'.split()
|
||||||
|
|
||||||
for exe_name in ('calibre.exe', 'ebook-viewer.exe', 'ebook-edit.exe'):
|
for exe_name in ('calibre.exe', 'ebook-viewer.exe', 'ebook-edit.exe'):
|
||||||
exe = j(base, exe_name.replace('.exe', '-portable.exe'))
|
exe = j(base, exe_name.replace('.exe', '-portable.exe'))
|
||||||
printf('Compiling', exe)
|
printf('Compiling', exe)
|
||||||
cmd = [CL] + cflags + ['/DEXE_NAME="%s"' % exe_name, '/Fo' + obj, '/Tc' + src]
|
cmd = [CL] + cflags + ['/Fo' + obj, '/Tp' + src]
|
||||||
run_compiler(env, *cmd)
|
run_compiler(env, *cmd)
|
||||||
printf('Linking', exe)
|
printf('Linking', exe)
|
||||||
desc = {
|
desc = {
|
||||||
@ -357,7 +357,7 @@ def build_portable(env):
|
|||||||
'/RELEASE',
|
'/RELEASE',
|
||||||
'/ENTRY:wWinMainCRTStartup',
|
'/ENTRY:wWinMainCRTStartup',
|
||||||
'/OUT:' + exe, embed_resources(env, exe, desc=desc, product_description=desc),
|
'/OUT:' + exe, embed_resources(env, exe, desc=desc, product_description=desc),
|
||||||
obj, 'User32.lib']
|
obj, 'User32.lib', 'Shell32.lib']
|
||||||
run(*cmd)
|
run(*cmd)
|
||||||
|
|
||||||
printf('Creating portable installer')
|
printf('Creating portable installer')
|
||||||
|
@ -1,158 +0,0 @@
|
|||||||
#ifndef UNICODE
|
|
||||||
#define UNICODE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef _UNICODE
|
|
||||||
#define _UNICODE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
#include <tchar.h>
|
|
||||||
#include <wchar.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#define BUFSIZE 4096
|
|
||||||
|
|
||||||
void show_error(LPCTSTR msg) {
|
|
||||||
MessageBeep(MB_ICONERROR);
|
|
||||||
MessageBox(NULL, msg, _T("Error"), MB_OK|MB_ICONERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
void show_detailed_error(LPCTSTR preamble, LPCTSTR msg, int code) {
|
|
||||||
LPTSTR buf;
|
|
||||||
buf = (LPTSTR)LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*
|
|
||||||
(_tcslen(msg) + _tcslen(preamble) + 80));
|
|
||||||
|
|
||||||
_sntprintf_s(buf,
|
|
||||||
LocalSize(buf) / sizeof(TCHAR), _TRUNCATE,
|
|
||||||
_T("%s\r\n %s (Error Code: %d)\r\n"),
|
|
||||||
preamble, msg, code);
|
|
||||||
|
|
||||||
show_error(buf);
|
|
||||||
LocalFree(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
void show_last_error_crt(LPCTSTR preamble) {
|
|
||||||
TCHAR buf[BUFSIZE];
|
|
||||||
int err = 0;
|
|
||||||
|
|
||||||
_get_errno(&err);
|
|
||||||
_tcserror_s(buf, BUFSIZE, err);
|
|
||||||
show_detailed_error(preamble, buf, err);
|
|
||||||
}
|
|
||||||
|
|
||||||
void show_last_error(LPCTSTR preamble) {
|
|
||||||
TCHAR *msg = NULL;
|
|
||||||
DWORD dw = GetLastError();
|
|
||||||
|
|
||||||
FormatMessage(
|
|
||||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
||||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
|
||||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
||||||
NULL,
|
|
||||||
dw,
|
|
||||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
||||||
(LPTSTR)&msg,
|
|
||||||
0, NULL );
|
|
||||||
|
|
||||||
show_detailed_error(preamble, msg, (int)dw);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
LPTSTR get_app_dir() {
|
|
||||||
LPTSTR buf, buf2, buf3;
|
|
||||||
DWORD sz;
|
|
||||||
TCHAR drive[4] = _T("\0\0\0");
|
|
||||||
errno_t err;
|
|
||||||
|
|
||||||
buf = (LPTSTR)calloc(BUFSIZE, sizeof(TCHAR));
|
|
||||||
buf2 = (LPTSTR)calloc(BUFSIZE, sizeof(TCHAR));
|
|
||||||
buf3 = (LPTSTR)calloc(BUFSIZE, sizeof(TCHAR));
|
|
||||||
|
|
||||||
sz = GetModuleFileName(NULL, buf, BUFSIZE);
|
|
||||||
|
|
||||||
if (sz == 0 || sz > BUFSIZE-1) {
|
|
||||||
show_error(_T("Failed to get path to calibre-portable.exe"));
|
|
||||||
ExitProcess(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
err = _tsplitpath_s(buf, drive, 4, buf2, BUFSIZE, NULL, 0, NULL, 0);
|
|
||||||
|
|
||||||
if (err != 0) {
|
|
||||||
show_last_error_crt(_T("Failed to split path to calibre-portable.exe"));
|
|
||||||
ExitProcess(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
_sntprintf_s(buf3, BUFSIZE-1, _TRUNCATE, _T("%s%s"), drive, buf2);
|
|
||||||
if (_tcslen(buf3) > 58) {
|
|
||||||
_snwprintf_s(buf, 4*MAX_PATH, _TRUNCATE,
|
|
||||||
L"Path to Calibre Portable (%s) too long. Must be less than 59 characters.", buf3);
|
|
||||||
show_error(buf);
|
|
||||||
ExitProcess(1);
|
|
||||||
}
|
|
||||||
free(buf); free(buf2);
|
|
||||||
return buf3;
|
|
||||||
}
|
|
||||||
|
|
||||||
void launch_calibre(LPCTSTR exe, LPCTSTR config_dir) {
|
|
||||||
DWORD dwFlags=0;
|
|
||||||
STARTUPINFO si;
|
|
||||||
PROCESS_INFORMATION pi;
|
|
||||||
BOOL fSuccess;
|
|
||||||
|
|
||||||
if (! SetEnvironmentVariable(_T("CALIBRE_CONFIG_DIRECTORY"), config_dir)) {
|
|
||||||
show_last_error(_T("Failed to set environment variables"));
|
|
||||||
ExitProcess(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! SetEnvironmentVariable(_T("CALIBRE_PORTABLE_BUILD"), exe)) {
|
|
||||||
show_last_error(_T("Failed to set environment variables"));
|
|
||||||
ExitProcess(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
dwFlags = CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_PROCESS_GROUP;
|
|
||||||
|
|
||||||
ZeroMemory( &si, sizeof(si) );
|
|
||||||
si.cb = sizeof(si);
|
|
||||||
ZeroMemory( &pi, sizeof(pi) );
|
|
||||||
|
|
||||||
fSuccess = CreateProcess(exe, NULL,
|
|
||||||
NULL, // Process handle not inheritable
|
|
||||||
NULL, // Thread handle not inheritable
|
|
||||||
FALSE, // Set handle inheritance to FALSE
|
|
||||||
dwFlags, // Creation flags http://msdn.microsoft.com/en-us/library/ms684863(v=vs.85).aspx
|
|
||||||
NULL, // Use parent's environment block
|
|
||||||
NULL, // Use parent's starting directory
|
|
||||||
&si, // Pointer to STARTUPINFO structure
|
|
||||||
&pi // Pointer to PROCESS_INFORMATION structure
|
|
||||||
);
|
|
||||||
|
|
||||||
if (fSuccess == 0) {
|
|
||||||
show_last_error(_T("Failed to launch the calibre program"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close process and thread handles.
|
|
||||||
CloseHandle( pi.hProcess );
|
|
||||||
CloseHandle( pi.hThread );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
|
|
||||||
{
|
|
||||||
LPTSTR app_dir, config_dir, exe;
|
|
||||||
|
|
||||||
app_dir = get_app_dir();
|
|
||||||
config_dir = (LPTSTR)calloc(BUFSIZE, sizeof(TCHAR));
|
|
||||||
exe = (LPTSTR)calloc(BUFSIZE, sizeof(TCHAR));
|
|
||||||
|
|
||||||
_sntprintf_s(config_dir, BUFSIZE, _TRUNCATE, _T("%sCalibre Settings"), app_dir);
|
|
||||||
_sntprintf_s(exe, BUFSIZE, _TRUNCATE, _T("%sCalibre\\%s"), app_dir, _T(EXE_NAME));
|
|
||||||
|
|
||||||
launch_calibre(exe, config_dir);
|
|
||||||
|
|
||||||
free(app_dir); free(config_dir); free(exe);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
238
bypy/windows/portable.cpp
Normal file
238
bypy/windows/portable.cpp
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
/*
|
||||||
|
* portable.cpp
|
||||||
|
* Copyright (C) 2020 Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
*
|
||||||
|
* Distributed under terms of the GPL3 license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef UNICODE
|
||||||
|
#define UNICODE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _UNICODE
|
||||||
|
#define _UNICODE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <tchar.h>
|
||||||
|
#include <wchar.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define BUFSIZE 4096
|
||||||
|
|
||||||
|
|
||||||
|
// error handling {{{
|
||||||
|
void show_error(LPCWSTR msg) {
|
||||||
|
MessageBeep(MB_ICONERROR);
|
||||||
|
MessageBoxW(NULL, msg, L"Error", MB_OK|MB_ICONERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_detailed_error(LPCTSTR preamble, LPCTSTR msg, int code) {
|
||||||
|
LPTSTR buf;
|
||||||
|
buf = (LPTSTR)LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*
|
||||||
|
(_tcslen(msg) + _tcslen(preamble) + 80));
|
||||||
|
|
||||||
|
_sntprintf_s(buf,
|
||||||
|
LocalSize(buf) / sizeof(TCHAR), _TRUNCATE,
|
||||||
|
_T("%s\r\n %s (Error Code: %d)\r\n"),
|
||||||
|
preamble, msg, code);
|
||||||
|
|
||||||
|
show_error(buf);
|
||||||
|
LocalFree(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_last_error_crt(LPCTSTR preamble) {
|
||||||
|
TCHAR buf[BUFSIZE];
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
_get_errno(&err);
|
||||||
|
_tcserror_s(buf, BUFSIZE, err);
|
||||||
|
show_detailed_error(preamble, buf, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_last_error(LPCTSTR preamble) {
|
||||||
|
TCHAR *msg = NULL;
|
||||||
|
DWORD dw = GetLastError();
|
||||||
|
|
||||||
|
FormatMessage(
|
||||||
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||||
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||||
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
|
NULL,
|
||||||
|
dw,
|
||||||
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
(LPTSTR)&msg,
|
||||||
|
0, NULL );
|
||||||
|
|
||||||
|
show_detailed_error(preamble, msg, (int)dw);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// }}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static bool
|
||||||
|
get_app_dir(std::wstring& ans, std::wstring& exe_name) {
|
||||||
|
DWORD sz;
|
||||||
|
static wchar_t drive[_MAX_DRIVE] = {0};
|
||||||
|
static wchar_t buf[BUFSIZE] = {0}, dirpath[_MAX_DIR] = {0}, fname[_MAX_FNAME] = {0}, ext[_MAX_EXT] = {0};
|
||||||
|
|
||||||
|
sz = GetModuleFileName(NULL, buf, BUFSIZE);
|
||||||
|
|
||||||
|
if (sz == 0 || sz > BUFSIZE-1) {
|
||||||
|
show_error(L"Failed to get path to portable launcher");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
errno_t err = _wsplitpath_s(buf, drive, _MAX_DRIVE, dirpath, _MAX_DIR, fname, _MAX_FNAME, ext, _MAX_EXT);
|
||||||
|
|
||||||
|
if (err != 0) {
|
||||||
|
show_last_error_crt(L"Failed to split path to portable launcher");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ans.append(drive); ans.append(dirpath);
|
||||||
|
if (ans.length() > 58) {
|
||||||
|
std::wstring msg;
|
||||||
|
msg.append(L"Path to Calibre Portable (");
|
||||||
|
msg.append(ans);
|
||||||
|
msg.append(L") too long. Must be less than 59 characters.");
|
||||||
|
show_error(msg.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
exe_name.append(fname);
|
||||||
|
exe_name.erase(exe_name.length() - sizeof("portable"), sizeof("portable"));
|
||||||
|
exe_name.append(ext);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
quote_argv(const std::wstring& arg, std::wstring& cmd_line) {
|
||||||
|
if (!arg.empty() && arg.find_first_of(L" \t\n\v\"") == arg.npos) {
|
||||||
|
cmd_line.append(arg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cmd_line.push_back(L'"');
|
||||||
|
|
||||||
|
for (auto iterator = arg.begin() ; ; ++iterator) {
|
||||||
|
unsigned num_back_slashes = 0;
|
||||||
|
|
||||||
|
while (iterator != arg.end() && *iterator == L'\\') {
|
||||||
|
++iterator;
|
||||||
|
++num_back_slashes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iterator == arg.end()) {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Escape all backslashes, but let the terminating
|
||||||
|
// double quotation mark we add below be interpreted
|
||||||
|
// as a metacharacter.
|
||||||
|
//
|
||||||
|
|
||||||
|
cmd_line.append (num_back_slashes * 2, L'\\');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (*iterator == L'"') {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Escape all backslashes and the following
|
||||||
|
// double quotation mark.
|
||||||
|
//
|
||||||
|
|
||||||
|
cmd_line.append (num_back_slashes * 2 + 1, L'\\');
|
||||||
|
cmd_line.push_back (*iterator);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Backslashes aren't special here.
|
||||||
|
//
|
||||||
|
|
||||||
|
cmd_line.append (num_back_slashes, L'\\');
|
||||||
|
cmd_line.push_back (*iterator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_line.push_back (L'"');
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
launch_exe(LPCWSTR exe_path, const std::wstring &cmd_line, LPCWSTR config_dir) {
|
||||||
|
DWORD dwFlags=0;
|
||||||
|
STARTUPINFO si;
|
||||||
|
PROCESS_INFORMATION pi;
|
||||||
|
BOOL fSuccess;
|
||||||
|
if (cmd_line.length() > BUFSIZE - 4) {
|
||||||
|
show_error(L"Path to executable in portable folder too long.");
|
||||||
|
ExitProcess(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SetEnvironmentVariable(_T("CALIBRE_CONFIG_DIRECTORY"), config_dir)) {
|
||||||
|
show_last_error(_T("Failed to set environment variables"));
|
||||||
|
ExitProcess(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SetEnvironmentVariable(_T("CALIBRE_PORTABLE_BUILD"), exe_path)) {
|
||||||
|
show_last_error(_T("Failed to set environment variables"));
|
||||||
|
ExitProcess(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
dwFlags = CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_PROCESS_GROUP;
|
||||||
|
|
||||||
|
ZeroMemory( &si, sizeof(si) );
|
||||||
|
si.cb = sizeof(si);
|
||||||
|
ZeroMemory( &pi, sizeof(pi) );
|
||||||
|
static wchar_t mutable_cmdline[BUFSIZE] = {0};
|
||||||
|
cmd_line.copy(mutable_cmdline, BUFSIZE-1);
|
||||||
|
|
||||||
|
fSuccess = CreateProcess(NULL, mutable_cmdline,
|
||||||
|
NULL, // Process handle not inheritable
|
||||||
|
NULL, // Thread handle not inheritable
|
||||||
|
FALSE, // Set handle inheritance to FALSE
|
||||||
|
dwFlags, // Creation flags http://msdn.microsoft.com/en-us/library/ms684863(v=vs.85).aspx
|
||||||
|
NULL, // Use parent's environment block
|
||||||
|
NULL, // Use parent's starting directory
|
||||||
|
&si, // Pointer to STARTUPINFO structure
|
||||||
|
&pi // Pointer to PROCESS_INFORMATION structure
|
||||||
|
);
|
||||||
|
|
||||||
|
if (fSuccess == 0) {
|
||||||
|
show_last_error(_T("Failed to launch the calibre program"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close process and thread handles.
|
||||||
|
CloseHandle( pi.hProcess );
|
||||||
|
CloseHandle( pi.hThread );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int WINAPI
|
||||||
|
wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR orig_cmd_line, int nCmdShow) {
|
||||||
|
std::wstring exe, config_dir, cmd_line, application_dir, exe_name;
|
||||||
|
|
||||||
|
if (!get_app_dir(application_dir, exe_name)) return 1;
|
||||||
|
config_dir.append(application_dir); config_dir.append(L"Calibre Settings");
|
||||||
|
exe.append(application_dir); exe.append(L"Calibre\\"); exe.append(exe_name);
|
||||||
|
|
||||||
|
// Note that orig_cmd_line does not have argv[0] as the executable name
|
||||||
|
int argc;
|
||||||
|
wchar_t **argv = CommandLineToArgvW(orig_cmd_line, &argc);
|
||||||
|
if (argv == NULL) {
|
||||||
|
show_last_error(L"Failed to convert cmdline to argv array");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
quote_argv(exe, cmd_line);
|
||||||
|
for (int i = 0; i < argc; i++) {
|
||||||
|
std::wstring arg(argv[i]);
|
||||||
|
cmd_line.push_back(L' ');
|
||||||
|
quote_argv(arg, cmd_line);
|
||||||
|
}
|
||||||
|
LocalFree(argv);
|
||||||
|
launch_exe(exe.c_str(), cmd_line.c_str(), config_dir.c_str());
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user