summaryrefslogtreecommitdiff
path: root/comphelper
diff options
context:
space:
mode:
authorMarkus Mohrhard <markus.mohrhard@googlemail.com>2017-08-07 15:35:30 +0200
committerMarkus Mohrhard <markus.mohrhard@googlemail.com>2017-08-08 07:07:21 +0200
commit5875fd0313cb0aeb7e63d5ea9455f83ea18787b5 (patch)
tree1e7ed70496f073d88bbaba3789a8e1160cd8f790 /comphelper
parent9d184e2cc9a9a78a61ae9b91a66e1153f778d9a4 (diff)
updater: move the windows process starting code to comphelper
Change-Id: I1a499f57d01ee28afdb2c4f85dc976f2e6837dfd Reviewed-on: https://gerrit.libreoffice.org/40837 Tested-by: Jenkins <ci@libreoffice.org> Reviewed-by: Markus Mohrhard <markus.mohrhard@googlemail.com>
Diffstat (limited to 'comphelper')
-rw-r--r--comphelper/Module_comphelper.mk2
-rw-r--r--comphelper/StaticLibrary_windows_process.mk24
-rw-r--r--comphelper/source/windows/windows_process.cxx270
3 files changed, 296 insertions, 0 deletions
diff --git a/comphelper/Module_comphelper.mk b/comphelper/Module_comphelper.mk
index e04d230d2069..30ac708a927d 100644
--- a/comphelper/Module_comphelper.mk
+++ b/comphelper/Module_comphelper.mk
@@ -21,6 +21,8 @@ $(eval $(call gb_Module_Module,comphelper))
$(eval $(call gb_Module_add_targets,comphelper,\
Library_comphelper \
+ $(if $(filter WNT,$(OS)),\
+ StaticLibrary_windows_process )\
))
$(eval $(call gb_Module_add_subsequentcheck_targets,comphelper,\
diff --git a/comphelper/StaticLibrary_windows_process.mk b/comphelper/StaticLibrary_windows_process.mk
new file mode 100644
index 000000000000..1b086eac952b
--- /dev/null
+++ b/comphelper/StaticLibrary_windows_process.mk
@@ -0,0 +1,24 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_StaticLibrary_StaticLibrary,windows_process))
+
+$(eval $(call gb_StaticLibrary_set_include,windows_process,\
+ $$(INCLUDE) \
+))
+
+$(eval $(call gb_StaticLibrary_add_defs,windows_process,\
+ -DUNICODE \
+))
+
+$(eval $(call gb_StaticLibrary_add_exception_objects,windows_process,\
+ comphelper/source/windows/windows_process \
+))
+
+# vim:set shiftwidth=4 tabstop=4 noexpandtab: */
diff --git a/comphelper/source/windows/windows_process.cxx b/comphelper/source/windows/windows_process.cxx
new file mode 100644
index 000000000000..1c782d7a289f
--- /dev/null
+++ b/comphelper/source/windows/windows_process.cxx
@@ -0,0 +1,270 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <windows.h>
+#include <shellapi.h>
+
+#include <cstddef>
+#include <cwchar>
+
+// Needed for CreateEnvironmentBlock
+#include <userenv.h>
+#pragma comment(lib, "userenv.lib")
+
+/**
+ * Get the length that the string will take and takes into account the
+ * additional length if the string needs to be quoted and if characters need to
+ * be escaped.
+ */
+static int ArgStrLen(const wchar_t *s)
+{
+ int backslashes = 0;
+ int i = wcslen(s);
+ BOOL hasDoubleQuote = wcschr(s, L'"') != nullptr;
+ // Only add doublequotes if the string contains a space or a tab
+ BOOL addDoubleQuotes = wcspbrk(s, L" \t") != nullptr;
+
+ if (addDoubleQuotes)
+ {
+ i += 2; // initial and final duoblequote
+ }
+
+ if (hasDoubleQuote)
+ {
+ while (*s)
+ {
+ if (*s == '\\')
+ {
+ ++backslashes;
+ }
+ else
+ {
+ if (*s == '"')
+ {
+ // Escape the doublequote and all backslashes preceding the doublequote
+ i += backslashes + 1;
+ }
+
+ backslashes = 0;
+ }
+
+ ++s;
+ }
+ }
+
+ return i;
+}
+
+/**
+ * Copy string "s" to string "d", quoting the argument as appropriate and
+ * escaping doublequotes along with any backslashes that immediately precede
+ * doublequotes.
+ * The CRT parses this to retrieve the original argc/argv that we meant,
+ * see STDARGV.C in the MSVC CRT sources.
+ *
+ * @return the end of the string
+ */
+static wchar_t* ArgToString(wchar_t *d, const wchar_t *s)
+{
+ int backslashes = 0;
+ BOOL hasDoubleQuote = wcschr(s, L'"') != nullptr;
+ // Only add doublequotes if the string contains a space or a tab
+ BOOL addDoubleQuotes = wcspbrk(s, L" \t") != nullptr;
+
+ if (addDoubleQuotes)
+ {
+ *d = '"'; // initial doublequote
+ ++d;
+ }
+
+ if (hasDoubleQuote)
+ {
+ int i;
+ while (*s)
+ {
+ if (*s == '\\')
+ {
+ ++backslashes;
+ }
+ else
+ {
+ if (*s == '"')
+ {
+ // Escape the doublequote and all backslashes preceding the doublequote
+ for (i = 0; i <= backslashes; ++i)
+ {
+ *d = '\\';
+ ++d;
+ }
+ }
+
+ backslashes = 0;
+ }
+
+ *d = *s;
+ ++d;
+ ++s;
+ }
+ }
+ else
+ {
+ wcscpy(d, s);
+ d += wcslen(s);
+ }
+
+ if (addDoubleQuotes)
+ {
+ *d = '"'; // final doublequote
+ ++d;
+ }
+
+ return d;
+}
+
+/**
+ * Creates a command line from a list of arguments. The returned
+ * string is allocated with "malloc" and should be "free"d.
+ *
+ * argv is UTF8
+ */
+wchar_t*
+MakeCommandLine(int argc, wchar_t **argv)
+{
+ int i;
+ int len = 0;
+
+ // The + 1 of the last argument handles the allocation for null termination
+ for (i = 0; i < argc; ++i)
+ len += ArgStrLen(argv[i]) + 1;
+
+ // Protect against callers that pass 0 arguments
+ if (len == 0)
+ len = 1;
+
+ wchar_t *s = (wchar_t*) malloc(len * sizeof(wchar_t));
+ if (!s)
+ return nullptr;
+
+ wchar_t *c = s;
+ for (i = 0; i < argc; ++i)
+ {
+ c = ArgToString(c, argv[i]);
+ if (i + 1 != argc)
+ {
+ *c = ' ';
+ ++c;
+ }
+ }
+
+ *c = '\0';
+
+ return s;
+}
+
+/**
+ * Launch a child process with the specified arguments.
+ * @note argv[0] is ignored
+ * @note The form of this function that takes char **argv expects UTF-8
+ */
+BOOL
+WinLaunchChild(const wchar_t *exePath,
+ int argc, wchar_t **argv,
+ HANDLE userToken = nullptr,
+ HANDLE *hProcess = nullptr);
+
+BOOL
+WinLaunchChild(const wchar_t *exePath,
+ int argc,
+ wchar_t **argv,
+ HANDLE userToken,
+ HANDLE *hProcess)
+{
+ wchar_t *cl;
+ BOOL ok;
+
+ cl = MakeCommandLine(argc, argv);
+ if (!cl)
+ {
+ return FALSE;
+ }
+
+ STARTUPINFOW si = {0};
+ si.cb = sizeof(STARTUPINFOW);
+ si.lpDesktop = L"winsta0\\Default";
+ PROCESS_INFORMATION pi = {0};
+
+ if (userToken == nullptr)
+ {
+ ok = CreateProcessW(exePath,
+ cl,
+ nullptr, // no special security attributes
+ nullptr, // no special thread attributes
+ FALSE, // don't inherit filehandles
+ 0, // creation flags
+ nullptr, // inherit my environment
+ nullptr, // use my current directory
+ &si,
+ &pi);
+ }
+ else
+ {
+ // Create an environment block for the process we're about to start using
+ // the user's token.
+ LPVOID environmentBlock = nullptr;
+ if (!CreateEnvironmentBlock(&environmentBlock, userToken, TRUE))
+ {
+ environmentBlock = nullptr;
+ }
+
+ ok = CreateProcessAsUserW(userToken,
+ exePath,
+ cl,
+ nullptr, // no special security attributes
+ nullptr, // no special thread attributes
+ FALSE, // don't inherit filehandles
+ 0, // creation flags
+ environmentBlock,
+ nullptr, // use my current directory
+ &si,
+ &pi);
+
+ if (environmentBlock)
+ {
+ DestroyEnvironmentBlock(environmentBlock);
+ }
+ }
+
+ if (ok)
+ {
+ if (hProcess)
+ {
+ *hProcess = pi.hProcess; // the caller now owns the HANDLE
+ }
+ else
+ {
+ CloseHandle(pi.hProcess);
+ }
+ CloseHandle(pi.hThread);
+ }
+ else
+ {
+ LPVOID lpMsgBuf = nullptr;
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ nullptr);
+ wprintf(L"Error restarting: %s\n", lpMsgBuf ? lpMsgBuf : L"(null)");
+ if (lpMsgBuf)
+ LocalFree(lpMsgBuf);
+ }
+
+ free(cl);
+
+ return ok;
+}