# HG changeset patch # User herrick # Date 1560800284 14400 # Node ID 45c74e6547940161f04926824adcff8a89b8b19a # Parent 02034583f4dce85b79adbdb29805a22996522a54 8221333: Replace Inno Setup with custom MSI wrapper for .exe bundler (missed files) Submitted-by: asemenyuk Reviewed-by: herrick, almatvee diff -r 02034583f4dc -r 45c74e654794 src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExeBundler.java.html --- /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 @@ + + + + + + + + + + + + +New src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExeBundler.java + +
+   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<WinAppBundler> APP_BUNDLER
+  44             = new WindowsBundlerParam<>(
+  45                     "win.app.bundler",
+  46                     WinAppBundler.class,
+  47                     params -> new WinAppBundler(),
+  48                     null);
+  49 
+  50     public static final BundlerParamInfo<File> EXE_IMAGE_DIR
+  51             = new WindowsBundlerParam<>(
+  52                     "win.exe.imageDir",
+  53                     File.class,
+  54                     params -> {
+  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) -> 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<BundlerParamInfo<?>> getBundleParameters() {
+  87         return new WinMsiBundler().getBundleParameters();
+  88     }
+  89 
+  90     @Override
+  91     public File execute(Map<String, ? super Object> 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<String, ? super Object> params)
+ 103             throws UnsupportedPlatformException, ConfigException {
+ 104         return new WinMsiBundler().validate(params);
+ 105     }
+ 106 
+ 107     public File bundle(Map<String, ? super Object> 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 }
+
diff -r 02034583f4dc -r 45c74e654794 src/jdk.jpackage/windows/native/libjpackage/ErrorHandling.cpp --- /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 + +#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; +} diff -r 02034583f4dc -r 45c74e654794 src/jdk.jpackage/windows/native/libjpackage/ErrorHandling.h --- /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 + +#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 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((Base*)0)) }; + + std::string msg; +}; + +template +inline JpError makeException(const T& obj, const SourceCodePos& p) { + return JpError(obj, p); +} + +inline JpError makeException( + const std::string& msg, const SourceCodePos& p) { + return JpError(std::runtime_error(msg), p); +} + +inline JpError makeException( + const tstrings::any& msg, const SourceCodePos& p) { + return makeException(msg.str(), p); +} + +inline JpError 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 diff -r 02034583f4dc -r 45c74e654794 src/jdk.jpackage/windows/native/libjpackage/FileUtils.cpp --- /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 +#include +#include + +#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 callback; + if (recursive) { + callback = std::unique_ptr( + new DeleteAllCallback(failfast)); + } else { + callback = std::unique_ptr( + 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 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(buf), bytes); + return *this; +} + +void FileWriter::finalize() { + tmp.close(); + + FileUtils::moveFile(tmpFile, dstPath, false); + + // cancel file deletion + cleaner.cancel(); +} + +} // namespace FileUtils diff -r 02034583f4dc -r 45c74e654794 src/jdk.jpackage/windows/native/libjpackage/FileUtils.h --- /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 +#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 . + // 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 + // 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 Path; + typedef std::vector 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 + 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 + 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 + 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 diff -r 02034583f4dc -r 45c74e654794 src/jdk.jpackage/windows/native/libjpackage/Log.cpp --- /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(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, (PID: processID, TID: threadID), fileName:lineNum (funcName)] + // 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 << ")"); + } +} diff -r 02034583f4dc -r 45c74e654794 src/jdk.jpackage/windows/native/libjpackage/Log.h --- /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 +#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_ diff -r 02034583f4dc -r 45c74e654794 src/jdk.jpackage/windows/native/libjpackage/ResourceEditor.cpp --- /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 +#include +#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(v); + theType = printer.str(); + theTypePtr = MAKEINTRESOURCE(static_cast(reinterpret_cast(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(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 ByteArray; + ByteArray buf; + if (size <= 0) { + // Read the entire stream. + buf = ByteArray((std::istreambuf_iterator(srcStream)), + std::istreambuf_iterator()); + } else { + buf.resize(size_t(size)); + srcStream.read(reinterpret_cast(buf.data()), size); + } + + auto reply = UpdateResource(dstBinary.get(), theTypePtr, theIdPtr, lang, + buf.data(), static_cast(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); +} diff -r 02034583f4dc -r 45c74e654794 src/jdk.jpackage/windows/native/libjpackage/ResourceEditor.h --- /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 +#include +#include + + +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 diff -r 02034583f4dc -r 45c74e654794 src/jdk.jpackage/windows/native/libjpackage/SourceCodePos.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 diff -r 02034583f4dc -r 45c74e654794 src/jdk.jpackage/windows/native/libjpackage/SysInfo.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 diff -r 02034583f4dc -r 45c74e654794 src/jdk.jpackage/windows/native/libjpackage/UniqueHandle.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 +#include + + +struct WndHandleDeleter { + typedef HANDLE pointer; + + void operator()(HANDLE h) { + ::CloseHandle(h); + } +}; + +typedef std::unique_ptr UniqueHandle; + +#endif // #ifndef UNIQUEHANDLE_H diff -r 02034583f4dc -r 45c74e654794 src/jdk.jpackage/windows/native/libjpackage/WinErrorHandling.cpp --- /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(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; i0; 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)); +} diff -r 02034583f4dc -r 45c74e654794 src/jdk.jpackage/windows/native/libjpackage/WinErrorHandling.h --- /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 (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
(error_description)" + // in UNICODE is not defined, the string returned is utf8-encoded + static std::wstring getComErrorMessage(HRESULT hr); +}; + +#endif // #ifndef WinErrorHandling_h diff -r 02034583f4dc -r 45c74e654794 src/jdk.jpackage/windows/native/libjpackage/WinSysInfo.cpp --- /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 buffer(MAX_PATH); + DWORD res = GetTempPath(static_cast(buffer.size()), buffer.data()); + if (res > buffer.size()) { + buffer.resize(res); + GetTempPath(static_cast(buffer.size()), buffer.data()); + } + return FileUtils::removeTrailingSlash(buffer.data()); +} + +namespace { + +template +tstring getSystemDirImpl(Func func, const std::string& label) { + std::vector buffer(MAX_PATH); + for (int i=0; i<2; i++) { + DWORD res = func(buffer.data(), static_cast(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 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(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 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 diff -r 02034583f4dc -r 45c74e654794 src/jdk.jpackage/windows/native/libjpackage/WinSysInfo.h --- /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 diff -r 02034583f4dc -r 45c74e654794 src/jdk.jpackage/windows/native/libjpackage/tstrings.cpp --- /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 +#include +#include +#include + +#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 diff -r 02034583f4dc -r 45c74e654794 src/jdk.jpackage/windows/native/libjpackage/tstrings.h --- /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 +#include +// Want compiler issue C4995 warnings for encounters of deprecated functions. +#include +#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 +#include +#include +#include + +#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_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 + 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 + 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 { + 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 arg(const std::string& v) { + return v; + } + + inline str_arg_value arg(std::string::const_pointer v) { + return (v ? v : "(null)"); + } + +#ifdef TSTRINGS_WITH_WCHAR + inline str_arg_value arg(const std::wstring& v) { + return v; + } + + inline str_arg_value 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 + struct arg_value { + arg_value(const T v): v(v) { + } + T operator () () const { + return v; + } + private: + const T v; + }; + + inline arg_value arg(int v) { + return v; + } + inline arg_value arg(unsigned v) { + return v; + } + inline arg_value arg(long v) { + return v; + } + inline arg_value arg(unsigned long v) { + return v; + } + inline arg_value arg(long long v) { + return v; + } + inline arg_value arg(unsigned long long v) { + return v; + } + inline arg_value arg(float v) { + return v; + } + inline arg_value arg(double v) { + return v; + } + inline arg_value arg(bool v) { + return v; + } + inline arg_value arg(const void* v) { + return v; + } + +} // namespace format_detail +} // namespace tstrings + + +namespace tstrings { + template + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 diff -r 02034583f4dc -r 45c74e654794 src/jdk.jpackage/windows/native/msiwrapper/Executor.cpp --- /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 +#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(exitCode); +} + + +UniqueHandle Executor::startProcess() const { + const std::wstring argsStr = args(); + + std::vector 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); +} diff -r 02034583f4dc -r 45c74e654794 src/jdk.jpackage/windows/native/msiwrapper/Executor.h --- /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 diff -r 02034583f4dc -r 45c74e654794 src/jdk.jpackage/windows/native/msiwrapper/MsiWrapper.cpp --- /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 + +#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; +} diff -r 02034583f4dc -r 45c74e654794 src/jdk.jpackage/windows/native/msiwrapper/Resources.cpp --- /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 + + +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(name); + nameStr = printer.str(); + namePtr = name; + } else { + nameStr = name; + namePtr = nameStr.c_str(); + } + if (IS_INTRESOURCE(type)) { + std::wostringstream printer; + printer << L"#" << reinterpret_cast(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); +} diff -r 02034583f4dc -r 45c74e654794 src/jdk.jpackage/windows/native/msiwrapper/Resources.h --- /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 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