8221333: Replace Inno Setup with custom MSI wrapper for .exe bundler (missed files) JDK-8200758-branch
authorherrick
Mon, 17 Jun 2019 15:38:04 -0400
branchJDK-8200758-branch
changeset 57413 45c74e654794
parent 57412 02034583f4dc
child 57414 6eda749d3117
8221333: Replace Inno Setup with custom MSI wrapper for .exe bundler (missed files) Submitted-by: asemenyuk Reviewed-by: herrick, almatvee
src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExeBundler.java.html
src/jdk.jpackage/windows/native/libjpackage/ErrorHandling.cpp
src/jdk.jpackage/windows/native/libjpackage/ErrorHandling.h
src/jdk.jpackage/windows/native/libjpackage/FileUtils.cpp
src/jdk.jpackage/windows/native/libjpackage/FileUtils.h
src/jdk.jpackage/windows/native/libjpackage/Log.cpp
src/jdk.jpackage/windows/native/libjpackage/Log.h
src/jdk.jpackage/windows/native/libjpackage/ResourceEditor.cpp
src/jdk.jpackage/windows/native/libjpackage/ResourceEditor.h
src/jdk.jpackage/windows/native/libjpackage/SourceCodePos.h
src/jdk.jpackage/windows/native/libjpackage/SysInfo.h
src/jdk.jpackage/windows/native/libjpackage/UniqueHandle.h
src/jdk.jpackage/windows/native/libjpackage/WinErrorHandling.cpp
src/jdk.jpackage/windows/native/libjpackage/WinErrorHandling.h
src/jdk.jpackage/windows/native/libjpackage/WinSysInfo.cpp
src/jdk.jpackage/windows/native/libjpackage/WinSysInfo.h
src/jdk.jpackage/windows/native/libjpackage/tstrings.cpp
src/jdk.jpackage/windows/native/libjpackage/tstrings.h
src/jdk.jpackage/windows/native/msiwrapper/Executor.cpp
src/jdk.jpackage/windows/native/msiwrapper/Executor.h
src/jdk.jpackage/windows/native/msiwrapper/MsiWrapper.cpp
src/jdk.jpackage/windows/native/msiwrapper/Resources.cpp
src/jdk.jpackage/windows/native/msiwrapper/Resources.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExeBundler.java.html	Mon Jun 17 15:38:04 2019 -0400
@@ -0,0 +1,240 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head><meta charset="utf-8">
+<meta http-equiv="cache-control" content="no-cache" />
+<meta http-equiv="Pragma" content="no-cache" />
+<meta http-equiv="Expires" content="-1" />
+<!--
+   Note to customizers: the body of the webrev is IDed as SUNWwebrev
+   to allow easy overriding by users of webrev via the userContent.css
+   mechanism available in some browsers.
+
+   For example, to have all "removed" information be red instead of
+   brown, set a rule in your userContent.css file like:
+
+       body#SUNWwebrev span.removed { color: red ! important; }
+-->
+<style type="text/css" media="screen">
+body {
+    background-color: #eeeeee;
+}
+hr {
+    border: none 0;
+    border-top: 1px solid #aaa;
+    height: 1px;
+}
+div.summary {
+    font-size: .8em;
+    border-bottom: 1px solid #aaa;
+    padding-left: 1em;
+    padding-right: 1em;
+}
+div.summary h2 {
+    margin-bottom: 0.3em;
+}
+div.summary table th {
+    text-align: right;
+    vertical-align: top;
+    white-space: nowrap;
+}
+span.lineschanged {
+    font-size: 0.7em;
+}
+span.oldmarker {
+    color: red;
+    font-size: large;
+    font-weight: bold;
+}
+span.newmarker {
+    color: green;
+    font-size: large;
+    font-weight: bold;
+}
+span.removed {
+    color: brown;
+}
+span.changed {
+    color: blue;
+}
+span.new {
+    color: blue;
+    font-weight: bold;
+}
+a.print { font-size: x-small; }
+
+</style>
+
+<style type="text/css" media="print">
+pre { font-size: 0.8em; font-family: courier, monospace; }
+span.removed { color: #444; font-style: italic }
+span.changed { font-weight: bold; }
+span.new { font-weight: bold; }
+span.newmarker { font-size: 1.2em; font-weight: bold; }
+span.oldmarker { font-size: 1.2em; font-weight: bold; }
+a.print {display: none}
+hr { border: none 0; border-top: 1px solid #aaa; height: 1px; }
+</style>
+
+<title>New src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExeBundler.java</title>
+<body id="SUNWwebrev">
+<pre>
+   1 /*
+   2  * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
+   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+   4  *
+   5  * This code is free software; you can redistribute it and/or modify it
+   6  * under the terms of the GNU General Public License version 2 only, as
+   7  * published by the Free Software Foundation.  Oracle designates this
+   8  * particular file as subject to the "Classpath" exception as provided
+   9  * by Oracle in the LICENSE file that accompanied this code.
+  10  *
+  11  * This code is distributed in the hope that it will be useful, but WITHOUT
+  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+  14  * version 2 for more details (a copy is included in the LICENSE file that
+  15  * accompanied this code).
+  16  *
+  17  * You should have received a copy of the GNU General Public License version
+  18  * 2 along with this work; if not, write to the Free Software Foundation,
+  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+  20  *
+  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+  22  * or visit www.oracle.com if you need additional information or have any
+  23  * questions.
+  24  */
+  25 package jdk.jpackage.internal;
+  26 
+  27 import java.io.*;
+  28 import java.nio.file.Files;
+  29 import java.nio.file.Path;
+  30 import java.nio.file.Paths;
+  31 import java.text.MessageFormat;
+  32 import java.util.*;
+  33 
+  34 public class WinExeBundler extends AbstractBundler {
+  35 
+  36     static {
+  37         System.loadLibrary("jpackage");
+  38     }
+  39 
+  40     private static final ResourceBundle I18N = ResourceBundle.getBundle(
+  41             "jdk.jpackage.internal.resources.WinResources");
+  42 
+  43     public static final BundlerParamInfo&lt;WinAppBundler&gt; APP_BUNDLER
+  44             = new WindowsBundlerParam&lt;&gt;(
+  45                     "win.app.bundler",
+  46                     WinAppBundler.class,
+  47                     params -&gt; new WinAppBundler(),
+  48                     null);
+  49 
+  50     public static final BundlerParamInfo&lt;File&gt; EXE_IMAGE_DIR
+  51             = new WindowsBundlerParam&lt;&gt;(
+  52                     "win.exe.imageDir",
+  53                     File.class,
+  54                     params -&gt; {
+  55                         File imagesRoot = IMAGES_ROOT.fetchFrom(params);
+  56                         if (!imagesRoot.exists()) {
+  57                             imagesRoot.mkdirs();
+  58                         }
+  59                         return new File(imagesRoot, "win-exe.image");
+  60                     },
+  61                     (s, p) -&gt; null);
+  62 
+  63     private final static String EXE_WRAPPER_NAME = "msiwrapper.exe";
+  64 
+  65     @Override
+  66     public String getName() {
+  67         return getString("exe.bundler.name");
+  68     }
+  69 
+  70     @Override
+  71     public String getDescription() {
+  72         return getString("exe.bundler.description");
+  73     }
+  74 
+  75     @Override
+  76     public String getID() {
+  77         return "exe";
+  78     }
+  79 
+  80     @Override
+  81     public String getBundleType() {
+  82         return "INSTALLER";
+  83     }
+  84 
+  85     @Override
+  86     public Collection&lt;BundlerParamInfo&lt;?&gt;&gt; getBundleParameters() {
+  87         return new WinMsiBundler().getBundleParameters();
+  88     }
+  89 
+  90     @Override
+  91     public File execute(Map&lt;String, ? super Object&gt; params,
+  92             File outputParentDir) throws PackagerException {
+  93         return bundle(params, outputParentDir);
+  94     }
+  95 
+  96     @Override
+  97     public boolean supported(boolean platformInstaller) {
+  98         return (Platform.getPlatform() == Platform.WINDOWS);
+  99     }
+ 100 
+ 101     @Override
+ 102     public boolean validate(Map&lt;String, ? super Object&gt; params)
+ 103             throws UnsupportedPlatformException, ConfigException {
+ 104         return new WinMsiBundler().validate(params);
+ 105     }
+ 106 
+ 107     public File bundle(Map&lt;String, ? super Object&gt; params, File outdir)
+ 108             throws PackagerException {
+ 109 
+ 110         File exeImageDir = EXE_IMAGE_DIR.fetchFrom(params);
+ 111 
+ 112         // Write msi to temporary directory.
+ 113         File msi = new WinMsiBundler().bundle(params, exeImageDir);
+ 114 
+ 115         try {
+ 116             return buildEXE(msi, outdir);
+ 117         } catch (IOException ex) {
+ 118             Log.verbose(ex);
+ 119             throw new PackagerException(ex);
+ 120         }
+ 121     }
+ 122 
+ 123     private File buildEXE(File msi, File outdir)
+ 124             throws IOException {
+ 125 
+ 126         Log.verbose(MessageFormat.format(
+ 127                 getString("message.outputting-to-location"),
+ 128                 outdir.getAbsolutePath()));
+ 129 
+ 130         // Copy template msi wrapper next to msi file
+ 131         String exePath = msi.getAbsolutePath();
+ 132         exePath = exePath.substring(0, exePath.lastIndexOf('.')) + ".exe";
+ 133         try (InputStream is = getResourceAsStream(EXE_WRAPPER_NAME)) {
+ 134             Files.copy(is, Path.of(exePath));
+ 135         }
+ 136         // Embed msi in msi wrapper exe.
+ 137         embedMSI(exePath, msi.getAbsolutePath());
+ 138 
+ 139         Path dstExePath = Paths.get(outdir.getAbsolutePath(), Path.of(exePath).getFileName().toString());
+ 140         Files.deleteIfExists(dstExePath);
+ 141 
+ 142         Files.copy(Path.of(exePath), dstExePath);
+ 143 
+ 144         Log.verbose(MessageFormat.format(
+ 145                 getString("message.output-location"),
+ 146                 outdir.getAbsolutePath()));
+ 147 
+ 148         return dstExePath.toFile();
+ 149     }
+ 150 
+ 151     private static String getString(String key)
+ 152             throws MissingResourceException {
+ 153         return I18N.getString(key);
+ 154     }
+ 155 
+ 156     private static native int embedMSI(String exePath, String msiPath);
+ 157 }
+</pre></body></html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/ErrorHandling.cpp	Mon Jun 17 15:38:04 2019 -0400
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <algorithm>
+
+#include "ErrorHandling.h"
+#include "Log.h"
+
+
+namespace {
+
+tstring getFilename(const SourceCodePos& pos) {
+    const std::string buf(pos.file);
+    const std::string::size_type idx = buf.find_last_of("\\/");
+    if (idx == std::string::npos) {
+        return tstrings::fromUtf8(buf);
+    }
+    return tstrings::fromUtf8(buf.substr(idx + 1));
+}
+
+void reportError(const SourceCodePos& pos, const tstring& msg) {
+    Logger::defaultLogger().log(Logger::LOG_ERROR, getFilename(pos).c_str(),
+        pos.lno, tstrings::fromUtf8(pos.func).c_str(), msg);
+}
+
+} // namespace
+
+void reportError(const SourceCodePos& pos, const std::exception& e) {
+    reportError(pos, (tstrings::any() << "Exception with message \'"
+                                        << e.what() << "\' caught").tstr());
+}
+
+
+void reportUnknownError(const SourceCodePos& pos) {
+    reportError(pos, _T("Unknown exception caught"));
+}
+
+
+std::string makeMessage(const std::exception& e, const SourceCodePos& pos) {
+    std::ostringstream printer;
+    printer << getFilename(pos) << "(" << pos.lno << ") at "
+            << pos.func << "(): "
+            << e.what();
+    return printer.str();
+}
+
+
+namespace {
+
+bool isNotSpace(int chr) {
+    return isspace(chr) == 0;
+}
+
+
+enum TrimMode {
+    TrimLeading = 0x10,
+    TrimTrailing = 0x20,
+    TrimBoth = TrimLeading | TrimTrailing
+};
+
+// Returns position of the last printed character in the given string.
+// Returns std::string::npos if nothing was printed.
+size_t printWithoutWhitespaces(std::ostream& out, const std::string& str,
+                                                            TrimMode mode) {
+    std::string::const_reverse_iterator it = str.rbegin();
+    std::string::const_reverse_iterator end = str.rend();
+
+    if (mode & TrimLeading) {
+        // skip leading whitespace
+        std::string::const_iterator entry = std::find_if(str.begin(),
+                                                str.end(), isNotSpace);
+        end = std::string::const_reverse_iterator(entry);
+    }
+
+    if (mode & TrimTrailing) {
+        // skip trailing whitespace
+        it = std::find_if(it, end, isNotSpace);
+    }
+
+    if (it == end) {
+        return std::string::npos;
+    }
+
+    const size_t pos = str.rend() - end;
+    const size_t len = end - it;
+    out.write(str.c_str() + pos, len);
+    return pos + len - 1;
+}
+
+} // namespace
+
+std::string joinErrorMessages(const std::string& a, const std::string& b) {
+    const std::string endPhraseChars(";.,:!?");
+    const std::string space(" ");
+    const std::string dotAndSpace(". ");
+
+    std::ostringstream printer;
+    printer.exceptions(std::ios::failbit | std::ios::badbit);
+
+    size_t idx = printWithoutWhitespaces(printer, a, TrimTrailing);
+    size_t extra = 0;
+    if (idx < a.size() && endPhraseChars.find(a[idx]) == std::string::npos) {
+        printer << dotAndSpace;
+        extra = dotAndSpace.size();
+    } else if (idx != std::string::npos) {
+        printer << space;
+        extra = space.size();
+    }
+
+    idx = printWithoutWhitespaces(printer, b, TrimBoth);
+
+    const std::string str = printer.str();
+
+    if (std::string::npos == idx && extra) {
+        // Nothing printed from the 'b' message. Backout delimiter string.
+        return str.substr(0, str.size() - extra);
+    }
+    return str;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/ErrorHandling.h	Mon Jun 17 15:38:04 2019 -0400
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ErrorHandling_h
+#define ErrorHandling_h
+
+
+#include <stdexcept>
+
+#include "SourceCodePos.h"
+#include "tstrings.h"
+
+
+//
+// Exception handling helpers. Allow transparent exception logging.
+// Use as follows:
+//
+//  void foo () {
+//      JP_TRY;
+//
+//      if (!do_something()) {
+//          JP_THROW("do_something() failed");
+//      }
+//
+//      JP_CATCH_ALL;
+//  }
+//
+
+
+// Logs std::exception caught at 'pos'.
+void reportError(const SourceCodePos& pos, const std::exception& e);
+// Logs unknown exception caught at 'pos'.
+// Assumed to be called from catch (...) {}
+void reportUnknownError(const SourceCodePos& pos);
+
+std::string makeMessage(const std::exception& e, const SourceCodePos& pos);
+
+std::string joinErrorMessages(const std::string& a, const std::string& b);
+
+
+template <class Base>
+class JpError: public Base {
+public:
+    JpError(const Base& e, const SourceCodePos& pos):
+                                        Base(e), msg(::makeMessage(e, pos)) {
+    }
+
+    ~JpError() throw() {
+    }
+
+    // override Base::what()
+    const char* what() const throw() {
+        return msg.c_str();
+    }
+private:
+    // Assert Base is derived from std::exception
+    enum { isDerivedFromStdException =
+                        sizeof(static_cast<std::exception*>((Base*)0)) };
+
+    std::string msg;
+};
+
+template <class T>
+inline JpError<T> makeException(const T& obj, const SourceCodePos& p) {
+    return JpError<T>(obj, p);
+}
+
+inline JpError<std::runtime_error> makeException(
+                            const std::string& msg, const SourceCodePos& p) {
+    return JpError<std::runtime_error>(std::runtime_error(msg), p);
+}
+
+inline JpError<std::runtime_error> makeException(
+                        const tstrings::any& msg, const SourceCodePos& p) {
+    return makeException(msg.str(), p);
+}
+
+inline JpError<std::runtime_error> makeException(
+                    std::string::const_pointer msg, const SourceCodePos& p) {
+    return makeException(std::string(msg), p);
+}
+
+
+#define JP_REPORT_ERROR(e)          reportError(JP_SOURCE_CODE_POS, e)
+#define JP_REPORT_UNKNOWN_ERROR     reportUnknownError(JP_SOURCE_CODE_POS)
+
+// Redefine locally in cpp file(s) if need more handling than just reporting
+#define JP_HANDLE_ERROR(e)          JP_REPORT_ERROR(e)
+#define JP_HANDLE_UNKNOWN_ERROR     JP_REPORT_UNKNOWN_ERROR
+
+
+#define JP_TRY                              \
+        try                                 \
+        {                                   \
+            do {} while(0)
+
+#define JP_DEFAULT_CATCH_EXCEPTIONS         \
+        JP_CATCH_STD_EXCEPTION              \
+        JP_CATCH_UNKNOWN_EXCEPTION
+
+#define JP_CATCH_EXCEPTIONS                 \
+        JP_DEFAULT_CATCH_EXCEPTIONS
+
+#define JP_CATCH_ALL                        \
+        }                                   \
+        JP_CATCH_EXCEPTIONS                 \
+        do {} while(0)
+
+#define JP_CATCH_STD_EXCEPTION              \
+        catch (const std::exception& e)     \
+        {                                   \
+            JP_HANDLE_ERROR(e);             \
+        }
+
+#define JP_CATCH_UNKNOWN_EXCEPTION          \
+        catch (...)                         \
+        {                                   \
+            JP_HANDLE_UNKNOWN_ERROR;        \
+        }
+
+
+#define JP_THROW(e) throw makeException((e), JP_SOURCE_CODE_POS)
+
+#define JP_NO_THROW(expr) \
+    JP_TRY; \
+    expr; \
+    JP_CATCH_ALL
+
+#endif // #ifndef ErrorHandling_h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/FileUtils.cpp	Mon Jun 17 15:38:04 2019 -0400
@@ -0,0 +1,702 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <memory>
+#include <algorithm>
+#include <shlwapi.h>
+
+#include "FileUtils.h"
+#include "WinErrorHandling.h"
+#include "Log.h"
+
+
+// Needed by FileUtils::isDirectoryNotEmpty
+#pragma comment(lib, "shlwapi")
+
+
+namespace FileUtils {
+
+namespace {
+
+
+tstring reservedFilenameChars() {
+    tstring buf;
+    for (char charCode = 0; charCode < 32; ++charCode) {
+        buf.append(1, charCode);
+    }
+    buf += _T("<>:\"|?*/\\");
+    return buf;
+}
+
+} // namespace
+
+bool isDirSeparator(const tstring::value_type c) {
+    return (c == '/' || c == '\\');
+}
+
+bool isFileExists(const tstring &filePath) {
+    return GetFileAttributes(filePath.c_str()) != INVALID_FILE_ATTRIBUTES;
+}
+
+namespace {
+bool isDirectoryAttrs(const DWORD attrs) {
+    return attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY) != 0;
+}
+} // namespace
+
+bool isDirectory(const tstring &filePath) {
+    return isDirectoryAttrs(GetFileAttributes(filePath.c_str()));
+}
+
+bool isDirectoryNotEmpty(const tstring &dirPath) {
+    if (!isDirectory(dirPath)) {
+        return false;
+    }
+    return FALSE == PathIsDirectoryEmpty(dirPath.c_str());
+}
+
+tstring dirname(const tstring &path)
+{
+    tstring::size_type pos = path.find_last_of(_T("\\/"));
+    if (pos != tstring::npos) {
+        pos = path.find_last_not_of(_T("\\/"), pos); // skip trailing slashes
+    }
+    return pos == tstring::npos ? tstring() : path.substr(0, pos + 1);
+}
+
+tstring basename(const tstring &path) {
+    const tstring::size_type pos = path.find_last_of(_T("\\/"));
+    if (pos == tstring::npos) {
+        return path;
+    }
+    return path.substr(pos + 1);
+}
+
+tstring suffix(const tstring &path) {
+    const tstring::size_type pos = path.rfind('.');
+    if (pos == tstring::npos) {
+        return tstring();
+    }
+    const tstring::size_type dirSepPos = path.find_first_of(_T("\\/"),
+                                                            pos + 1);
+    if (dirSepPos != tstring::npos) {
+        return tstring();
+    }
+    // test for '/..' and '..' cases
+    if (pos != 0 && path[pos - 1] == '.'
+                            && (pos == 1 || isDirSeparator(path[pos - 2]))) {
+        return tstring();
+    }
+    return path.substr(pos);
+}
+
+tstring combinePath(const tstring& parent, const tstring& child) {
+    if (parent.empty()) {
+        return child;
+    }
+    if (child.empty()) {
+        return parent;
+    }
+
+    tstring parentWOSlash = removeTrailingSlash(parent);
+    // also handle the case when child contains starting slash
+    bool childHasSlash = isDirSeparator(child.front());
+    tstring childWOSlash = childHasSlash ? child.substr(1) : child;
+
+    return parentWOSlash + _T("\\") + childWOSlash;
+}
+
+tstring removeTrailingSlash(const tstring& path) {
+    if (path.empty()) {
+        return path;
+    }
+    tstring::const_reverse_iterator it = path.rbegin();
+    tstring::const_reverse_iterator end = path.rend();
+
+    while (it != end && isDirSeparator(*it)) {
+        ++it;
+    }
+    return path.substr(0, end - it);
+}
+
+tstring normalizePath(tstring v) {
+    std::replace(v.begin(), v.end(), '/', '\\');
+    return tstrings::toLower(v);
+}
+
+namespace {
+
+bool createNewFile(const tstring& path) {
+    HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+    // if the file exists => h == INVALID_HANDLE_VALUE & GetLastError returns ERROR_FILE_EXISTS
+    if (h != INVALID_HANDLE_VALUE) {
+        CloseHandle(h);
+        LOG_TRACE(tstrings::any() << "Created [" << path << "] file");
+        return true;
+    }
+    return false;
+}
+
+} // namespace
+
+tstring createTempFile(const tstring &prefix, const tstring &suffix, const tstring &path)
+{
+    const tstring invalidChars = reservedFilenameChars();
+
+    if (prefix.find_first_of(invalidChars) != tstring::npos) {
+        JP_THROW(tstrings::any() << "Illegal characters in prefix=" << prefix);
+    }
+
+    if (suffix.find_first_of(invalidChars) != tstring::npos) {
+        JP_THROW(tstrings::any() << "Illegal characters in suffix=" << suffix);
+    }
+
+    int rnd = (int)GetTickCount();
+
+    // do no more than 100 attempts
+    for (int i=0; i<100; i++) {
+        const tstring filePath = mkpath() << path << (prefix + (tstrings::any() << (rnd + i)).tstr() + suffix);
+        if (createNewFile(filePath)) {
+            return filePath;
+        }
+    }
+
+    // 100 attempts failed
+    JP_THROW(tstrings::any() << "createTempFile("  << prefix << ", "
+                                                    << suffix << ", "
+                                                    << path << ") failed");
+}
+
+tstring createTempDirectory(const tstring &prefix, const tstring &suffix, const tstring &basedir) {
+    const tstring filePath = createTempFile(prefix, suffix, basedir);
+    // delete the file and create directory with the same name
+    deleteFile(filePath);
+    createDirectory(filePath);
+    return filePath;
+}
+
+tstring createUniqueFile(const tstring &prototype) {
+    if (createNewFile(prototype)) {
+        return prototype;
+    }
+
+    return createTempFile(replaceSuffix(basename(prototype)),
+                                        suffix(prototype), dirname(prototype));
+}
+
+namespace {
+
+void createDir(const tstring path, LPSECURITY_ATTRIBUTES saAttr, tstring_array* createdDirs=0) {
+    if (CreateDirectory(path.c_str(), saAttr)) {
+        LOG_TRACE(tstrings::any() << "Created [" << path << "] directory");
+        if (createdDirs) {
+            createdDirs->push_back(removeTrailingSlash(path));
+        }
+    } else {
+        const DWORD createDirectoryErr = GetLastError();
+        // if saAttr is specified, fail even if the directory exists
+        if (saAttr != NULL || !isDirectory(path)) {
+            JP_THROW(SysError(tstrings::any() << "CreateDirectory(" << path << ") failed",
+                                    CreateDirectory, createDirectoryErr));
+        }
+    }
+}
+
+}
+
+void createDirectory(const tstring &path, tstring_array* createdDirs) {
+    const tstring dirPath = removeTrailingSlash(path) + _T("\\");
+
+    tstring::size_type pos = dirPath.find_first_of(_T("\\/"));
+    while (pos != tstring::npos) {
+        const tstring subdirPath = dirPath.substr(0, pos + 1);
+        createDir(subdirPath, NULL, createdDirs);
+        pos = dirPath.find_first_of(_T("\\/"), pos + 1);
+    }
+}
+
+
+void copyFile(const tstring& fromPath, const tstring& toPath, bool failIfExists) {
+    createDirectory(dirname(toPath));
+    if (!CopyFile(fromPath.c_str(), toPath.c_str(), (failIfExists ? TRUE : FALSE))) {
+        JP_THROW(SysError(tstrings::any()
+                    << "CopyFile(" << fromPath << ", " << toPath << ", "
+                                    << failIfExists << ") failed", CopyFile));
+    }
+    LOG_TRACE(tstrings::any() << "Copied [" << fromPath << "] file to ["
+                                                            << toPath << "]");
+}
+
+
+namespace {
+
+void moveFileImpl(const tstring& fromPath, const tstring& toPath,
+                                                                DWORD flags) {
+    const bool isDir = isDirectory(fromPath);
+    if (!MoveFileEx(fromPath.c_str(), toPath.empty() ? NULL : toPath.c_str(),
+                                                                    flags)) {
+        JP_THROW(SysError(tstrings::any() << "MoveFileEx(" << fromPath
+                        << ", " << toPath << ", " << flags << ") failed",
+                                                                MoveFileEx));
+    }
+
+    const bool onReboot = 0 != (flags & MOVEFILE_DELAY_UNTIL_REBOOT);
+
+    const LPCTSTR label = isDir ? _T("folder") : _T("file");
+
+    tstrings::any msg;
+    if (!toPath.empty()) {
+        if (onReboot) {
+            msg << "Move";
+        } else {
+            msg << "Moved";
+        }
+        msg << " '" << fromPath << "' " << label << " to '" << toPath << "'";
+    } else {
+        if (onReboot) {
+            msg << "Delete";
+        } else {
+            msg << "Deleted";
+        }
+        msg << " '" << fromPath << "' " << label;
+    }
+    if (onReboot) {
+        msg << " on reboot";
+    }
+    LOG_TRACE(msg);
+}
+
+} // namespace
+
+
+void moveFile(const tstring& fromPath, const tstring& toPath, bool failIfExists)
+{
+    createDirectory(dirname(toPath));
+
+    DWORD flags = MOVEFILE_COPY_ALLOWED;
+    if (!failIfExists) {
+        flags |= MOVEFILE_REPLACE_EXISTING;
+    }
+
+    moveFileImpl(fromPath, toPath, flags);
+}
+
+void deleteFile(const tstring &path)
+{
+    if (!deleteFile(path, std::nothrow)) {
+        JP_THROW(SysError(tstrings::any()
+                        << "DeleteFile(" << path << ") failed", DeleteFile));
+    }
+}
+
+namespace {
+
+bool notFound(const DWORD status=GetLastError()) {
+    return status == ERROR_FILE_NOT_FOUND || status == ERROR_PATH_NOT_FOUND;
+}
+
+bool deleteFileImpl(const std::nothrow_t &, const tstring &path) {
+    const bool deleted = (DeleteFile(path.c_str()) != 0);
+    if (deleted) {
+        LOG_TRACE(tstrings::any() << "Deleted [" << path << "] file");
+        return true;
+    }
+    return notFound();
+}
+
+} // namespace
+
+bool deleteFile(const tstring &path, const std::nothrow_t &) throw()
+{
+    bool deleted = deleteFileImpl(std::nothrow, path);
+    const DWORD status = GetLastError();
+    if (!deleted && status == ERROR_ACCESS_DENIED) {
+        DWORD attrs = GetFileAttributes(path.c_str());
+        SetLastError(status);
+        if (attrs == INVALID_FILE_ATTRIBUTES) {
+            return false;
+        }
+        if (attrs & FILE_ATTRIBUTE_READONLY) {
+            // DeleteFile() failed because file is R/O.
+            // Remove R/O attribute and retry DeleteFile().
+            attrs &= ~FILE_ATTRIBUTE_READONLY;
+            if (SetFileAttributes(path.c_str(), attrs)) {
+                LOG_TRACE(tstrings::any() << "Discarded R/O attribute from ["
+                                                        << path << "] file");
+                deleted = deleteFileImpl(std::nothrow, path);
+            } else {
+                LOG_WARNING(SysError(tstrings::any()
+                            << "Failed to discard R/O attribute from ["
+                            << path << "] file. File will not be deleted",
+                            SetFileAttributes).what());
+                SetLastError(status);
+            }
+        }
+    }
+
+    return deleted || notFound();
+}
+
+void deleteDirectory(const tstring &path)
+{
+    if (!deleteDirectory(path, std::nothrow)) {
+        JP_THROW(SysError(tstrings::any()
+                << "RemoveDirectory(" << path << ") failed", RemoveDirectory));
+    }
+}
+
+bool deleteDirectory(const tstring &path, const std::nothrow_t &) throw()
+{
+    const bool deleted = (RemoveDirectory(path.c_str()) != 0);
+    if (deleted) {
+        LOG_TRACE(tstrings::any() << "Deleted [" << path << "] directory");
+    }
+    return deleted || notFound();
+}
+
+namespace {
+
+class DeleteFilesCallback: public DirectoryCallback {
+public:
+    explicit DeleteFilesCallback(bool ff): failfast(ff), failed(false) {
+    }
+
+    virtual bool onFile(const tstring& path) {
+        if (failfast) {
+            deleteFile(path);
+        } else {
+            updateStatus(deleteFile(path, std::nothrow));
+        }
+        return true;
+    }
+
+    bool good() const {
+        return !failed;
+    }
+
+protected:
+    void updateStatus(bool success) {
+        if (!success) {
+            failed = true;
+        }
+    }
+
+    const bool failfast;
+private:
+    bool failed;
+};
+
+class DeleteAllCallback: public DeleteFilesCallback {
+public:
+    explicit DeleteAllCallback(bool failfast): DeleteFilesCallback(failfast) {
+    }
+
+    virtual bool onDirectory(const tstring& path) {
+        if (failfast) {
+            deleteDirectoryRecursive(path);
+        } else {
+            updateStatus(deleteDirectoryRecursive(path, std::nothrow));
+        }
+        return true;
+    }
+};
+
+
+class BatchDeleter {
+    const tstring dirPath;
+    bool recursive;
+public:
+    explicit BatchDeleter(const tstring& path): dirPath(path) {
+        deleteSubdirs(false);
+    }
+
+    BatchDeleter& deleteSubdirs(bool v) {
+        recursive = v;
+        return *this;
+    }
+
+    void execute() const {
+        if (!isFileExists(dirPath)) {
+            return;
+        }
+        iterateDirectory(true /* fail fast */);
+        if (recursive) {
+            deleteDirectory(dirPath);
+        }
+    }
+
+    bool execute(const std::nothrow_t&) const {
+        if (!isFileExists(dirPath)) {
+            return true;
+        }
+
+        if (!isDirectory(dirPath)) {
+            return false;
+        }
+
+        JP_TRY;
+        if (!iterateDirectory(false /* ignore errors */)) {
+            return false;
+        }
+        if (recursive) {
+            return deleteDirectory(dirPath, std::nothrow);
+        }
+        return true;
+        JP_CATCH_ALL;
+
+        return false;
+    }
+
+private:
+    bool iterateDirectory(bool failfast) const {
+        std::unique_ptr<DeleteFilesCallback> callback;
+        if (recursive) {
+            callback = std::unique_ptr<DeleteFilesCallback>(
+                                            new DeleteAllCallback(failfast));
+        } else {
+            callback = std::unique_ptr<DeleteFilesCallback>(
+                                            new DeleteFilesCallback(failfast));
+        }
+
+        FileUtils::iterateDirectory(dirPath, *callback);
+        return callback->good();
+    }
+};
+
+} // namespace
+
+void deleteFilesInDirectory(const tstring &dirPath) {
+    BatchDeleter(dirPath).execute();
+}
+
+bool deleteFilesInDirectory(const tstring &dirPath,
+                                            const std::nothrow_t &) throw() {
+    return BatchDeleter(dirPath).execute(std::nothrow);
+}
+
+void deleteDirectoryRecursive(const tstring &dirPath) {
+    BatchDeleter(dirPath).deleteSubdirs(true).execute();
+}
+
+bool deleteDirectoryRecursive(const tstring &dirPath,
+                                            const std::nothrow_t &) throw() {
+    return BatchDeleter(dirPath).deleteSubdirs(true).execute(std::nothrow);
+}
+
+namespace {
+
+struct FindFileDeleter {
+    typedef HANDLE pointer;
+    
+    void operator()(HANDLE h) {
+        if (h && h != INVALID_HANDLE_VALUE) {
+            FindClose(h);
+        }
+    }
+};
+
+typedef std::unique_ptr<HANDLE, FindFileDeleter> UniqueFindFileHandle;
+
+}; // namesace
+void iterateDirectory(const tstring &dirPath, DirectoryCallback& callback)
+{
+    const tstring searchString = combinePath(dirPath, _T("*"));
+    WIN32_FIND_DATA findData;
+    UniqueFindFileHandle h(FindFirstFile(searchString.c_str(), &findData));
+    if (h.get() == INVALID_HANDLE_VALUE) {
+        // GetLastError() == ERROR_FILE_NOT_FOUND is OK - no files in the directory
+        // ERROR_PATH_NOT_FOUND is returned if the parent directory does not exist
+        if (GetLastError() != ERROR_FILE_NOT_FOUND) {
+            JP_THROW(SysError(tstrings::any() << "FindFirstFile(" << dirPath << ") failed", FindFirstFile));
+        }
+        return;
+    }
+
+    do {
+        const tstring fname(findData.cFileName);
+        const tstring filePath = combinePath(dirPath, fname);
+        if (!isDirectoryAttrs(findData.dwFileAttributes)) {
+            if (!callback.onFile(filePath)) {
+                return;
+            }
+        } else if (fname != _T(".") && fname != _T("..")) {
+            if (!callback.onDirectory(filePath)) {
+                return;
+            }
+        }
+    } while (FindNextFile(h.get(), &findData));
+
+    // expect GetLastError() == ERROR_NO_MORE_FILES
+    if (GetLastError() != ERROR_NO_MORE_FILES) {
+        JP_THROW(SysError(tstrings::any() << "FindNextFile(" << dirPath << ") failed", FindNextFile));
+    }
+}
+
+
+tstring replaceSuffix(const tstring& path, const tstring& newSuffix) {
+    return (path.substr(0, path.size() - suffix(path).size()) + newSuffix);
+}
+
+
+DirectoryIterator& DirectoryIterator::findItems(tstring_array& v) {
+    if (!isDirectory(root)) {
+        return *this;
+    }
+
+    iterateDirectory(root, *this);
+    v.insert(v.end(), items.begin(), items.end());
+    items = tstring_array();
+    return *this;
+}
+
+bool DirectoryIterator::onFile(const tstring& path) {
+    if (theWithFiles) {
+        items.push_back(path);
+    }
+    return true;
+}
+
+bool DirectoryIterator::onDirectory(const tstring& path) {
+    if (theWithFolders) {
+        items.push_back(path);
+    }
+    if (theRecurse) {
+        DirectoryIterator(path).recurse(theRecurse)
+                                    .withFiles(theWithFiles)
+                                    .withFolders(theWithFolders)
+                                    .findItems(items);
+    }
+    return true;
+}
+
+
+namespace {
+
+struct DeleterFunctor {
+    // Order of items in the following enum is important!
+    // It controls order in which items of particular type will be deleted.
+    // See Deleter::execute().
+    enum {
+        File,
+        FilesInDirectory,
+        RecursiveDirectory,
+        EmptyDirectory
+    };
+
+    void operator () (const Deleter::Path& path) const {
+        switch (path.second) {
+#define DELETE_SOME(o, f)\
+        case o:\
+            f(path.first, std::nothrow);\
+            break
+
+        DELETE_SOME(File, deleteFile);
+        DELETE_SOME(EmptyDirectory, deleteDirectory);
+        DELETE_SOME(FilesInDirectory, deleteFilesInDirectory);
+        DELETE_SOME(RecursiveDirectory, deleteDirectoryRecursive);
+
+#undef DELETE_SOME
+        default:
+            break;
+        }
+    }
+};
+
+} // namespace
+
+void Deleter::execute() {
+    Paths tmp;
+    tmp.swap(paths);
+
+    // Reorder items to delete.
+    std::stable_sort(tmp.begin(), tmp.end(), [] (const Paths::value_type& a,
+                                                const Paths::value_type& b) {
+        return a.second < b.second;
+    });
+
+    std::for_each(tmp.begin(), tmp.end(), DeleterFunctor());
+}
+
+Deleter& Deleter::appendFile(const tstring& path) {
+    paths.push_back(std::make_pair(path, DeleterFunctor::File));
+    return *this;
+}
+
+Deleter& Deleter::appendEmptyDirectory(const Directory& dir) {
+     tstring path =  normalizePath(removeTrailingSlash(dir));
+     const tstring parent = normalizePath(removeTrailingSlash(dir.parent));
+     while(parent != path) {
+         appendEmptyDirectory(path);
+         path = dirname(path);
+     }
+
+    return *this;
+}
+
+Deleter& Deleter::appendEmptyDirectory(const tstring& path) {
+    paths.push_back(std::make_pair(path,
+                                DeleterFunctor::EmptyDirectory));
+    return *this;
+}
+
+Deleter& Deleter::appendAllFilesInDirectory(const tstring& path) {
+    paths.push_back(std::make_pair(path,
+                                DeleterFunctor::FilesInDirectory));
+    return *this;
+}
+
+Deleter& Deleter::appendRecursiveDirectory(const tstring& path) {
+    paths.push_back(std::make_pair(path,
+                            DeleterFunctor::RecursiveDirectory));
+    return *this;
+}
+
+
+FileWriter::FileWriter(const tstring& path): dstPath(path) {
+    tmpFile = FileUtils::createTempFile(_T("jds"), _T(".tmp"),
+                                                    FileUtils::dirname(path));
+
+    cleaner.appendFile(tmpFile);
+
+    // we want to get exception on error
+    tmp.exceptions(std::ifstream::failbit | std::ifstream::badbit);
+    tmp.open(tmpFile, std::ios::binary | std::ios::trunc);
+}
+
+FileWriter& FileWriter::write(const void* buf, size_t bytes) {
+    tmp.write(static_cast<const char*>(buf), bytes);
+    return *this;
+}
+
+void FileWriter::finalize() {
+    tmp.close();
+
+    FileUtils::moveFile(tmpFile, dstPath, false);
+
+    // cancel file deletion
+    cleaner.cancel();
+}
+
+} //  namespace FileUtils
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/FileUtils.h	Mon Jun 17 15:38:04 2019 -0400
@@ -0,0 +1,378 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef FILEUTILS_H
+#define FILEUTILS_H
+
+
+#include <fstream>
+#include "SysInfo.h"
+
+
+namespace FileUtils {
+
+    // Returns 'true' if the given character is a path separator.
+    bool isDirSeparator(const tstring::value_type c);
+
+    // checks if the file or directory exists
+    bool isFileExists(const tstring &filePath);
+
+    // checks is the specified file is a directory
+    // returns false if the path does not exist
+    bool isDirectory(const tstring &filePath);
+
+    //checks if the specified directory is not empty
+    //returns true if the path is an existing directory and it contains at least one file other than "." or "..".
+    bool isDirectoryNotEmpty(const tstring &dirPath);
+
+    // returns directory part of the path.
+    // returns empty string if the path contains only filename.
+    // if the path ends with slash/backslash, returns removeTrailingSlashes(path).
+    tstring dirname(const tstring &path);
+
+    // returns basename part of the path
+    // if the path ends with slash/backslash, returns empty string.
+    tstring basename(const tstring &path);
+
+    /**
+     * Translates forward slashes to back slashes and returns lower case version
+     * of the given string.
+     */
+    tstring normalizePath(tstring v);
+
+    // Returns suffix of the path. If the given path has a suffix the first
+    // character of the return value is '.'.
+    // Otherwise return value if empty string.
+    tstring suffix(const tstring &path);
+
+    // combines two strings into a path
+    tstring combinePath(const tstring& parent, const tstring& child);
+
+    // removes trailing slashes and backslashes in the path if any
+    tstring removeTrailingSlash(const tstring& path);
+
+    // Creates a file with unique name in the specified base directory, throws an exception if operation fails
+    // path is constructed as <prefix><random number><suffix>.
+    // The function fails and throws exception if 'path' doesn't exist.
+    tstring createTempFile(const tstring &prefix = _T(""), const tstring &suffix = _T(".tmp"), const tstring &path=SysInfo::getTempDir());
+
+    // Creates a directory with unique name in the specified base directory, throws an exception if operation fails
+    // path is constructed as <prefix><random number><suffix>
+    // The function fails and throws exception if 'path' doesn't exist.
+    tstring createTempDirectory(const tstring &prefix = _T(""), const tstring &suffix = _T(".tmp"), const tstring &basedir=SysInfo::getTempDir());
+
+    // If the file referenced with "prototype" parameter DOES NOT exist, the return
+    // value is the given path. No new files created.
+    // Otherwise the function creates another file in the same directory as
+    // the given file with the same suffix and with the basename from the
+    // basename of the given file with some random chars appended to ensure
+    // created file is unique.
+    tstring createUniqueFile(const tstring &prototype);
+
+    // Creates directory and subdirectories if don't exist.
+    // Currently supports only "standard" path like "c:\bla-bla"
+    // If 'createdDirs' parameter is not NULL, the given array is appended with
+    // all subdirectories created by this function call.
+    void createDirectory(const tstring &path, tstring_array* createdDirs=0);
+
+    // copies file from fromPath to toPath. Creates output directory if doesn't exist.
+    void copyFile(const tstring& fromPath, const tstring& toPath, bool failIfExists);
+
+    // moves file from fromPath to toPath. Creates output directory if doesn't exist.
+    void moveFile(const tstring& fromPath, const tstring& toPath, bool failIfExists);
+
+    // Throws exception if fails to delete specified 'path'.
+    // Exits normally if 'path' doesn't exist or it has been deleted.
+    // Attempts to strip R/O attribute if delete fails and retry delete.
+    void deleteFile(const tstring &path);
+    // Returns 'false' if fails to delete specified 'path'.
+    // Returns 'true' if 'path' doesn't exist or it has been deleted.
+    // Attempts to strip R/O attribute if delete fails and retry delete.
+    bool deleteFile(const tstring &path, const std::nothrow_t &) throw();
+
+    // Like deleteFile(), but applies to directories.
+    void deleteDirectory(const tstring &path);
+    bool deleteDirectory(const tstring &path, const std::nothrow_t &) throw();
+
+    // Deletes all files (not subdirectories) from the specified directory.
+    // Exits normally if all files in 'dirPath' have been deleted or if
+    // 'dirPath' doesn't exist.
+    // Throws exception if 'dirPath' references existing file system object
+    // which is not a directory or when the first failure of file delete
+    // occurs.
+    void deleteFilesInDirectory(const tstring &dirPath);
+    // Deletes all files (not subdirectories) from the specified directory.
+    // Returns 'true' normally if all files in 'dirPath' have been deleted or
+    // if 'dirPath' doesn't exist.
+    // Returns 'false' if 'dirPath' references existing file system object
+    // which is not a directory or if failed to delete one ore more files in
+    // 'dirPath' directory.
+    // Doesn't abort iteration over files if the given directory after the
+    // first failure to delete a file.
+    bool deleteFilesInDirectory(const tstring &dirPath, const std::nothrow_t &) throw();
+    // Like deleteFilesInDirectory, but deletes subdirectories as well
+    void deleteDirectoryRecursive(const tstring &dirPath);
+    bool deleteDirectoryRecursive(const tstring &dirPath, const std::nothrow_t &) throw();
+
+    class DirectoryCallback {
+    public:
+        virtual ~DirectoryCallback() {};
+
+        virtual bool onFile(const tstring& path) {
+            return true;
+        }
+        virtual bool onDirectory(const tstring& path) {
+            return true;
+        }
+    };
+
+    // Calls the given callback for every file and subdirectory of
+    // the given directory.
+    void iterateDirectory(const tstring &dirPath, DirectoryCallback& callback);
+
+    /**
+     * Replace file suffix, example replaceSuffix("file/path.txt", ".csv")
+     * @param path file path to replace suffix
+     * @param suffix new suffix for path
+     * @return return file path with new suffix
+     */
+    tstring replaceSuffix(const tstring& path, const tstring& suffix=tstring());
+
+    class DirectoryIterator: DirectoryCallback {
+    public:
+        DirectoryIterator(const tstring& root=tstring()): root(root) {
+            recurse().withFiles().withFolders();
+        }
+
+        DirectoryIterator& recurse(bool v=true) {
+            theRecurse = v;
+            return *this;
+        }
+
+        DirectoryIterator& withFiles(bool v=true) {
+            theWithFiles = v;
+            return *this;
+        }
+
+        DirectoryIterator& withFolders(bool v=true) {
+            theWithFolders = v;
+            return *this;
+        }
+
+        tstring_array findItems() {
+            tstring_array reply;
+            findItems(reply);
+            return reply;
+        }
+
+        DirectoryIterator& findItems(tstring_array& v);
+
+    private:
+        virtual bool onFile(const tstring& path);
+        virtual bool onDirectory(const tstring& path);
+
+    private:
+        bool theRecurse;
+        bool theWithFiles;
+        bool theWithFolders;
+        tstring root;
+        tstring_array items;
+    };
+
+    // Returns array of all the files/sub-folders from the given directory, empty array if basedir is not a directory. The returned
+    // array is ordered from top down (i.e. dirs are listed first followed by subfolders and files).
+    // Order of subfolders and files is undefined but usually they are sorted by names.
+    inline tstring_array listAllContents(const tstring& basedir) {
+        return DirectoryIterator(basedir).findItems();
+    }
+
+    // Helper to construct path from multiple components.
+    //
+    // Sample usage:
+    //  Construct "c:\Program Files\Java" string from three components
+    //
+    //  tstring path = FileUtils::mkpath()  << _T("c:")
+    //                                      << _T("Program Files")
+    //                                      << _T("Java");
+    //
+    class mkpath {
+    public:
+        operator const tstring& () const {
+            return path;
+        }
+
+        mkpath& operator << (const tstring& p) {
+            path = combinePath(path, p);
+            return *this;
+        }
+
+        // mimic std::string
+        const tstring::value_type* c_str() const {
+            return path.c_str();
+        }
+    private:
+        tstring path;
+    };
+
+    struct Directory {
+        Directory() {
+        }
+
+        Directory(const tstring &parent, const tstring &subdir) : parent(parent), subdir(subdir)  {
+        }
+
+        operator tstring () const {
+            return getPath();
+        }
+
+        tstring getPath() const {
+            return combinePath(parent, subdir);
+        }
+
+        bool empty() const {
+            return (parent.empty() && subdir.empty());
+        }
+
+        tstring parent;
+        tstring subdir;
+    };
+
+    // Deletes list of files and directories in batch mode.
+    // Registered files and directories are deleted when destructor is called.
+    // Order or delete operations is following:
+    //  - delete items registered with appendFile() calls;
+    //  - delete items registered with appendAllFilesInDirectory() calls;
+    //  - delete items registered with appendRecursiveDirectory() calls;
+    //  - delete items registered with appendEmptyDirectory() calls.
+    class Deleter {
+    public:
+        Deleter() {
+        }
+
+        ~Deleter() {
+            execute();
+        }
+
+        typedef std::pair<tstring, int> Path;
+        typedef std::vector<Path> Paths;
+
+        /**
+         * Appends all records from the given deleter Deleter into this Deleter
+         * instance. On success array with records in the passed in Deleter
+         * instance is emptied.
+         */
+        Deleter& appendFrom(Deleter& other) {
+            Paths tmp(paths);
+            tmp.insert(tmp.end(), other.paths.begin(), other.paths.end());
+            Paths empty;
+            other.paths.swap(empty);
+            paths.swap(tmp);
+            return *this;
+        }
+
+        // Schedule file for deletion.
+        Deleter& appendFile(const tstring& path);
+
+        // Schedule files for deletion.
+        template <class It>
+        Deleter& appendFiles(It b, It e) {
+            for (It it = b; it != e; ++it) {
+                appendFile(*it);
+            }
+            return *this;
+        }
+
+        // Schedule files for deletion in the given directory.
+        template <class It>
+        Deleter& appendFiles(const tstring& dirname, It b, It e) {
+            for (It it = b; it != e; ++it) {
+                appendFile(FileUtils::mkpath() << dirname << *it);
+            }
+            return *this;
+        }
+
+        // Schedule empty directory for deletion with empty roots (up to Directory.parent).
+        Deleter& appendEmptyDirectory(const Directory& dir);
+
+        // Schedule empty directory for deletion without roots.
+        // This is a particular case of appendEmptyDirectory(const Directory& dir)
+        // with Directory(dirname(path), basename(path)).
+        Deleter& appendEmptyDirectory(const tstring& path);
+
+        // Schedule all file from the given directory for deletion.
+        Deleter& appendAllFilesInDirectory(const tstring& path);
+
+        // Schedule directory for recursive deletion.
+        Deleter& appendRecursiveDirectory(const tstring& path);
+
+        void cancel() {
+            paths.clear();
+        }
+
+        // Deletes scheduled files and directories. After this function
+        // is called internal list of scheduled items is emptied.
+        void execute();
+
+    private:
+        Paths paths;
+    };
+
+
+    /**
+     * Helper to write chunks of data into binary file.
+     * Creates temporary file in the same folder with destination file.
+     * All subsequent requests to save data chunks are redirected to temporary
+     * file. finalize() method closes temporary file stream and renames
+     * temporary file.
+     * If finalize() method is not called, temporary file is deleted in
+     * ~FileWriter(), destination file is not touched.
+     */
+    class FileWriter {
+    public:
+        explicit FileWriter(const tstring& path);
+
+        FileWriter& write(const void* buf, size_t bytes);
+
+        template <class Ctnr>
+        FileWriter& write(const Ctnr& buf) {
+            return write(buf.data(),
+                            buf.size() * sizeof(typename Ctnr::value_type));
+        }
+
+        void finalize();
+
+    private:
+        // Not accessible by design!
+        FileWriter& write(const std::wstring& str);
+
+    private:
+        tstring tmpFile;
+        Deleter cleaner;
+        std::ofstream tmp;
+        tstring dstPath;
+    };
+} // FileUtils
+
+#endif // FILEUTILS_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/Log.cpp	Mon Jun 17 15:38:04 2019 -0400
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "Log.h"
+#include "SysInfo.h"
+#include "FileUtils.h"
+
+
+namespace {
+    //
+    // IMPORTANT: Static objects with non-trivial constructors are NOT allowed
+    // in logger module. Allocate buffers only and do lazy initialization of
+    // globals in Logger::getDefault().
+    //
+    // Logging subsystem is used almost in every module, and logging API can be
+    // called from constructors of static objects in various modules. As
+    // ordering of static objects initialization between modules is undefined,
+    // this means some module may call logging api before logging static
+    // variables are initialized if any. This will result in AV. To avoid such
+    // use cases keep logging module free from static variables that require
+    // initialization with functions called by CRT.
+    //
+
+    // by default log everything
+    const Logger::LogLevel defaultLogLevel = Logger::LOG_TRACE;
+
+    char defaultLogAppenderMemory[sizeof(StderrLogAppender)] = {};
+
+    char defaultLoggerMemory[sizeof(Logger)] = {};
+
+    NopLogAppender nopLogApender;
+
+    LPCTSTR getLogLevelStr(Logger::LogLevel level) {
+        switch (level) {
+        case Logger::LOG_TRACE:
+            return _T("TRACE");
+        case Logger::LOG_INFO:
+            return _T("INFO");
+        case Logger::LOG_WARNING:
+            return _T("WARNING");
+        case Logger::LOG_ERROR:
+            return _T("ERROR");
+        }
+        return _T("UNKNOWN");
+    }
+
+    tstring retrieveModuleName() {
+        try {
+            return FileUtils::basename(SysInfo::getCurrentModulePath());
+        } catch (const std::exception&) {
+            return _T("Unknown");
+        }
+    }
+
+    TCHAR moduleName[MAX_PATH] = { 'U', 'n', 'k', 'o', 'w', 'n', TCHAR(0) };
+
+    const LPCTSTR format = _T("[%04u/%02u/%02u %02u:%02u:%02u.%03u, %s (PID: %u, TID: %u), %s:%u (%s)]\n\t%s: %s\n");
+
+    enum State { NotInitialized, Initializing, Initialized };
+    State state = NotInitialized;
+}
+
+
+LogEvent::LogEvent() {
+    memset(this, 0, sizeof(*this));
+    moduleName = tstring();
+    logLevel = tstring();
+    fileName = tstring();
+    funcName = tstring();
+    message = tstring();
+}
+
+
+StderrLogAppender::StderrLogAppender() {
+}
+
+
+/*static*/
+Logger& Logger::defaultLogger()
+{
+    Logger* reply = reinterpret_cast<Logger*>(defaultLoggerMemory);
+
+    if (!reply->appender) {
+        // Memory leak by design. Not an issue at all as this is global
+        // object. OS will do resources clean up anyways when application
+        // terminates and the default log appender should live as long as
+        // application lives.
+        reply->appender = new (defaultLogAppenderMemory) StderrLogAppender();
+    }
+
+    if (Initializing == state) {
+        // Recursive call to Logger::defaultLogger.
+        moduleName[0] = TCHAR(0);
+    } else if (NotInitialized == state) {
+        state = Initializing;
+
+        tstring mname = retrieveModuleName();
+        mname.resize(_countof(moduleName) - 1);
+        std::memcpy(moduleName, mname.c_str(), mname.size());
+        moduleName[mname.size()] = TCHAR(0);
+
+        // if JPACKAGE_DEBUG environment variable is NOT set to "true" disable 
+        // logging.
+        if (SysInfo::getEnvVariable(std::nothrow, L"JPACKAGE_DEBUG") != L"true") {
+            reply->appender = &nopLogApender;
+        }
+
+        state = Initialized;
+    }
+
+    return *reply;
+}
+
+Logger::Logger(LogAppender& appender, LogLevel logLevel)
+    : level(logLevel), appender(&appender)
+{
+}
+
+void Logger::setLogLevel(LogLevel logLevel)
+{
+    level = logLevel;
+}
+
+Logger::~Logger()
+{
+}
+
+
+bool Logger::isLoggable(LogLevel logLevel) const
+{
+    return logLevel >= level;
+}
+
+void Logger::log(LogLevel logLevel, LPCTSTR fileName, int lineNum, LPCTSTR funcName, const tstring& message) const
+{
+    LogEvent logEvent;
+
+    // [YYYY/MM/DD HH:MM:SS.ms, <module> (PID: processID, TID: threadID), fileName:lineNum (funcName)]
+    // <tab>LEVEL: message
+    GetLocalTime(&logEvent.ts);
+
+    logEvent.pid = GetCurrentProcessId();
+    logEvent.tid = GetCurrentThreadId();
+    logEvent.moduleName = moduleName;
+    logEvent.fileName = FileUtils::basename(fileName);
+    logEvent.funcName = funcName;
+    logEvent.logLevel = getLogLevelStr(logLevel);
+    logEvent.lineNum = lineNum;
+    logEvent.message = message;
+
+    appender->append(logEvent);
+}
+
+
+void StderrLogAppender::append(const LogEvent& v)
+{
+    const tstring out = tstrings::unsafe_format(format,
+        unsigned(v.ts.wYear), unsigned(v.ts.wMonth), unsigned(v.ts.wDay),                       // date
+        unsigned(v.ts.wHour), unsigned(v.ts.wMinute), unsigned(v.ts.wSecond), unsigned(v.ts.wMilliseconds), // time
+        v.moduleName.c_str(), v.pid, v.tid,
+        v.fileName.c_str(), v.lineNum, v.funcName.c_str(),
+        v.logLevel.c_str(),
+        v.message.c_str());
+
+    std::cerr << tstrings::toUtf8(out);
+}
+
+
+// Logger::ScopeTracer
+Logger::ScopeTracer::ScopeTracer(Logger &logger, LogLevel logLevel, LPCTSTR fileName, int lineNum, LPCTSTR funcName, const tstring& scopeName)
+    : log(logger), level(logLevel), file(fileName), line(lineNum), func(funcName), scope(scopeName), needLog(logger.isLoggable(logLevel))
+{
+    if (needLog) {
+        log.log(level, file.c_str(), line, func.c_str(), tstrings::any() << "Entering " << scope);
+    }
+}
+
+Logger::ScopeTracer::~ScopeTracer() {
+    if (needLog) {
+        // we don't know what line is end of scope at, so specify line 0
+        // and add note about line when the scope begins
+        log.log(level, file.c_str(), 0, func.c_str(),
+            tstrings::any() << "Exiting " << scope << " (entered at " << FileUtils::basename(file) << ":" << line << ")");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/Log.h	Mon Jun 17 15:38:04 2019 -0400
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef __LOG_H_INCLUDED_
+#define __LOG_H_INCLUDED_
+
+#include <windows.h>
+#include "tstrings.h"
+
+
+/* Default logger (Logger::defaultLogger()) writes log messages to the default log file.
+ * Common scenario:
+ *   - main() function configures default logger:
+ *       FileLogAppender appender(_T("my_log_filename.log"));
+ *       Logger::defaultLogger().setAppender(appender);
+ *       Logger::defaultLogger().setLogLevel(LOG_INFO);
+ * If the default file name and log level are not set, _T("jusched.log")/LOG_TRACE are used.
+ *
+ * Logger fileName specifies only file name,
+ * full path for the log file depends on the platform (usually value of the TMP env. var)
+ */
+
+
+struct LogEvent {
+    SYSTEMTIME ts;
+    long tid;
+    long pid;
+    tstring moduleName;
+    tstring logLevel;
+    tstring fileName;
+    int lineNum;
+    tstring funcName;
+    tstring message;
+
+    LogEvent();
+};
+
+
+class LogAppender {
+public:
+    virtual ~LogAppender() {
+    }
+    virtual void append(const LogEvent& v) = 0;
+};
+
+
+class NopLogAppender: public LogAppender {
+public:
+    virtual void append(const LogEvent& v) {};
+};
+
+
+class TeeLogAppender: public LogAppender {
+public:
+    TeeLogAppender(LogAppender* first, LogAppender* second):
+                                                first(first), second(second) {
+    }
+    virtual ~TeeLogAppender() {
+    }
+    virtual void append(const LogEvent& v) {
+        if (first) {
+            first->append(v);
+        }
+        if (second) {
+            second->append(v);
+        }
+    }
+private:
+    LogAppender* first;
+    LogAppender* second;
+};
+
+
+/**
+ * Writes log events to stderr.
+ */
+class StderrLogAppender: public LogAppender {
+public:
+    explicit StderrLogAppender();
+
+    virtual void append(const LogEvent& v);
+};
+
+
+class Logger {
+public:
+    enum LogLevel {
+        LOG_TRACE,
+        LOG_INFO,
+        LOG_WARNING,
+        LOG_ERROR
+    };
+
+    static Logger& defaultLogger();
+
+    explicit Logger(LogAppender& appender, LogLevel logLevel = LOG_TRACE);
+    ~Logger();
+
+    LogAppender& setAppender(LogAppender& v) {
+        LogAppender& oldAppender = *appender;
+        appender = &v;
+        return oldAppender;
+    }
+
+    LogAppender& getAppender() const {
+        return *appender;
+    }
+
+    void setLogLevel(LogLevel logLevel);
+
+    bool isLoggable(LogLevel logLevel) const ;
+    void log(LogLevel logLevel, LPCTSTR fileName, int lineNum, LPCTSTR funcName, const tstring& message) const;
+    void log(LogLevel logLevel, LPCTSTR fileName, int lineNum,
+                    LPCTSTR funcName, const tstrings::any& message) const {
+        return log(logLevel, fileName, lineNum, funcName, message.tstr());
+    }
+    void log(LogLevel logLevel, LPCTSTR fileName, int lineNum,
+                    LPCTSTR funcName, tstring::const_pointer message) const {
+        return log(logLevel, fileName, lineNum, funcName, tstring(message));
+    }
+
+    // internal class for scope tracing
+    class ScopeTracer {
+    public:
+        ScopeTracer(Logger &logger, LogLevel logLevel, LPCTSTR fileName, int lineNum, LPCTSTR funcName, const tstring& scopeName);
+        ~ScopeTracer();
+
+    private:
+        const Logger &log;
+        const LogLevel level;
+        const bool needLog;
+        const tstring file;
+        const int line;
+        const tstring func;
+        const tstring scope;
+    };
+
+private:
+    LogLevel level;
+    LogAppender* appender;
+};
+
+
+// base logging macro
+#define LOGGER_LOG(logger, logLevel, message) \
+    do { \
+        if (logger.isLoggable(logLevel)) { \
+            logger.log(logLevel, _T(__FILE__), __LINE__, _T(__FUNCTION__), message); \
+        } \
+    } while(false)
+
+
+// custom logger macros
+#define LOGGER_TRACE(logger, message)   LOGGER_LOG(logger, Logger::LOG_TRACE, message)
+#define LOGGER_INFO(logger, message)    LOGGER_LOG(logger, Logger::LOG_INFO, message)
+#define LOGGER_WARNING(logger, message) LOGGER_LOG(logger, Logger::LOG_WARNING, message)
+#define LOGGER_ERROR(logger, message)   LOGGER_LOG(logger, Logger::LOG_ERROR, message)
+// scope tracing macros
+#define LOGGER_TRACE_SCOPE(logger, scopeName) \
+    Logger::ScopeTracer tracer__COUNTER__(logger, Logger::LOG_TRACE, _T(__FILE__), __LINE__, _T(__FUNCTION__), scopeName)
+#define LOGGER_TRACE_FUNCTION(logger)   LOGGER_TRACE_SCOPE(logger, _T(__FUNCTION__))
+
+
+// default logger macros
+#define LOG_TRACE(message)              LOGGER_LOG(Logger::defaultLogger(), Logger::LOG_TRACE, message)
+#define LOG_INFO(message)               LOGGER_LOG(Logger::defaultLogger(), Logger::LOG_INFO, message)
+#define LOG_WARNING(message)            LOGGER_LOG(Logger::defaultLogger(), Logger::LOG_WARNING, message)
+#define LOG_ERROR(message)              LOGGER_LOG(Logger::defaultLogger(), Logger::LOG_ERROR, message)
+// scope tracing macros
+// logs (_T("Entering ") + scopeName) at the beging, (_T("Exiting ") + scopeName) at the end of scope
+#define LOG_TRACE_SCOPE(scopeName)      LOGGER_TRACE_SCOPE(Logger::defaultLogger(), scopeName)
+// logs (_T("Entering ") + functionName) at the beging, (_T("Exiting ") + __FUNCTION__) at the end of scope
+#define LOG_TRACE_FUNCTION()            LOGGER_TRACE_FUNCTION(Logger::defaultLogger())
+
+
+#endif // __LOG_H_INCLUDED_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/ResourceEditor.cpp	Mon Jun 17 15:38:04 2019 -0400
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <algorithm>
+#include <fstream>
+#include "ResourceEditor.h"
+#include "WinErrorHandling.h"
+#include "Log.h"
+
+
+ResourceEditor::FileLock::FileLock(const std::wstring& binaryPath) {
+    h = BeginUpdateResource(binaryPath.c_str(), FALSE);
+    if (NULL == h) {
+        JP_THROW(SysError(tstrings::any() << "BeginUpdateResource(" 
+                    << binaryPath << ") failed", BeginUpdateResource));
+    }
+
+    discard(false);
+}
+
+
+ResourceEditor::FileLock::~FileLock() {
+    if (!EndUpdateResource(h, theDiscard)) {
+        JP_NO_THROW(JP_THROW(SysError(tstrings::any() 
+            << "EndUpdateResource(" << h << ") failed.", EndUpdateResource)));
+    }
+}
+
+
+ResourceEditor::ResourceEditor() {
+    language(MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)).type(unsigned(0)).id(unsigned(0));
+}
+
+
+ResourceEditor& ResourceEditor::type(unsigned v) {
+    return type(MAKEINTRESOURCE(v));
+}
+
+
+ResourceEditor& ResourceEditor::type(LPCWSTR v) {
+    if (IS_INTRESOURCE(v)) {
+        std::wostringstream printer;
+        printer << L"#" << reinterpret_cast<size_t>(v);
+        theType = printer.str();
+        theTypePtr = MAKEINTRESOURCE(static_cast<DWORD>(reinterpret_cast<DWORD_PTR>(v)));
+    } else {
+        theType = v;
+        theTypePtr = theType.c_str();
+    }
+    return *this;
+}
+
+
+ResourceEditor& ResourceEditor::id(unsigned v) {
+    return id(MAKEINTRESOURCE(v));
+}
+
+
+ResourceEditor& ResourceEditor::id(LPCWSTR v) {
+    if (IS_INTRESOURCE(v)) {
+        std::wostringstream printer;
+        printer << L"#" << reinterpret_cast<size_t>(v);
+        theId = printer.str();
+    } else {
+        theId = v;
+        theIdPtr = theId.c_str();
+    }
+    return *this;
+}
+
+
+ResourceEditor& ResourceEditor::apply(const FileLock& dstBinary, 
+                            std::istream& srcStream, std::streamsize size) {
+
+    typedef std::vector<BYTE> ByteArray;
+    ByteArray buf;
+    if (size <= 0) {
+        // Read the entire stream.
+        buf = ByteArray((std::istreambuf_iterator<char>(srcStream)),
+                                            std::istreambuf_iterator<char>());
+    } else {
+        buf.resize(size_t(size));
+        srcStream.read(reinterpret_cast<char*>(buf.data()), size); 
+    }
+
+    auto reply = UpdateResource(dstBinary.get(), theTypePtr, theIdPtr, lang, 
+                                buf.data(), static_cast<DWORD>(buf.size()));
+    if (reply == FALSE) {
+        JP_THROW(SysError("UpdateResource() failed", UpdateResource));
+    }
+
+    return *this;
+}
+
+
+ResourceEditor& ResourceEditor::apply(const FileLock& dstBinary, 
+                                                const std::wstring& srcFile) {
+    std::ifstream input(srcFile, std::ios_base::binary);
+    input.exceptions(std::ios::failbit | std::ios::badbit);
+    return apply(dstBinary, input);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/ResourceEditor.h	Mon Jun 17 15:38:04 2019 -0400
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef RESOURCEEDITOR_H
+#define RESOURCEEDITOR_H
+
+#include <windows.h>
+#include <vector>
+#include <string>
+
+
+class ResourceEditor {
+public:
+    class FileLock {
+    public:
+        FileLock(const std::wstring& binaryPath);
+        ~FileLock();
+
+        HANDLE get() const {
+            return h;
+        }
+
+        void discard(bool v = true) {
+            theDiscard = v;
+        }
+
+    private:
+        FileLock(const FileLock&);
+        FileLock& operator=(const FileLock&);
+    private:
+        HANDLE h;
+        bool theDiscard;
+    };
+
+public:
+    ResourceEditor();
+
+    /**
+     * Set the language identifier of the resource to be updated.
+     */
+    ResourceEditor& language(unsigned v) {
+        lang = v;
+        return *this;
+    }
+
+    /**
+     * Set the resource type to be updated.
+     */
+    ResourceEditor& type(unsigned v);
+
+    /**
+     * Set the resource type to be updated.
+     */
+    ResourceEditor& type(LPCWSTR v);
+
+    /**
+     * Set resource ID.
+     */
+    ResourceEditor& id(unsigned v);
+
+    /**
+     * Set resource ID.
+     */
+    ResourceEditor& id(LPCWSTR v);
+
+    /**
+     * Relaces resource configured in the given binary with the given data stream.
+     */
+    ResourceEditor& apply(const FileLock& dstBinary, std::istream& srcStream, std::streamsize size=0);
+
+    /**
+     * Relaces resource configured in the given binary with contents of 
+     * the given binary file.
+     */
+    ResourceEditor& apply(const FileLock& dstBinary, const std::wstring& srcFile);
+
+private:
+    unsigned lang;
+    std::wstring theId;
+    LPCWSTR theIdPtr;
+    std::wstring theType;
+    LPCWSTR theTypePtr;
+};
+
+#endif // #ifndef RESOURCEEDITOR_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/SourceCodePos.h	Mon Jun 17 15:38:04 2019 -0400
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+#ifndef SourceCodePos_h
+#define SourceCodePos_h
+
+
+//
+// Position in source code.
+//
+
+struct SourceCodePos
+{
+    SourceCodePos(const char* fl, const char* fnc, int l):
+                                                file(fl), func(fnc), lno(l)
+    {
+    }
+
+    const char* file;
+    const char* func;
+    int lno;
+};
+
+
+// Initializes SourceCodePos instance with the
+// information from the point of calling.
+#define JP_SOURCE_CODE_POS SourceCodePos(__FILE__, __FUNCTION__, __LINE__)
+
+
+#endif // #ifndef SourceCodePos_h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/SysInfo.h	Mon Jun 17 15:38:04 2019 -0400
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+#ifndef SYSINFO_H
+#define SYSINFO_H
+
+#include "tstrings.h"
+
+
+//
+// This namespace provides information about environment in which
+// the current application runs.
+// It is for general purpose use.
+// Functions in this namespaces are just queries about the environment.
+// Functions that change the existing environment like file or directory
+// creation should not be added to this namespace.
+//
+namespace SysInfo {
+    /**
+     * Returns temp dir (for the current user).
+     */
+    tstring getTempDir();
+
+    /**
+     * Returns absolute path to the process executable.
+     */
+    tstring getProcessModulePath();
+
+    /**
+     * Returns absolute path to the current executable module.
+     */ 
+    tstring getCurrentModulePath();
+
+    /**
+     * Returns value of environment variable with the given name.
+     * Throws exception if variable is not set or any other error occurred
+     * reading the value.
+     */
+    tstring getEnvVariable(const tstring& name);
+
+    /**
+     * Returns value of environment variable with the given name.
+     * Returns value of 'defValue' parameter if variable is not set or any
+     * other error occurred reading the value.
+     */
+    tstring getEnvVariable(const std::nothrow_t&, const tstring& name,
+                                            const tstring& defValue=tstring());
+
+    /**
+     * Returns 'true' if environment variable with the given name is set.
+     */
+    bool isEnvVariableSet(const tstring& name);
+}
+
+#endif // SYSINFO_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/UniqueHandle.h	Mon Jun 17 15:38:04 2019 -0400
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef UNIQUEHANDLE_H
+#define UNIQUEHANDLE_H
+
+#include <windows.h>
+#include <memory>
+
+
+struct WndHandleDeleter {
+    typedef HANDLE pointer;
+    
+    void operator()(HANDLE h) {
+        ::CloseHandle(h);
+    }
+};
+
+typedef std::unique_ptr<HANDLE, WndHandleDeleter> UniqueHandle;
+
+#endif // #ifndef UNIQUEHANDLE_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/WinErrorHandling.cpp	Mon Jun 17 15:38:04 2019 -0400
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "WinErrorHandling.h"
+#include "Log.h"
+#include "SysInfo.h"
+#include "FileUtils.h"
+
+
+namespace {
+
+std::string makeMessage(const std::string& msg, const char* label,
+                                            const void* c, DWORD errorCode) {
+    std::ostringstream err;
+    err << (label ? label : "Some error") << " [" << errorCode << "]";
+
+    HMODULE hmodule = NULL;
+    if (c) {
+        GetModuleHandleEx(
+            GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+            reinterpret_cast<LPCTSTR>(c),
+            &hmodule);
+
+        if (!hmodule) {
+            LOG_WARNING(tstrings::any() << "GetModuleHandleEx() failed for " << c << " address.");
+        }
+    }
+    if (hmodule || !c) {
+        err << "(" << SysError::getSysErrorMessage(errorCode, hmodule) << ")";
+    }
+
+    return joinErrorMessages(msg, err.str());
+}
+
+
+std::wstring getSystemMessageDescription(DWORD messageId, HMODULE moduleHandle) {
+    LPWSTR pMsg = NULL;
+    std::wstring descr;
+
+    // we always retrieve UNICODE description from system,
+    // convert it to utf8 if UNICODE is not defined
+
+    while (true) {
+        DWORD res = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER
+                                        | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS
+                                        | (moduleHandle != NULL ? FORMAT_MESSAGE_FROM_HMODULE : 0),
+                                    moduleHandle, messageId, 0, (LPWSTR)&pMsg, 0, NULL);
+        if (res > 0) {
+            // replace all non-printed chars with space
+            for (DWORD i=0; i<res; i++) {
+                if (pMsg[i] < L' ') {
+                    pMsg[i] = L' ';
+                }
+            }
+            // trim right (spaces and dots)
+            for (DWORD i=res; i>0; i--) {
+                if (pMsg[i] > L' ' && pMsg[i] != L'.') {
+                    break;
+                }
+                pMsg[i] = 0;
+            }
+
+            descr = pMsg;
+
+            LocalFree(pMsg);
+        } else {
+            // if we fail to get description for specific moduleHandle,
+            // try to get "common" description.
+            if (moduleHandle != NULL) {
+                moduleHandle = NULL;
+                continue;
+            }
+            descr = L"No description available";
+        }
+        break;
+    }
+
+    return descr;
+}
+
+} // namespace
+
+
+SysError::SysError(const tstrings::any& msg, const void* caller, DWORD ec,
+                                                            const char* label):
+        std::runtime_error(makeMessage(msg.str(), label, caller, ec)) {
+}
+
+std::wstring SysError::getSysErrorMessage(DWORD errCode, HMODULE moduleHandle) {
+    tstrings::any msg;
+    msg << "system error " << errCode
+        << " (" << getSystemMessageDescription(errCode, moduleHandle) << ")";
+    return msg.tstr();
+}
+
+std::wstring SysError::getComErrorMessage(HRESULT hr) {
+    HRESULT hrOrig = hr;
+    // for FACILITY_WIN32 facility we need to reset hiword
+    if(HRESULT_FACILITY(hr) == FACILITY_WIN32) {
+        hr = HRESULT_CODE(hr);
+    }
+    return tstrings::format(_T("COM error 0x%08X (%s)"), hrOrig, getSystemMessageDescription(hr, NULL));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/WinErrorHandling.h	Mon Jun 17 15:38:04 2019 -0400
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+#ifndef WinErrorHandling_h
+#define WinErrorHandling_h
+
+
+#include "ErrorHandling.h"
+
+
+class SysError : public std::runtime_error {
+public:
+    SysError(const tstrings::any& msg, const void* caller,
+            DWORD errorCode=GetLastError(), const char* label="System error");
+
+    // returns string "system error <errCode> (error_description)"
+    // in UNICODE is not defined, the string returned is utf8-encoded
+    static std::wstring getSysErrorMessage(DWORD errCode = GetLastError(), HMODULE moduleHandle = NULL);
+    
+    // returns string "COM error 0x<hr> (error_description)"
+    // in UNICODE is not defined, the string returned is utf8-encoded
+    static std::wstring getComErrorMessage(HRESULT hr);
+};
+
+#endif // #ifndef WinErrorHandling_h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/WinSysInfo.cpp	Mon Jun 17 15:38:04 2019 -0400
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "WinSysInfo.h"
+#include "FileUtils.h"
+#include "WinErrorHandling.h"
+
+namespace SysInfo {
+
+tstring getTempDir() {
+    std::vector<TCHAR> buffer(MAX_PATH);
+    DWORD res = GetTempPath(static_cast<DWORD>(buffer.size()), buffer.data());
+    if (res > buffer.size()) {
+        buffer.resize(res);
+        GetTempPath(static_cast<DWORD>(buffer.size()), buffer.data());
+    }
+    return FileUtils::removeTrailingSlash(buffer.data());
+}
+
+namespace {
+
+template <class Func>
+tstring getSystemDirImpl(Func func, const std::string& label) {
+    std::vector<TCHAR> buffer(MAX_PATH);
+    for (int i=0; i<2; i++) {
+        DWORD res = func(buffer.data(), static_cast<DWORD>(buffer.size()));
+        if (!res) {
+            JP_THROW(SysError(label + " failed", func));
+        }
+        if (res < buffer.size()) {
+            return FileUtils::removeTrailingSlash(buffer.data());
+        }
+        buffer.resize(res + 1);
+    }
+    JP_THROW("Unexpected reply from" + label);
+}
+
+} // namespace
+
+tstring getSystem32Dir() {
+    return getSystemDirImpl(GetSystemDirectory, "GetSystemDirectory");
+}
+
+tstring getWIPath() {
+    return FileUtils::mkpath() << getSystem32Dir() << _T("msiexec.exe");
+}
+
+namespace {
+
+tstring getModulePath(HMODULE h)
+{
+    std::vector<TCHAR> buf(MAX_PATH);
+    DWORD len = 0;
+    while (true) {
+        len = GetModuleFileName(h, buf.data(), (DWORD)buf.size());
+        if (len < buf.size()) {
+            break;
+        }
+        // buffer is too small, increase it
+        buf.resize(buf.size() * 2);
+    }
+
+    if (len == 0) {
+        // error occured
+        JP_THROW(SysError("GetModuleFileName failed", GetModuleFileName));
+    }
+    return tstring(buf.begin(), buf.begin() + len);
+}
+
+} // namespace
+
+tstring getProcessModulePath() {
+    return getModulePath(NULL);
+}
+
+HMODULE getCurrentModuleHandle()
+{
+    // get module handle for the address of this function
+    LPCWSTR address = reinterpret_cast<LPCWSTR>(getCurrentModuleHandle);
+    HMODULE hmodule = NULL;
+    if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+                            address, &hmodule))
+    {
+        JP_THROW(SysError(tstrings::any() << "GetModuleHandleExW failed", GetModuleHandleExW));
+    }
+    return hmodule;
+}
+
+tstring getCurrentModulePath()
+{
+    return getModulePath(getCurrentModuleHandle());
+}
+
+namespace {
+
+tstring getEnvVariableImpl(const tstring& name, bool* errorOccured=0) {
+    std::vector<TCHAR> buf(10);
+    SetLastError(ERROR_SUCCESS);
+    const DWORD size = GetEnvironmentVariable(name.c_str(), buf.data(),
+                                                            DWORD(buf.size()));
+    if (GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
+        if (errorOccured) {
+            *errorOccured = true;
+            return tstring();
+        }
+        JP_THROW(SysError(tstrings::any() << "GetEnvironmentVariable("
+            << name << ") failed. Variable not set", GetEnvironmentVariable));
+    }
+
+    if (size > buf.size()) {
+        buf.resize(size);
+        GetEnvironmentVariable(name.c_str(), buf.data(), DWORD(buf.size()));
+        if (GetLastError() != ERROR_SUCCESS) {
+            if (errorOccured) {
+                *errorOccured = true;
+                return tstring();
+            }
+            JP_THROW(SysError(tstrings::any() << "GetEnvironmentVariable("
+                            << name << ") failed", GetEnvironmentVariable));
+        }
+    }
+
+    if (errorOccured) {
+        *errorOccured = false;
+    }
+    return tstring(buf.data());
+}
+
+} // namespace
+
+tstring getEnvVariable(const tstring& name) {
+    return getEnvVariableImpl(name);
+}
+
+tstring getEnvVariable(const std::nothrow_t&, const tstring& name,
+                                                    const tstring& defValue) {
+    bool errorOccured = false;
+    const tstring reply = getEnvVariableImpl(name, &errorOccured);
+    if (errorOccured) {
+        return defValue;
+    }
+    return reply;
+}
+
+bool isEnvVariableSet(const tstring& name) {
+    TCHAR unused[1];
+    SetLastError(ERROR_SUCCESS);
+    GetEnvironmentVariable(name.c_str(), unused, _countof(unused));
+    return GetLastError() != ERROR_ENVVAR_NOT_FOUND;
+}
+
+} // end of namespace SysInfo
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/WinSysInfo.h	Mon Jun 17 15:38:04 2019 -0400
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+#ifndef WINSYSINFO_H
+#define WINSYSINFO_H
+
+#include "SysInfo.h"
+
+
+//
+// Windows specific SysInfo.
+//
+namespace SysInfo {
+    // gets Windows System folder. A typical path is C:\Windows\System32.
+    tstring getSystem32Dir();
+
+    // returns full path to msiexec.exe executable
+    tstring getWIPath();
+
+    // Returns handle of the current module (exe or dll).
+    // The function assumes this code is statically linked to the module.
+    HMODULE getCurrentModuleHandle();
+}
+
+
+#endif // WINSYSINFO_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/tstrings.cpp	Mon Jun 17 15:38:04 2019 -0400
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdexcept>
+#include <algorithm>
+
+#include "tstrings.h"
+#include "ErrorHandling.h"
+
+
+namespace tstrings {
+
+/* Create formatted string
+ */
+tstring unsafe_format(tstring::const_pointer format, ...) {
+    if (!format) {
+        throw std::invalid_argument("Destination buffer can't be NULL");
+    }
+
+    tstring fmtout;
+    int ret;
+    const int inc = 256;
+
+    va_list args;
+    va_start(args, format);
+    do {
+        fmtout.resize(fmtout.size() + inc);
+#ifdef _MSC_VER
+        ret = _vsntprintf_s(&*fmtout.begin(), fmtout.size(), _TRUNCATE, format, args);
+#else
+        // With g++ this compiles only with '-std=gnu++0x' option
+        ret = vsnprintf(&*fmtout.begin(), fmtout.size(), format, args);
+#endif
+    } while(-1 == ret);
+    va_end(args);
+
+    //update string size by actual value
+    fmtout.resize(ret);
+
+    return fmtout;
+}
+
+/*
+ * Tests if two strings are equal according to CompareType.
+ *
+ * a - string to compare
+ * b - string to compare
+ * ct - CASE_SENSITIVE: case sensitive comparing type
+ *      IGNORE_CASE: case insensitive comparing type
+ */
+bool equals(const tstring& a, const tstring& b, const CompareType ct) {
+    if (IGNORE_CASE==ct) {
+        return toLower(a) == toLower(b);
+    }
+    return a == b;
+}
+
+bool startsWith(const tstring &str, const tstring &substr, const CompareType ct)
+{
+    if (str.size() < substr.size()) {
+        return false;
+    }
+    const tstring startOfStr = str.substr(0, substr.size());
+    return tstrings::equals(startOfStr, substr, ct);
+}
+
+bool endsWith(const tstring &str, const tstring &substr, const CompareType ct)
+{
+    if (str.size() < substr.size()) {
+        return false;
+    }
+    const tstring endOfStr = str.substr(str.size() - substr.size());
+    return tstrings::equals(endOfStr, substr, ct);
+}
+
+/*
+ * Split string into a vector with given delimiter string
+ *
+ * strVector - string vector to store split tstring
+ * str - string to split
+ * delimiter - delimiter to split the string around
+ * st - ST_ALL: return value includes an empty string
+ *      ST_EXCEPT_EMPTY_STRING: return value does not include an empty string
+ *
+ * Note: It does not support multiple delimiters
+ */
+void split(tstring_array &strVector, const tstring &str,
+          const tstring &delimiter, const SplitType st) {
+    tstring::size_type start = 0, end = 0, length = str.length();
+
+    if (length == 0 || delimiter.length() == 0) {
+        return;
+    }
+
+    end = str.find(delimiter, start);
+    while(end != tstring::npos) {
+        if(st == ST_ALL || end - start > 1 ) {
+            strVector.push_back(str.substr(start, end == tstring::npos ?
+                                                  tstring::npos : end - start));
+        }
+        start = end > (tstring::npos - delimiter.size()) ?
+                tstring::npos : end + delimiter.size();
+        end = str.find(delimiter, start);
+    }
+
+    if(st == ST_ALL || start < length) {
+        strVector.push_back(str.substr(start, length - start));
+    }
+}
+
+/*
+ * Convert uppercase letters to lowercase
+ */
+tstring toLower(const tstring& str) {
+    tstring lower(str);
+    tstring::iterator ok = std::transform(lower.begin(), lower.end(),
+                                          lower.begin(), tolower);
+    if (ok!=lower.end()) {
+        lower.resize(0);
+    }
+    return lower;
+}
+
+
+/*
+ * Replace all substring occurrences in a tstring.
+ * If 'str' or 'search' is empty the function returns 'str'.
+ * The given 'str' remains unchanged in any case.
+ * The function returns changed copy of 'str'.
+ */
+tstring replace(const tstring &str, const tstring &search, const tstring &replace)
+{
+    if (search.empty()) {
+        return str;
+    }
+
+    tstring s(str);
+
+    for (size_t pos = 0; ; pos += replace.length()) {
+        pos = s.find(search, pos);
+        if (pos == tstring::npos) {
+            break;
+        }
+        s.erase(pos, search.length());
+        s.insert(pos, replace);
+    }
+    return s;
+}
+
+
+/*
+ * Remove trailing spaces
+ */
+
+tstring trim(const tstring& str, const tstring& whitespace) {
+    const size_t strBegin = str.find_first_not_of(whitespace);
+    if (strBegin == std::string::npos) {
+        return tstring(); // no content
+    }
+
+    const size_t  strEnd = str.find_last_not_of(whitespace);
+    const size_t strRange = strEnd - strBegin + 1;
+
+    return str.substr(strBegin, strRange);
+}
+
+} // namespace tstrings
+
+
+#ifdef TSTRINGS_WITH_WCHAR
+namespace tstrings {
+
+namespace {
+/*
+ * Converts UTF16-encoded string into multi-byte string of the given encoding.
+ */
+std::string toMultiByte(const std::wstring& utf16str, int encoding) {
+    std::string reply;
+    do {
+        int cm = WideCharToMultiByte(encoding,
+                                    0,
+                                    utf16str.c_str(),
+                                    int(utf16str.size()),
+                                    NULL,
+                                    0,
+                                    NULL,
+                                    NULL);
+        if (cm < 0) {
+            JP_THROW("Unexpected reply from WideCharToMultiByte()");
+        }
+        if (0 == cm) {
+            break;
+        }
+
+        reply.resize(cm);
+        int cm2 = WideCharToMultiByte(encoding,
+                                    0,
+                                    utf16str.c_str(),
+                                    int(utf16str.size()),
+                                    &*reply.begin(),
+                                    cm,
+                                    NULL,
+                                    NULL);
+        if (cm != cm2) {
+            JP_THROW("Unexpected reply from WideCharToMultiByte()");
+        }
+    } while(0);
+
+    return reply;
+}
+
+/*
+ * Converts multi-byte string of the given encoding into UTF16-encoded string.
+ */
+std::wstring fromMultiByte(const std::string& str, int encoding) {
+    std::wstring utf16;
+    do {
+        int cw = MultiByteToWideChar(encoding,
+                                    MB_ERR_INVALID_CHARS,
+                                    str.c_str(),
+                                    int(str.size()),
+                                    NULL,
+                                    0);
+        if (cw < 0) {
+            JP_THROW("Unexpected reply from MultiByteToWideChar()");
+        }
+        if (0 == cw) {
+            break;
+        }
+
+        utf16.resize(cw);
+        int cw2 = MultiByteToWideChar(encoding,
+                                    MB_ERR_INVALID_CHARS,
+                                    str.c_str(),
+                                    int(str.size()),
+                                    &*utf16.begin(),
+                                    cw);
+        if (cw != cw2) {
+            JP_THROW("Unexpected reply from MultiByteToWideChar()");
+        }
+    } while(0);
+
+    return utf16;
+}
+} // namespace
+
+std::string toUtf8(const std::wstring& utf16str) {
+    return toMultiByte(utf16str, CP_UTF8);
+}
+
+std::wstring toUtf16(const std::string& utf8str) {
+    return fromMultiByte(utf8str, CP_UTF8);
+}
+
+} // namespace tstrings
+#endif // ifdef TSTRINGS_WITH_WCHAR
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/tstrings.h	Mon Jun 17 15:38:04 2019 -0400
@@ -0,0 +1,415 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef TSTRINGS_H
+#define TSTRINGS_H
+
+#ifdef _MSC_VER
+#   define TSTRINGS_WITH_WCHAR
+#endif
+
+#ifdef TSTRINGS_WITH_WCHAR
+#include <windows.h>
+#include <tchar.h>
+// Want compiler issue C4995 warnings for encounters of deprecated functions.
+#include <strsafe.h>
+#endif
+
+// STL's string header depends on deprecated functions.
+// We don't care about warnings from STL header, so disable them locally.
+#ifdef _MSC_VER
+#   pragma warning(push)
+#   pragma warning(disable:4995)
+#endif
+
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <vector>
+
+#ifdef _MSC_VER
+#   pragma warning(pop)
+#endif
+
+
+#ifndef _T
+#   define _T(x) x
+#endif
+
+
+#ifdef TSTRINGS_WITH_WCHAR
+typedef std::wstring        tstring;
+typedef std::wostringstream tostringstream;
+typedef std::wistringstream tistringstream;
+typedef std::wstringstream  tstringstream;
+typedef std::wistream       tistream;
+typedef std::wostream       tostream;
+typedef std::wiostream      tiostream;
+typedef std::wios           tios;
+#else
+typedef std::string         tstring;
+typedef std::ostringstream  tostringstream;
+typedef std::istringstream  tistringstream;
+typedef std::stringstream   tstringstream;
+typedef std::istream        tistream;
+typedef std::ostream        tostream;
+typedef std::iostream       tiostream;
+typedef std::ios            tios;
+
+typedef const char* LPCTSTR;
+typedef char TCHAR;
+#endif
+
+// frequently used "array of tstrings" type
+typedef std::vector<tstring> tstring_array;
+
+namespace tstrings {
+    tstring unsafe_format(tstring::const_pointer format, ...);
+
+    enum CompareType {CASE_SENSITIVE, IGNORE_CASE};
+    bool equals(const tstring& a, const tstring& b, const CompareType ct=CASE_SENSITIVE);
+    bool startsWith(const tstring &str, const tstring &substr, const CompareType ct=CASE_SENSITIVE);
+    bool endsWith(const tstring &str, const tstring &substr, const CompareType ct=CASE_SENSITIVE);
+
+    enum SplitType {ST_ALL, ST_EXCEPT_EMPTY_STRING};
+    void split(tstring_array &strVector, const tstring &str,
+              const tstring &delimiter, const SplitType st = ST_ALL);
+    inline tstring_array split(const tstring &str, const tstring &delimiter, const SplitType st = ST_ALL) {
+        tstring_array result;
+        split(result, str, delimiter, st);
+        return result;
+    }
+    tstring trim(const tstring& str, const tstring& whitespace = _T(" \t"));
+
+    /**
+     * Writes sequence of values from [b, e) range into string buffer inserting
+     * 'delimiter' after each value except of the last one.
+     * Returns contents of string buffer.
+     */
+    template <class It>
+    tstring join(It b, It e, const tstring& delimiter=tstring()) {
+        tostringstream buf;
+        if (b != e) {
+            for (;;) {
+                buf << *b;
+                if (++b == e) {
+                    break;
+                }
+                buf << delimiter;
+            }
+        }
+        return buf.str();
+    }
+
+    tstring toLower(const tstring& str);
+
+    tstring replace(const tstring &str, const tstring &search, const tstring &replace);
+}
+
+
+namespace tstrings {
+    inline std::string toUtf8(const std::string& utf8str) { return utf8str; }
+    
+#ifdef TSTRINGS_WITH_WCHAR
+    // conversion to Utf8
+    std::string toUtf8(const std::wstring& utf16str);
+
+    // conversion to Utf16
+    std::wstring toUtf16(const std::string& utf8str);
+
+    inline std::wstring fromUtf8(const std::string& utf8str) { return toUtf16(utf8str); }
+
+#else
+    inline std::string fromUtf8(const std::string& utf8str) { return utf8str; }
+#endif
+} // namespace tstrings
+
+
+namespace tstrings {
+namespace format_detail {
+
+    template <class T>
+    struct str_arg_value {
+        const tstring value;
+
+        str_arg_value(const std::string& v): value(fromUtf8(v)) {
+        }
+
+#ifdef TSTRINGS_WITH_WCHAR
+        str_arg_value(const std::wstring& v): value(v) {
+        }
+#endif
+
+        tstring::const_pointer operator () () const {
+            return value.c_str();
+        }
+    };
+
+    template <>
+    struct str_arg_value<tstring> {
+        const tstring::const_pointer value;
+
+        str_arg_value(const tstring& v): value(v.c_str()) {
+        }
+
+        str_arg_value(tstring::const_pointer v): value(v) {
+        }
+
+        tstring::const_pointer operator () () const {
+            return value;
+        }
+    };
+
+    inline str_arg_value<std::string> arg(const std::string& v) {
+        return v;
+    }
+
+    inline str_arg_value<std::string> arg(std::string::const_pointer v) {
+        return (v ? v : "(null)");
+    }
+
+#ifdef TSTRINGS_WITH_WCHAR
+    inline str_arg_value<std::wstring> arg(const std::wstring& v) {
+        return v;
+    }
+
+    inline str_arg_value<std::wstring> arg(std::wstring::const_pointer v) {
+        return (v ? v : L"(null)");
+    }
+#else
+    void arg(const std::wstring&);          // Compilation error by design.
+    void arg(std::wstring::const_pointer);  // Compilation error by design.
+#endif
+
+    template <class T>
+    struct arg_value {
+        arg_value(const T v): v(v) {
+        }
+        T operator () () const {
+            return v;
+        }
+    private:
+        const T v;
+    };
+
+    inline arg_value<int> arg(int v) {
+        return v;
+    }
+    inline arg_value<unsigned> arg(unsigned v) {
+        return v;
+    }
+    inline arg_value<long> arg(long v) {
+        return v;
+    }
+    inline arg_value<unsigned long> arg(unsigned long v) {
+        return v;
+    }
+    inline arg_value<long long> arg(long long v) {
+        return v;
+    }
+    inline arg_value<unsigned long long> arg(unsigned long long v) {
+        return v;
+    }
+    inline arg_value<float> arg(float v) {
+        return v;
+    }
+    inline arg_value<double> arg(double v) {
+        return v;
+    }
+    inline arg_value<bool> arg(bool v) {
+        return v;
+    }
+    inline arg_value<const void*> arg(const void* v) {
+        return v;
+    }
+
+} // namespace format_detail
+} // namespace tstrings
+
+
+namespace tstrings {
+    template <class T, class T2, class T3, class T4, class T5, class T6, class T7>
+    inline tstring format(const tstring& fmt, const T& v, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7) {
+        return unsafe_format(fmt.c_str(),   format_detail::arg(v)(),
+                                            format_detail::arg(v2)(),
+                                            format_detail::arg(v3)(),
+                                            format_detail::arg(v4)(),
+                                            format_detail::arg(v5)(),
+                                            format_detail::arg(v6)(),
+                                            format_detail::arg(v7)());
+    }
+
+    template <class T, class T2, class T3, class T4, class T5, class T6>
+    inline tstring format(const tstring& fmt, const T& v, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6) {
+        return unsafe_format(fmt.c_str(),   format_detail::arg(v)(),
+                                            format_detail::arg(v2)(),
+                                            format_detail::arg(v3)(),
+                                            format_detail::arg(v4)(),
+                                            format_detail::arg(v5)(),
+                                            format_detail::arg(v6)());
+    }
+
+    template <class T, class T2, class T3, class T4, class T5>
+    inline tstring format(const tstring& fmt, const T& v, const T2& v2, const T3& v3, const T4& v4, const T5& v5) {
+        return unsafe_format(fmt.c_str(),   format_detail::arg(v)(),
+                                            format_detail::arg(v2)(),
+                                            format_detail::arg(v3)(),
+                                            format_detail::arg(v4)(),
+                                            format_detail::arg(v5)());
+    }
+
+    template <class T, class T2, class T3, class T4>
+    inline tstring format(const tstring& fmt, const T& v, const T2& v2, const T3& v3, const T4& v4) {
+        return unsafe_format(fmt.c_str(),   format_detail::arg(v)(),
+                                            format_detail::arg(v2)(),
+                                            format_detail::arg(v3)(),
+                                            format_detail::arg(v4)());
+    }
+
+    template <class T, class T2, class T3>
+    inline tstring format(const tstring& fmt, const T& v, const T2& v2, const T3& v3) {
+        return unsafe_format(fmt.c_str(),   format_detail::arg(v)(),
+                                            format_detail::arg(v2)(),
+                                            format_detail::arg(v3)());
+    }
+
+    template <class T, class T2>
+    inline tstring format(const tstring& fmt, const T& v, const T2& v2) {
+        return unsafe_format(fmt.c_str(),   format_detail::arg(v)(),
+                                            format_detail::arg(v2)());
+
+    }
+
+    template <class T>
+    inline tstring format(const tstring& fmt, const T& v) {
+        return unsafe_format(fmt.c_str(),   format_detail::arg(v)());
+    }
+} // namespace tstrings
+
+
+namespace tstrings {
+    /**
+     * Buffer that accepts both std::wstring and std::string instances doing
+     * encoding conversions behind the scenes. All std::string-s assumed to be
+     * UTF8-encoded, all std::wstring-s assumed to be UTF16-encoded.
+     */
+    class any {
+    public:
+        any() {
+        }
+
+        any(std::string::const_pointer msg) {
+            data << fromUtf8(msg);
+        }
+
+        any(const std::string& msg) {
+            data << fromUtf8(msg);
+        }
+
+#ifdef TSTRINGS_WITH_WCHAR
+        any(std::wstring::const_pointer msg) {
+            data << msg;
+        }
+
+        any(const std::wstring& msg) {
+            data << msg;
+        }
+
+        any& operator << (const std::wstring& v) {
+            data << v;
+            return *this;
+        }
+
+        // need this specialization instead std::wstring::pointer,
+        // otherwise LPWSTR is handled as abstract pointer (void*)
+        any& operator << (LPWSTR v) {
+            data << (v ? v : L"NULL");
+            return *this;
+        }
+
+        // need this specialization instead std::wstring::const_pointer,
+        // otherwise LPCWSTR is handled as abstract pointer (const void*)
+        any& operator << (LPCWSTR v) {
+            data << (v ? v : L"NULL");
+            return *this;
+        }
+
+        std::wstring wstr() const {
+            return data.str();
+        }
+#endif
+
+        template <class T>
+        any& operator << (T v) {
+            data << v;
+            return *this;
+        }
+
+        any& operator << (tostream& (*pf)(tostream&)) {
+            data << pf;
+            return *this;
+        }
+
+        any& operator << (tios& (*pf)(tios&)) {
+            data << pf;
+            return *this;
+        }
+
+        any& operator << (std::ios_base& (*pf)(std::ios_base&)) {
+            data << pf;
+            return *this;
+        }
+
+        std::string str() const {
+            return toUtf8(data.str());
+        }
+
+        tstring tstr() const {
+            return data.str();
+        }
+
+    private:
+        tostringstream data;
+    };
+
+    inline tstring to_tstring(const any& val) {
+        return val.tstr();
+    }
+} // namespace tstrings
+
+
+inline std::ostream& operator << (std::ostream& os, const tstrings::any& buf) {
+    os << buf.str();
+    return os;
+}
+
+#ifdef TSTRINGS_WITH_WCHAR
+inline std::wostream& operator << (std::wostream& os, const tstrings::any& buf) {
+    os << buf.wstr();
+    return os;
+}
+#endif
+
+#endif //TSTRINGS_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/msiwrapper/Executor.cpp	Mon Jun 17 15:38:04 2019 -0400
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <algorithm>
+#include "Executor.h"
+#include "Log.h"
+#include "WinErrorHandling.h"
+
+
+namespace {
+
+void escapeArg(std::wstring& str) {
+    if (str.empty()) {
+        return;
+    }
+
+    if (str.front() == L'\"' && str.back() == L'\"' && str.size() > 1) {
+        return;
+    }
+
+    if (str.find_first_of(L" \t") != std::wstring::npos) {
+        str = L'"' + str + L'"';
+    }
+}
+
+} // namespace
+
+
+std::wstring Executor::args() const {
+    tstring_array tmpArgs;
+    // argv[0] is the module name.
+    tmpArgs.push_back(appPath);
+    tmpArgs.insert(tmpArgs.end(), argsArray.begin(), argsArray.end());
+
+    std::for_each(tmpArgs.begin(), tmpArgs.end(), escapeArg);
+    return tstrings::join(tmpArgs.begin(), tmpArgs.end(), _T(" "));
+}
+
+
+int Executor::execAndWaitForExit() const {
+    UniqueHandle h = startProcess();
+    
+    const DWORD res = ::WaitForSingleObject(h.get(), INFINITE);
+    if (WAIT_FAILED ==  res) {
+        JP_THROW(SysError("WaitForSingleObject() failed", WaitForSingleObject));
+    }
+
+    DWORD exitCode = 0;
+    if (!GetExitCodeProcess(h.get(), &exitCode)) {
+        // Error reading process's exit code.
+        JP_THROW(SysError("GetExitCodeProcess() failed", GetExitCodeProcess));
+    }
+
+    const DWORD processId = GetProcessId(h.get());
+    if (!processId) {
+        JP_THROW(SysError("GetProcessId() failed.", GetProcessId));
+    }
+
+    LOG_TRACE(tstrings::any() << "Process with PID=" << processId
+                                << " terminated. Exit code=" << exitCode);
+    
+    return static_cast<int>(exitCode);
+}
+
+
+UniqueHandle Executor::startProcess() const {
+    const std::wstring argsStr = args();
+
+    std::vector<TCHAR> argsBuffer(argsStr.begin(), argsStr.end());
+    argsBuffer.push_back(0); // terminating '\0'
+
+    STARTUPINFO startupInfo;
+    ZeroMemory(&startupInfo, sizeof(startupInfo));
+    startupInfo.cb = sizeof(startupInfo);
+
+    PROCESS_INFORMATION processInfo;
+    ZeroMemory(&processInfo, sizeof(processInfo));
+
+    DWORD creationFlags = 0;
+
+    if (!theVisible) {
+        // For GUI applications.
+        startupInfo.dwFlags |= STARTF_USESHOWWINDOW;
+        startupInfo.wShowWindow = SW_HIDE;
+
+        // For console applications.
+        creationFlags |= CREATE_NO_WINDOW;
+    }
+
+    tstrings::any msg;
+    msg << "CreateProcess(" << appPath << ", " << argsStr << ")";
+
+    if (!CreateProcess(appPath.c_str(), argsBuffer.data(), NULL, NULL, FALSE,
+                    creationFlags, NULL, NULL, &startupInfo, &processInfo)) {
+        msg << " failed";
+        JP_THROW(SysError(msg, CreateProcess));
+    }
+
+    msg << " succeeded; PID=" << processInfo.dwProcessId;
+    LOG_TRACE(msg);
+
+    // Close unneeded handles immediately.
+    UniqueHandle(processInfo.hThread);
+
+    // Return process handle.
+    return UniqueHandle(processInfo.hProcess);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/msiwrapper/Executor.h	Mon Jun 17 15:38:04 2019 -0400
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef EXECUTOR_H
+#define EXECUTOR_H
+
+#include "tstrings.h"
+#include "UniqueHandle.h"
+
+
+class Executor {
+public:
+    explicit Executor(const std::wstring& appPath=std::wstring()) {
+        app(appPath).visible(false);
+    }
+
+    /**
+     * Returns command line configured with arg() calls so far.
+     */
+    std::wstring args() const;
+
+    /**
+     * Set path to application to execute.
+     */
+    Executor& app(const std::wstring& v) {
+        appPath = v;
+        return *this;
+    }
+
+    /**
+     * Adds another command line argument.
+     */
+    Executor& arg(const std::wstring& v) {
+        argsArray.push_back(v);
+        return *this;
+    }
+
+    /**
+     * Controls if application window should be visible.
+     */
+    Executor& visible(bool v) {
+        theVisible = v;
+        return *this;
+    }
+
+    /**
+     * Starts application process and blocks waiting when the started
+     * process terminates.
+     * Returns process exit code.
+     * Throws exception if process start failed.
+     */
+    int execAndWaitForExit() const;
+
+private:
+    UniqueHandle startProcess() const;
+
+    bool theVisible;
+    tstring_array argsArray;
+    std::wstring appPath;
+};
+
+#endif // #ifndef EXECUTOR_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/msiwrapper/MsiWrapper.cpp	Mon Jun 17 15:38:04 2019 -0400
@@ -0,0 +1,37 @@
+#include <windows.h>
+
+#include "WinSysInfo.h"
+#include "FileUtils.h"
+#include "Executor.h"
+#include "Resources.h"
+#include "WinErrorHandling.h"
+
+
+int wmain(int argc, wchar_t *argv[])
+{
+    JP_TRY;
+
+    // Create temporary directory where to extract msi file.
+    const auto tempMsiDir = FileUtils::createTempDirectory();
+
+    // Schedule temporary directory for deletion.
+    FileUtils::Deleter cleaner;
+    cleaner.appendRecursiveDirectory(tempMsiDir);
+
+    const auto msiPath = FileUtils::mkpath() << tempMsiDir << L"main.msi";
+
+    // Extract msi file.
+    Resource(L"msi", RT_RCDATA).saveToFile(msiPath);
+
+    // Setup executor to run msiexec
+    Executor msiExecutor(SysInfo::getWIPath());
+    msiExecutor.arg(L"/i").arg(msiPath);
+    for (int i = 1; i < argc; ++i) {
+        msiExecutor.arg(argv[i]);
+    }
+
+    // Install msi file.
+    return msiExecutor.execAndWaitForExit();
+
+    JP_CATCH_ALL;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/msiwrapper/Resources.cpp	Mon Jun 17 15:38:04 2019 -0400
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "Resources.h"
+#include "FileUtils.h"
+#include "WinErrorHandling.h"
+
+#include <fstream>
+
+
+Resource::Resource(LPCTSTR name, LPCTSTR type, HINSTANCE module) {
+    init(name, type, module);
+}
+
+Resource::Resource(UINT id, LPCTSTR type, HINSTANCE module) {
+    init(MAKEINTRESOURCE(id), type, module);
+}
+
+void Resource::init(LPCTSTR name, LPCTSTR type, HINSTANCE module) {
+    if (IS_INTRESOURCE(name)) {
+        std::wostringstream printer;
+        printer << L"#" << reinterpret_cast<size_t>(name);
+        nameStr = printer.str();
+        namePtr = name;
+    } else {
+        nameStr = name;
+        namePtr = nameStr.c_str();
+    }
+    if (IS_INTRESOURCE(type)) {
+        std::wostringstream printer;
+        printer << L"#" << reinterpret_cast<size_t>(name);
+        typeStr = printer.str();
+        typePtr = type;
+    } else {
+        typeStr = type;
+        typePtr = typeStr.c_str();
+    }
+    instance = module;
+}
+
+std::string Resource::getErrMsg(const std::string &descr) const {
+    return (tstrings::any() << descr << " (name='" << nameStr << "', type='" << typeStr << "')").str();
+}
+
+HRSRC Resource::findResource() const {
+    LPCTSTR id = namePtr;
+    // string resources are stored in blocks (stringtables)
+    // id of the resource is (stringId / 16 + 1)
+    if (typePtr == RT_STRING) {
+        id = MAKEINTRESOURCE(UINT(size_t(id) / 16 + 1));
+    }
+    return FindResource(instance, id, typePtr);
+}
+
+LPVOID Resource::getPtr(DWORD &size) const
+{
+    // LoadString returns the same result if value is zero-length or if if the value does not exists,
+    // so wee need to ensure the stringtable exists
+    HRSRC resInfo = findResource();
+    if (resInfo == NULL) {
+        JP_THROW(SysError(getErrMsg("cannot find resource"), FindResource));
+    }
+
+    HGLOBAL res = LoadResource(instance, resInfo);
+    if (res == NULL) {
+        JP_THROW(SysError(getErrMsg("cannot load resource"), LoadResource));
+    }
+
+    LPVOID ptr = LockResource(res);
+    if (res == NULL) {
+        JP_THROW(SysError(getErrMsg("cannot lock resource"), LockResource));
+    }
+
+    if (typePtr == RT_STRING) {
+        // string resources are stored in stringtables and need special handling
+        // The simplest way (while we don't need handle resource locale) is LoadString
+        // But this adds dependency on user32.dll, so implement custom string extraction
+
+        // number in the block (namePtr is an integer)
+        size_t num = size_t(namePtr) & 0xf;
+        LPWSTR strPtr = (LPWSTR)ptr;
+        for (size_t i = 0; i < num; i++) {
+            // 1st symbol contains string length
+            strPtr += DWORD(*strPtr) + 1;
+        }
+        // *strPtr contains string length, string value starts at strPtr+1
+        size = DWORD(*strPtr) * sizeof(wchar_t);
+        ptr = strPtr+1;
+    } else {
+        size = SizeofResource(instance, resInfo);
+    }
+
+    return ptr;
+}
+
+bool Resource::available() const {
+    return NULL != findResource();
+}
+
+unsigned Resource::size() const {
+    DWORD size = 0;
+    getPtr(size);
+    return size;
+}
+
+LPCVOID Resource::rawData() const {
+    DWORD size = 0;
+    return getPtr(size);
+}
+
+void Resource::saveToFile(const std::wstring &filePath) const {
+    DWORD size = 0;
+    const char *resPtr = (const char *)getPtr(size);
+
+    FileUtils::FileWriter(filePath).write(resPtr, size).finalize();
+}
+
+Resource::ByteArray Resource::binary() const {
+    DWORD size = 0;
+    LPBYTE resPtr = (LPBYTE)getPtr(size);
+    return ByteArray(resPtr, resPtr+size);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/msiwrapper/Resources.h	Mon Jun 17 15:38:04 2019 -0400
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef RESOURCES_H
+#define RESOURCES_H
+
+#include "WinSysInfo.h"
+
+
+/**
+ * Classes for resource loading.
+ * Common use cases:
+ *  - check if resource is available and save it to file:
+ *      Resource res(_T("MyResource"), _T("CustomResourceType"));
+ *      if (res.available()) {
+ *          res.saveToFile(_T("c:\\temp\\my_resource.bin"));
+ *      }
+ */
+
+class Resource {
+public:
+    // name and type can be specified by string id, by integer id (RT_* constants or MAKEINTRESOURCE)
+    Resource(LPCWSTR name, LPCWSTR type, HINSTANCE module = SysInfo::getCurrentModuleHandle());
+    Resource(UINT id, LPCWSTR type, HINSTANCE module = SysInfo::getCurrentModuleHandle());
+
+    bool available() const;
+
+    // all this methods throw exception if the resource is not available
+    unsigned size() const;
+    // gets raw pointer to the resource data
+    LPCVOID rawData() const;
+
+    // save the resource to a file
+    void saveToFile(const std::wstring &filePath) const;
+
+    typedef std::vector<BYTE> ByteArray;
+    // returns the resource as byte array
+    ByteArray binary() const;
+
+private:
+    std::wstring nameStr;
+    LPCWSTR namePtr;    // can be integer (MAKEINTRESOURCE) value or point to nameStr.c_str()
+    std::wstring typeStr;
+    LPCWSTR typePtr;    // can be integer (MAKEINTRESOURCE) value or point to nameStr.c_str()
+    HINSTANCE instance;
+
+    void init(LPCWSTR name, LPCWSTR type, HINSTANCE module);
+
+    // generates error message
+    std::string getErrMsg(const std::string &descr) const;
+    HRSRC findResource() const;
+    LPVOID getPtr(DWORD &size) const;
+
+private:
+    // disable copying
+    Resource(const Resource&);
+    Resource& operator = (const Resource&);
+};
+
+#endif // RESOURCES_H