8255899: Allow uninstallation of jpackage exe bundles
Reviewed-by: almatvee, herrick
This commit is contained in:
parent
65756abf44
commit
d53ee6219b
@ -30,6 +30,7 @@
|
||||
#include "UnixSysInfo.h"
|
||||
#include "Package.h"
|
||||
#include "Log.h"
|
||||
#include "app.h"
|
||||
#include "ErrorHandling.h"
|
||||
|
||||
|
||||
@ -124,5 +125,5 @@ void launchApp() {
|
||||
int main(int argc, char *argv[]) {
|
||||
SysInfo::argc = argc;
|
||||
SysInfo::argv = argv;
|
||||
return AppLauncher::launch(std::nothrow, launchApp);
|
||||
return app::launch(std::nothrow, launchApp);
|
||||
}
|
||||
|
@ -24,6 +24,7 @@
|
||||
*/
|
||||
|
||||
#include "AppLauncher.h"
|
||||
#include "app.h"
|
||||
#include "FileUtils.h"
|
||||
#include "UnixSysInfo.h"
|
||||
#include "JvmLauncher.h"
|
||||
@ -74,10 +75,10 @@ int main(int argc, char *argv[]) {
|
||||
// Besides we should ignore main() arguments because these are the
|
||||
// arguments passed into JLI_Launch() call and not the arguments with
|
||||
// which the launcher was started.
|
||||
return AppLauncher::launch(std::nothrow, launchJvm);
|
||||
return app::launch(std::nothrow, launchJvm);
|
||||
}
|
||||
|
||||
SysInfo::argc = argc;
|
||||
SysInfo::argv = argv;
|
||||
return AppLauncher::launch(std::nothrow, initJvmLauncher);
|
||||
return app::launch(std::nothrow, initJvmLauncher);
|
||||
}
|
||||
|
@ -136,158 +136,3 @@ Jvm* AppLauncher::createJvmLauncher() const {
|
||||
void AppLauncher::launch() const {
|
||||
std::unique_ptr<Jvm>(createJvmLauncher())->launch();
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
const std::string* theLastErrorMsg = 0;
|
||||
|
||||
NopLogAppender nopLogAppender;
|
||||
|
||||
class StandardLogAppender : public LogAppender {
|
||||
public:
|
||||
virtual void append(const LogEvent& v) {
|
||||
std::cerr << "[" << v.logLevel << "] "
|
||||
<< v.fileName
|
||||
<< ":" << v.lineNum
|
||||
<< ": " << v.message
|
||||
<< std::endl;
|
||||
}
|
||||
} standardLogAppender;
|
||||
|
||||
class LastErrorLogAppender : public LogAppender {
|
||||
public:
|
||||
virtual void append(const LogEvent& v) {
|
||||
std::cerr << AppLauncher::lastErrorMsg() << std::endl;
|
||||
}
|
||||
} lastErrorLogAppender;
|
||||
} // namespace
|
||||
|
||||
LogAppender& AppLauncher::defaultLastErrorLogAppender() {
|
||||
return lastErrorLogAppender;
|
||||
}
|
||||
|
||||
|
||||
std::string AppLauncher::lastErrorMsg() {
|
||||
if (theLastErrorMsg) {
|
||||
return *theLastErrorMsg;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
bool AppLauncher::isWithLogging() {
|
||||
// If JPACKAGE_DEBUG environment variable is set to "true"
|
||||
// logging is enabled.
|
||||
return SysInfo::getEnvVariable(
|
||||
std::nothrow, _T("JPACKAGE_DEBUG")) == _T("true");
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
class ResetLastErrorMsgAtEndOfScope {
|
||||
public:
|
||||
~ResetLastErrorMsgAtEndOfScope() {
|
||||
JP_NO_THROW(theLastErrorMsg = 0);
|
||||
}
|
||||
};
|
||||
|
||||
class SetLoggerAtEndOfScope {
|
||||
public:
|
||||
SetLoggerAtEndOfScope(
|
||||
std::unique_ptr<WithExtraLogAppender>& withLogAppender,
|
||||
LogAppender* lastErrorLogAppender):
|
||||
withLogAppender(withLogAppender),
|
||||
lastErrorLogAppender(lastErrorLogAppender) {
|
||||
}
|
||||
|
||||
~SetLoggerAtEndOfScope() {
|
||||
JP_TRY;
|
||||
std::unique_ptr<WithExtraLogAppender> other(
|
||||
new WithExtraLogAppender(*lastErrorLogAppender));
|
||||
withLogAppender.swap(other);
|
||||
JP_CATCH_ALL;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<WithExtraLogAppender>& withLogAppender;
|
||||
LogAppender* lastErrorLogAppender;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
int AppLauncher::launch(const std::nothrow_t&,
|
||||
LauncherFunc func, LogAppender* lastErrorLogAppender) {
|
||||
if (isWithLogging()) {
|
||||
Logger::defaultLogger().setAppender(standardLogAppender);
|
||||
} else {
|
||||
Logger::defaultLogger().setAppender(nopLogAppender);
|
||||
}
|
||||
|
||||
LOG_TRACE_FUNCTION();
|
||||
|
||||
if (!lastErrorLogAppender) {
|
||||
lastErrorLogAppender = &defaultLastErrorLogAppender();
|
||||
}
|
||||
std::unique_ptr<WithExtraLogAppender> withLogAppender;
|
||||
std::string errorMsg;
|
||||
const ResetLastErrorMsgAtEndOfScope resetLastErrorMsg;
|
||||
|
||||
JP_TRY;
|
||||
|
||||
// This will temporary change log appenders of the default logger
|
||||
// to save log messages in the default and additional log appenders.
|
||||
// Log appenders config of the default logger will be restored to
|
||||
// the original state at function exit automatically.
|
||||
const SetLoggerAtEndOfScope setLogger(withLogAppender, lastErrorLogAppender);
|
||||
func();
|
||||
return 0;
|
||||
|
||||
// The point of all these redefines is to save the last raw error message in
|
||||
// 'AppLauncher::theLastErrorMsg' variable.
|
||||
// By default error messages are saved in exception instances with the details
|
||||
// of error origin (source file, function name, line number).
|
||||
// We don't want these details in user error messages. However we still want to
|
||||
// save full information about the last error in the default log appender.
|
||||
#undef JP_HANDLE_ERROR
|
||||
#undef JP_HANDLE_UNKNOWN_ERROR
|
||||
#undef JP_CATCH_EXCEPTIONS
|
||||
#define JP_HANDLE_ERROR(e) \
|
||||
do { \
|
||||
errorMsg = (tstrings::any() << e.what()).str(); \
|
||||
theLastErrorMsg = &errorMsg; \
|
||||
reportError(JP_SOURCE_CODE_POS, e); \
|
||||
} while(0)
|
||||
#define JP_HANDLE_UNKNOWN_ERROR \
|
||||
do { \
|
||||
errorMsg = "Unknown error"; \
|
||||
theLastErrorMsg = &errorMsg; \
|
||||
reportUnknownError(JP_SOURCE_CODE_POS); \
|
||||
} while(0)
|
||||
#define JP_CATCH_EXCEPTIONS \
|
||||
catch (const JpErrorBase& e) { \
|
||||
errorMsg = (tstrings::any() << e.rawMessage()).str(); \
|
||||
theLastErrorMsg = &errorMsg; \
|
||||
try { \
|
||||
throw; \
|
||||
} catch (const std::runtime_error& e) { \
|
||||
reportError(JP_SOURCE_CODE_POS, e); \
|
||||
} \
|
||||
} catch (const std::runtime_error& e) { \
|
||||
errorMsg = lastCRTError(); \
|
||||
theLastErrorMsg = &errorMsg; \
|
||||
reportError(JP_SOURCE_CODE_POS, e); \
|
||||
} \
|
||||
JP_CATCH_UNKNOWN_EXCEPTION
|
||||
|
||||
JP_CATCH_ALL;
|
||||
|
||||
#undef JP_HANDLE_ERROR
|
||||
#undef JP_HANDLE_UNKNOWN_ERROR
|
||||
#undef JP_CATCH_EXCEPTIONS
|
||||
#define JP_HANDLE_ERROR(e) JP_REPORT_ERROR(e)
|
||||
#define JP_HANDLE_UNKNOWN_ERROR JP_REPORT_UNKNOWN_ERROR
|
||||
#define JP_CATCH_EXCEPTIONS JP_DEFAULT_CATCH_EXCEPTIONS
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -30,7 +30,6 @@
|
||||
#include "tstrings.h"
|
||||
|
||||
class Jvm;
|
||||
class LogAppender;
|
||||
|
||||
class AppLauncher {
|
||||
public:
|
||||
@ -65,17 +64,6 @@ public:
|
||||
|
||||
void launch() const;
|
||||
|
||||
static LogAppender& defaultLastErrorLogAppender();
|
||||
|
||||
static bool isWithLogging();
|
||||
|
||||
typedef void (*LauncherFunc) ();
|
||||
|
||||
static int launch(const std::nothrow_t&, LauncherFunc func,
|
||||
LogAppender* lastErrorLogAppender = 0);
|
||||
|
||||
static std::string lastErrorMsg();
|
||||
|
||||
private:
|
||||
tstring_array args;
|
||||
tstring launcherPath;
|
||||
|
186
src/jdk.jpackage/share/native/common/app.cpp
Normal file
186
src/jdk.jpackage/share/native/common/app.cpp
Normal file
@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include "app.h"
|
||||
#include "Log.h"
|
||||
#include "SysInfo.h"
|
||||
#include "ErrorHandling.h"
|
||||
|
||||
|
||||
namespace {
|
||||
const std::string* theLastErrorMsg = 0;
|
||||
|
||||
NopLogAppender nopLogAppender;
|
||||
|
||||
class StandardLogAppender : public LogAppender {
|
||||
public:
|
||||
virtual void append(const LogEvent& v) {
|
||||
std::cerr << "[" << v.logLevel << "] "
|
||||
<< v.fileName
|
||||
<< ":" << v.lineNum
|
||||
<< ": " << v.message
|
||||
<< std::endl;
|
||||
}
|
||||
} standardLogAppender;
|
||||
|
||||
class LastErrorLogAppender : public LogAppender {
|
||||
public:
|
||||
virtual void append(const LogEvent& v) {
|
||||
std::cerr << app::lastErrorMsg() << std::endl;
|
||||
}
|
||||
} lastErrorLogAppender;
|
||||
|
||||
|
||||
class ResetLastErrorMsgAtEndOfScope {
|
||||
public:
|
||||
~ResetLastErrorMsgAtEndOfScope() {
|
||||
JP_NO_THROW(theLastErrorMsg = 0);
|
||||
}
|
||||
};
|
||||
|
||||
class SetLoggerAtEndOfScope {
|
||||
public:
|
||||
SetLoggerAtEndOfScope(
|
||||
std::unique_ptr<WithExtraLogAppender>& withLogAppender,
|
||||
LogAppender* lastErrorLogAppender):
|
||||
withLogAppender(withLogAppender),
|
||||
lastErrorLogAppender(lastErrorLogAppender) {
|
||||
}
|
||||
|
||||
~SetLoggerAtEndOfScope() {
|
||||
JP_TRY;
|
||||
std::unique_ptr<WithExtraLogAppender> other(
|
||||
new WithExtraLogAppender(*lastErrorLogAppender));
|
||||
withLogAppender.swap(other);
|
||||
JP_CATCH_ALL;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<WithExtraLogAppender>& withLogAppender;
|
||||
LogAppender* lastErrorLogAppender;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
namespace app {
|
||||
LogAppender& defaultLastErrorLogAppender() {
|
||||
return lastErrorLogAppender;
|
||||
}
|
||||
|
||||
|
||||
std::string lastErrorMsg() {
|
||||
if (theLastErrorMsg) {
|
||||
return *theLastErrorMsg;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
bool isWithLogging() {
|
||||
// If JPACKAGE_DEBUG environment variable is set to "true"
|
||||
// logging is enabled.
|
||||
return SysInfo::getEnvVariable(
|
||||
std::nothrow, _T("JPACKAGE_DEBUG")) == _T("true");
|
||||
}
|
||||
|
||||
|
||||
int launch(const std::nothrow_t&,
|
||||
LauncherFunc func, LogAppender* lastErrorLogAppender) {
|
||||
if (isWithLogging()) {
|
||||
Logger::defaultLogger().setAppender(standardLogAppender);
|
||||
} else {
|
||||
Logger::defaultLogger().setAppender(nopLogAppender);
|
||||
}
|
||||
|
||||
LOG_TRACE_FUNCTION();
|
||||
|
||||
if (!lastErrorLogAppender) {
|
||||
lastErrorLogAppender = &defaultLastErrorLogAppender();
|
||||
}
|
||||
std::unique_ptr<WithExtraLogAppender> withLogAppender;
|
||||
std::string errorMsg;
|
||||
const ResetLastErrorMsgAtEndOfScope resetLastErrorMsg;
|
||||
|
||||
JP_TRY;
|
||||
|
||||
// This will temporary change log appenders of the default logger
|
||||
// to save log messages in the default and additional log appenders.
|
||||
// Log appenders config of the default logger will be restored to
|
||||
// the original state at function exit automatically.
|
||||
const SetLoggerAtEndOfScope setLogger(withLogAppender, lastErrorLogAppender);
|
||||
func();
|
||||
return 0;
|
||||
|
||||
// The point of all these redefines is to save the last raw error message in
|
||||
// 'theLastErrorMsg' variable.
|
||||
// By default error messages are saved in exception instances with the details
|
||||
// of error origin (source file, function name, line number).
|
||||
// We don't want these details in user error messages. However we still want to
|
||||
// save full information about the last error in the default log appender.
|
||||
#undef JP_HANDLE_ERROR
|
||||
#undef JP_HANDLE_UNKNOWN_ERROR
|
||||
#undef JP_CATCH_EXCEPTIONS
|
||||
#define JP_HANDLE_ERROR(e) \
|
||||
do { \
|
||||
errorMsg = (tstrings::any() << e.what()).str(); \
|
||||
theLastErrorMsg = &errorMsg; \
|
||||
reportError(JP_SOURCE_CODE_POS, e); \
|
||||
} while(0)
|
||||
#define JP_HANDLE_UNKNOWN_ERROR \
|
||||
do { \
|
||||
errorMsg = "Unknown error"; \
|
||||
theLastErrorMsg = &errorMsg; \
|
||||
reportUnknownError(JP_SOURCE_CODE_POS); \
|
||||
} while(0)
|
||||
#define JP_CATCH_EXCEPTIONS \
|
||||
catch (const JpErrorBase& e) { \
|
||||
errorMsg = (tstrings::any() << e.rawMessage()).str(); \
|
||||
theLastErrorMsg = &errorMsg; \
|
||||
try { \
|
||||
throw; \
|
||||
} catch (const std::runtime_error& e) { \
|
||||
reportError(JP_SOURCE_CODE_POS, e); \
|
||||
} \
|
||||
} catch (const std::runtime_error& e) { \
|
||||
errorMsg = lastCRTError(); \
|
||||
theLastErrorMsg = &errorMsg; \
|
||||
reportError(JP_SOURCE_CODE_POS, e); \
|
||||
} \
|
||||
JP_CATCH_UNKNOWN_EXCEPTION
|
||||
|
||||
JP_CATCH_ALL;
|
||||
|
||||
#undef JP_HANDLE_ERROR
|
||||
#undef JP_HANDLE_UNKNOWN_ERROR
|
||||
#undef JP_CATCH_EXCEPTIONS
|
||||
#define JP_HANDLE_ERROR(e) JP_REPORT_ERROR(e)
|
||||
#define JP_HANDLE_UNKNOWN_ERROR JP_REPORT_UNKNOWN_ERROR
|
||||
#define JP_CATCH_EXCEPTIONS JP_DEFAULT_CATCH_EXCEPTIONS
|
||||
|
||||
return 1;
|
||||
}
|
||||
} // namespace app
|
49
src/jdk.jpackage/share/native/common/app.h
Normal file
49
src/jdk.jpackage/share/native/common/app.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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 app_h
|
||||
#define app_h
|
||||
|
||||
#include "tstrings.h"
|
||||
|
||||
class LogAppender;
|
||||
|
||||
namespace app {
|
||||
|
||||
LogAppender& defaultLastErrorLogAppender();
|
||||
|
||||
bool isWithLogging();
|
||||
|
||||
typedef void (*LauncherFunc) ();
|
||||
|
||||
int launch(const std::nothrow_t&, LauncherFunc func,
|
||||
LogAppender* lastErrorLogAppender = 0);
|
||||
|
||||
std::string lastErrorMsg();
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif // app_h
|
@ -287,5 +287,36 @@ std::wstring toUtf16(const std::string& utf8str) {
|
||||
return fromMultiByte(utf8str, CP_UTF8);
|
||||
}
|
||||
|
||||
// converts utf16-encoded string to Windows encoded string (WIDECHAR or ACP)
|
||||
tstring toWinString(const std::wstring& utf16) {
|
||||
#if defined(_UNICODE) || defined(UNICODE)
|
||||
return utf16;
|
||||
#else
|
||||
return toMultiByte(utf16, CP_ACP);
|
||||
#endif
|
||||
}
|
||||
|
||||
// converts utf8-encoded string to Windows encoded string (WIDECHAR or ACP)
|
||||
tstring toWinString(const std::string& utf8) {
|
||||
return toWinString(tstrings::toUtf16(utf8));
|
||||
}
|
||||
|
||||
|
||||
std::string winStringToUtf8(const std::wstring& winStr) {
|
||||
return toUtf8(winStr);
|
||||
}
|
||||
|
||||
std::string winStringToUtf8(const std::string& winStr) {
|
||||
return toUtf8(fromMultiByte(winStr, CP_ACP));
|
||||
}
|
||||
|
||||
std::wstring winStringToUtf16(const std::wstring& winStr) {
|
||||
return winStr;
|
||||
}
|
||||
|
||||
std::wstring winStringToUtf16(const std::string& winStr) {
|
||||
return fromMultiByte(winStr, CP_ACP);
|
||||
}
|
||||
|
||||
} // namespace tstrings
|
||||
#endif // ifdef TSTRINGS_WITH_WCHAR
|
||||
|
@ -143,6 +143,16 @@ namespace tstrings {
|
||||
// conversion to the active code page
|
||||
std::string toACP(const std::wstring& utf16str);
|
||||
|
||||
// conversion from windows-encoding string (WIDECHAR or ACP) to utf8/utf16
|
||||
std::string winStringToUtf8(const std::wstring& winStr);
|
||||
std::string winStringToUtf8(const std::string& winStr);
|
||||
std::wstring winStringToUtf16(const std::wstring& winStr);
|
||||
std::wstring winStringToUtf16(const std::string& winStr);
|
||||
|
||||
// conversion from utf8/utf16 to windows-encoding string (WIDECHAR or ACP)
|
||||
tstring toWinString(const std::wstring& utf16);
|
||||
tstring toWinString(const std::string& utf8);
|
||||
|
||||
// conversion to Utf8
|
||||
std::string toUtf8(const std::wstring& utf16str);
|
||||
|
||||
@ -153,6 +163,10 @@ namespace tstrings {
|
||||
return toUtf16(utf8str);
|
||||
}
|
||||
|
||||
inline tstring fromUtf16(const std::wstring& utf16str) {
|
||||
return utf16str;
|
||||
}
|
||||
|
||||
#else
|
||||
inline std::string fromUtf8(const std::string& utf8str) {
|
||||
return utf8str;
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "JvmLauncher.h"
|
||||
#include "Log.h"
|
||||
#include "Dll.h"
|
||||
#include "WinApp.h"
|
||||
#include "Toolbox.h"
|
||||
#include "FileUtils.h"
|
||||
#include "UniqueHandle.h"
|
||||
@ -170,113 +171,13 @@ void launchApp() {
|
||||
#ifndef JP_LAUNCHERW
|
||||
|
||||
int __cdecl wmain() {
|
||||
return AppLauncher::launch(std::nothrow, launchApp);
|
||||
return app::launch(std::nothrow, launchApp);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
namespace {
|
||||
|
||||
class LastErrorGuiLogAppender : public LogAppender {
|
||||
public:
|
||||
virtual void append(const LogEvent& v) {
|
||||
JP_TRY;
|
||||
|
||||
const std::wstring msg = (tstrings::any()
|
||||
<< AppLauncher::lastErrorMsg()).wstr();
|
||||
MessageBox(0, msg.c_str(),
|
||||
FileUtils::basename(SysInfo::getProcessModulePath()).c_str(),
|
||||
MB_ICONERROR | MB_OK);
|
||||
|
||||
JP_CATCH_ALL;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Console {
|
||||
public:
|
||||
Console() {
|
||||
if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
|
||||
// Failed to connect to parent's console. Create our own.
|
||||
if (!AllocConsole()) {
|
||||
// We already have a console, no need to redirect std I/O.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
stdoutChannel = std::unique_ptr<Channel>(new Channel(stdout));
|
||||
stderrChannel = std::unique_ptr<Channel>(new Channel(stderr));
|
||||
}
|
||||
|
||||
struct FileCloser {
|
||||
typedef FILE* pointer;
|
||||
|
||||
void operator()(pointer h) {
|
||||
::fclose(h);
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::unique_ptr<
|
||||
FileCloser::pointer,
|
||||
FileCloser
|
||||
> UniqueFILEHandle;
|
||||
|
||||
private:
|
||||
class Channel {
|
||||
public:
|
||||
Channel(FILE* stdFILEHandle): stdFILEHandle(stdFILEHandle) {
|
||||
const char* stdFileName = "CONOUT$";
|
||||
const char* openMode = "w";
|
||||
if (stdFILEHandle == stdin) {
|
||||
stdFileName = "CONIN$";
|
||||
openMode = "r";
|
||||
}
|
||||
|
||||
FILE* fp = 0;
|
||||
freopen_s(&fp, stdFileName, openMode, stdFILEHandle);
|
||||
|
||||
fileHandle = UniqueFILEHandle(fp);
|
||||
|
||||
std::ios_base::sync_with_stdio();
|
||||
}
|
||||
|
||||
virtual ~Channel() {
|
||||
JP_TRY;
|
||||
|
||||
FILE* fp = 0;
|
||||
fileHandle = UniqueFILEHandle(fp);
|
||||
std::ios_base::sync_with_stdio();
|
||||
|
||||
JP_CATCH_ALL;
|
||||
}
|
||||
|
||||
private:
|
||||
UniqueFILEHandle fileHandle;
|
||||
FILE *stdFILEHandle;
|
||||
};
|
||||
|
||||
std::unique_ptr<Channel> stdoutChannel;
|
||||
std::unique_ptr<Channel> stderrChannel;
|
||||
};
|
||||
|
||||
|
||||
void launchAppW() {
|
||||
std::unique_ptr<Console> console;
|
||||
if (AppLauncher::isWithLogging()) {
|
||||
console = std::unique_ptr<Console>(new Console());
|
||||
}
|
||||
|
||||
launchApp();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) {
|
||||
LastErrorGuiLogAppender lastErrorLogAppender;
|
||||
TeeLogAppender logAppender(&AppLauncher::defaultLastErrorLogAppender(),
|
||||
&lastErrorLogAppender);
|
||||
return AppLauncher::launch(std::nothrow, launchAppW, &logAppender);
|
||||
return app::wlaunch(std::nothrow, launchApp);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
50
src/jdk.jpackage/windows/native/common/Flag.h
Normal file
50
src/jdk.jpackage/windows/native/common/Flag.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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 Flag_h
|
||||
#define Flag_h
|
||||
|
||||
|
||||
template <class T, class T2=int, int Id=0>
|
||||
class Flag {
|
||||
public:
|
||||
explicit Flag(T2 v): val(v) {}
|
||||
|
||||
bool operator == (const Flag& other) const {
|
||||
return val == other.val;
|
||||
}
|
||||
bool operator != (const Flag& other) const {
|
||||
return ! *this == other;
|
||||
}
|
||||
|
||||
T2 value() const {
|
||||
return val;
|
||||
}
|
||||
|
||||
private:
|
||||
T2 val;
|
||||
};
|
||||
|
||||
#endif // #ifndef Flag_h
|
137
src/jdk.jpackage/windows/native/common/Guid.cpp
Normal file
137
src/jdk.jpackage/windows/native/common/Guid.cpp
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <Objbase.h>
|
||||
|
||||
#include "Guid.h"
|
||||
#include "ErrorHandling.h"
|
||||
|
||||
|
||||
#pragma comment(lib, "ole32")
|
||||
|
||||
|
||||
Guid::Guid(const std::string& str) {
|
||||
*this = Guid(std::wstring(str.begin(), str.end()));
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
void initGuid(const std::wstring& str, GUID& v) {
|
||||
if (S_OK != IIDFromString(str.c_str(), &v)) {
|
||||
JP_THROW(tstrings::any() << "IIDFromString(" << str << ") failed");
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace
|
||||
|
||||
Guid::Guid(const std::wstring& str) {
|
||||
enum { StdGuildLength = 38 };
|
||||
|
||||
if ((str.size() == StdGuildLength && str.front() == L'{' && str.back() == L'}')) {
|
||||
initGuid(str, value);
|
||||
return;
|
||||
}
|
||||
|
||||
enum { BracketCount = 2 };
|
||||
enum { DashCount = 4 };
|
||||
|
||||
std::wstring buf(str);
|
||||
|
||||
if (str.size() >= (StdGuildLength - (BracketCount + DashCount))) {
|
||||
if (str.front() != L'{' && str.back() != L'}') {
|
||||
buf = L"{" + str + L"}";
|
||||
}
|
||||
|
||||
if (str.find(L'-') == std::wstring::npos) {
|
||||
const size_t positions[] = { 9, 14, 19, 24 };
|
||||
for (int i = 0; i < DashCount; ++i) {
|
||||
buf.insert(positions[i], 1, L'-');
|
||||
}
|
||||
}
|
||||
|
||||
if (buf.size() != StdGuildLength) {
|
||||
// Still no good, drop all tweaks.
|
||||
// Let parsing function fail on the original string.
|
||||
buf = str;
|
||||
}
|
||||
}
|
||||
initGuid(buf, value);
|
||||
}
|
||||
|
||||
|
||||
Guid::Guid(const GUID& v): value(v) {
|
||||
}
|
||||
|
||||
|
||||
Guid::Guid() {
|
||||
memset(&value, 0, sizeof(value));
|
||||
}
|
||||
|
||||
|
||||
bool Guid::operator < (const Guid& other) const {
|
||||
return toString() < other.toString();
|
||||
}
|
||||
|
||||
|
||||
bool Guid::operator == (const Guid& other) const {
|
||||
return IsEqualGUID(value, other.value) != FALSE;
|
||||
}
|
||||
|
||||
|
||||
tstring Guid::toString(int flags) const {
|
||||
wchar_t buffer[128];
|
||||
const int chars = StringFromGUID2(value, buffer, _countof(buffer));
|
||||
if (chars < 3 /* strlen("{}") + 1 */) {
|
||||
JP_THROW("StringFromGUID2() failed");
|
||||
}
|
||||
|
||||
tstring reply(tstrings::fromUtf16(buffer));
|
||||
|
||||
if (flags & NoCurlyBrackets) {
|
||||
reply = reply.substr(1, reply.size() - 2);
|
||||
}
|
||||
|
||||
if (flags & NoDashes) {
|
||||
// Drop all '-'.
|
||||
reply = tstring(reply.begin(), std::remove(reply.begin(), reply.end(), _T('-')));
|
||||
}
|
||||
|
||||
if (flags & LowerCase) {
|
||||
reply = tstrings::toLower(reply);
|
||||
}
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
|
||||
Guid Guid::generate() {
|
||||
GUID guid = { 0 };
|
||||
if (S_OK != CoCreateGuid(&guid)) {
|
||||
JP_THROW("CoCreateGuid() failed");
|
||||
}
|
||||
return Guid(guid);
|
||||
}
|
75
src/jdk.jpackage/windows/native/common/Guid.h
Normal file
75
src/jdk.jpackage/windows/native/common/Guid.h
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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 Guid_h
|
||||
#define Guid_h
|
||||
|
||||
#include <windows.h>
|
||||
#include "tstrings.h"
|
||||
|
||||
|
||||
class Guid {
|
||||
public:
|
||||
Guid(const std::string& str);
|
||||
Guid(const std::wstring& str);
|
||||
Guid(const GUID& v);
|
||||
Guid();
|
||||
|
||||
// Comparison for equality is the only comparison operation that make
|
||||
// sense for GUIDs. However in order to use STL algorithms with
|
||||
// Guid class need to define less operator.
|
||||
bool operator < (const Guid& other) const;
|
||||
bool operator == (const Guid& other) const;
|
||||
bool operator != (const Guid& other) const {
|
||||
return ! (*this == other);
|
||||
}
|
||||
|
||||
enum StringifyFlags {
|
||||
WithCurlyBrackets = 0x0,
|
||||
WithDashes = 0x0,
|
||||
UpperCase = 0x0,
|
||||
StringifyDefaults = WithCurlyBrackets | UpperCase | WithDashes,
|
||||
NoCurlyBrackets = 0x1,
|
||||
NoDashes = 0x2,
|
||||
LowerCase = 0x4,
|
||||
};
|
||||
|
||||
tstring toString(int flags=StringifyDefaults) const;
|
||||
|
||||
/**
|
||||
* Returns string GUID representation of this instance compatible with
|
||||
* Windows MSI API.
|
||||
*/
|
||||
tstring toMsiString() const {
|
||||
return toString(UpperCase | WithCurlyBrackets | WithDashes);
|
||||
}
|
||||
|
||||
static Guid generate();
|
||||
|
||||
private:
|
||||
GUID value;
|
||||
};
|
||||
|
||||
#endif // #ifndef Guid_h
|
282
src/jdk.jpackage/windows/native/common/MsiDb.cpp
Normal file
282
src/jdk.jpackage/windows/native/common/MsiDb.cpp
Normal file
@ -0,0 +1,282 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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 "MsiDb.h"
|
||||
#include "FileUtils.h"
|
||||
#include "WinFileUtils.h"
|
||||
#include "Log.h"
|
||||
|
||||
|
||||
#pragma comment(lib, "msi.lib")
|
||||
|
||||
|
||||
namespace msi {
|
||||
|
||||
void closeDatabaseView(MSIHANDLE hView) {
|
||||
if (hView) {
|
||||
const auto status = MsiViewClose(hView);
|
||||
if (status != ERROR_SUCCESS) {
|
||||
LOG_WARNING(tstrings::any() << "MsiViewClose("
|
||||
<< hView << ") failed with error=" << status);
|
||||
return;
|
||||
}
|
||||
closeMSIHANDLE(hView);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
UniqueMSIHANDLE openDatabase(const tstring& msiPath) {
|
||||
MSIHANDLE h = 0;
|
||||
const UINT status = MsiOpenDatabase(msiPath.c_str(),
|
||||
MSIDBOPEN_READONLY, &h);
|
||||
if (status != ERROR_SUCCESS) {
|
||||
JP_THROW(Error(tstrings::any()
|
||||
<< "MsiOpenDatabase(" << msiPath
|
||||
<< ", MSIDBOPEN_READONLY) failed", status));
|
||||
}
|
||||
return UniqueMSIHANDLE(h);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Database::Database(const Guid& productCode):
|
||||
msiPath(getProductInfo(productCode, INSTALLPROPERTY_LOCALPACKAGE)),
|
||||
dbHandle(openDatabase(msiPath)) {
|
||||
}
|
||||
|
||||
|
||||
Database::Database(const tstring& msiPath): msiPath(msiPath),
|
||||
dbHandle(openDatabase(msiPath)) {
|
||||
}
|
||||
|
||||
|
||||
tstring Database::getProperty(const tstring& name) const {
|
||||
// Query value of a property with the given name from 'Property' MSI table.
|
||||
const tstring sqlQuery = (tstrings::any()
|
||||
<< "SELECT Value FROM Property WHERE Property = '"
|
||||
<< name << "'").tstr();
|
||||
|
||||
DatabaseView view(*this, sqlQuery);
|
||||
const DatabaseRecord record(view);
|
||||
|
||||
// Data is stored in a record object. SQL query is constructed in a way
|
||||
// this record object contains a single field.
|
||||
// Verify record contains exactly one field.
|
||||
if (record.getFieldCount() != 1) {
|
||||
JP_THROW(Error(
|
||||
tstrings::any() << "record.getFieldCount(" << msiPath
|
||||
<< ", " << sqlQuery
|
||||
<< ") returned unexpected value",
|
||||
ERROR_SUCCESS));
|
||||
}
|
||||
|
||||
// Field identifier. They start with 1, not from 0.
|
||||
const unsigned field = 1;
|
||||
return record.getString(field);
|
||||
}
|
||||
|
||||
|
||||
tstring Database::getProperty(const std::nothrow_t&, const tstring& name) const {
|
||||
try {
|
||||
return getProperty(name);
|
||||
} catch (const NoMoreItemsError&) {
|
||||
}
|
||||
JP_CATCH_EXCEPTIONS;
|
||||
return tstring();
|
||||
}
|
||||
|
||||
|
||||
DatabaseRecord::DatabaseRecord(unsigned fieldCount) {
|
||||
MSIHANDLE h = MsiCreateRecord(fieldCount);
|
||||
if (!h) {
|
||||
JP_THROW(msi::Error(tstrings::any() << "MsiCreateRecord("
|
||||
<< fieldCount << ") failed", ERROR_FUNCTION_FAILED));
|
||||
}
|
||||
handle = UniqueMSIHANDLE(h);
|
||||
}
|
||||
|
||||
|
||||
DatabaseRecord& DatabaseRecord::operator=(const DatabaseRecord& other) {
|
||||
DatabaseRecord tmp(other);
|
||||
std::swap(handle, tmp.handle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
DatabaseRecord& DatabaseRecord::fetch(DatabaseView& view) {
|
||||
*this = view.fetch();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
DatabaseRecord& DatabaseRecord::tryFetch(DatabaseView& view) {
|
||||
*this = view.tryFetch();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
DatabaseRecord& DatabaseRecord::setString(unsigned idx, const tstring& v) {
|
||||
const UINT status = MsiRecordSetString(handle.get(), idx, v.c_str());
|
||||
if (status != ERROR_SUCCESS) {
|
||||
JP_THROW(Error(tstrings::any() << "MsiRecordSetString(" << idx
|
||||
<< ", " << v << ") failed", status));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
DatabaseRecord& DatabaseRecord::setInteger(unsigned idx, int v) {
|
||||
const UINT status = MsiRecordSetInteger(handle.get(), idx, v);
|
||||
if (status != ERROR_SUCCESS) {
|
||||
JP_THROW(Error(tstrings::any() << "MsiRecordSetInteger(" << idx
|
||||
<< ", " << v << ") failed", status));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
DatabaseRecord& DatabaseRecord::setStreamFromFile(unsigned idx,
|
||||
const tstring& v) {
|
||||
const UINT status = MsiRecordSetStream(handle.get(), idx, v.c_str());
|
||||
if (status != ERROR_SUCCESS) {
|
||||
JP_THROW(Error(tstrings::any() << "MsiRecordSetStream(" << idx
|
||||
<< ", " << v << ") failed", status));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
unsigned DatabaseRecord::getFieldCount() const {
|
||||
const unsigned reply = MsiRecordGetFieldCount(handle.get());
|
||||
if (int(reply) <= 0) {
|
||||
JP_THROW(Error(std::string("MsiRecordGetFieldCount() failed"),
|
||||
ERROR_FUNCTION_FAILED));
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
|
||||
|
||||
int DatabaseRecord::getInteger(unsigned idx) const {
|
||||
int const reply = MsiRecordGetInteger(handle.get(), idx);
|
||||
if (reply == MSI_NULL_INTEGER) {
|
||||
JP_THROW(Error(tstrings::any() << "MsiRecordGetInteger(" << idx
|
||||
<< ") failed", ERROR_FUNCTION_FAILED));
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
|
||||
|
||||
void DatabaseRecord::saveStreamToFile(unsigned idx,
|
||||
const tstring& path) const {
|
||||
enum { ReadStreamBufferBytes = 1024 * 1024 };
|
||||
|
||||
FileUtils::FileWriter writer(path);
|
||||
|
||||
std::vector<char> buffer(ReadStreamBufferBytes);
|
||||
DWORD bytes;
|
||||
do {
|
||||
bytes = ReadStreamBufferBytes;
|
||||
const UINT status = MsiRecordReadStream(handle.get(), UINT(idx),
|
||||
buffer.data(), &bytes);
|
||||
if (status != ERROR_SUCCESS) {
|
||||
JP_THROW(Error(std::string("MsiRecordReadStream() failed"),
|
||||
status));
|
||||
}
|
||||
writer.write(buffer.data(), bytes);
|
||||
} while(bytes == ReadStreamBufferBytes);
|
||||
writer.finalize();
|
||||
}
|
||||
|
||||
|
||||
DatabaseView::DatabaseView(const Database& db, const tstring& sqlQuery,
|
||||
const DatabaseRecord& queryParam): db(db), sqlQuery(sqlQuery) {
|
||||
MSIHANDLE h = 0;
|
||||
|
||||
// Create SQL query.
|
||||
for (const UINT status = MsiDatabaseOpenView(db.dbHandle.get(),
|
||||
sqlQuery.c_str(), &h); status != ERROR_SUCCESS; ) {
|
||||
JP_THROW(Error(tstrings::any() << "MsiDatabaseOpenView("
|
||||
<< sqlQuery << ") failed", status));
|
||||
}
|
||||
|
||||
UniqueMSIHANDLE tmp(h);
|
||||
|
||||
// Run SQL query.
|
||||
for (const UINT status = MsiViewExecute(h, queryParam.handle.get());
|
||||
status != ERROR_SUCCESS; ) {
|
||||
JP_THROW(Error(tstrings::any() << "MsiViewExecute("
|
||||
<< sqlQuery << ") failed", status));
|
||||
}
|
||||
|
||||
// MsiViewClose should be called only after
|
||||
// successful MsiViewExecute() call.
|
||||
handle = UniqueDbView(h);
|
||||
tmp.release();
|
||||
}
|
||||
|
||||
|
||||
DatabaseRecord DatabaseView::fetch() {
|
||||
DatabaseRecord reply = tryFetch();
|
||||
if (reply.empty()) {
|
||||
JP_THROW(NoMoreItemsError(tstrings::any() << "No more items in ["
|
||||
<< sqlQuery << "] query"));
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
|
||||
|
||||
DatabaseRecord DatabaseView::tryFetch() {
|
||||
MSIHANDLE h = 0;
|
||||
|
||||
// Fetch data from executed SQL query.
|
||||
// Data is stored in a record object.
|
||||
for (const UINT status = MsiViewFetch(handle.get(), &h);
|
||||
status != ERROR_SUCCESS; ) {
|
||||
if (status == ERROR_NO_MORE_ITEMS) {
|
||||
return DatabaseRecord();
|
||||
}
|
||||
|
||||
JP_THROW(Error(tstrings::any() << "MsiViewFetch(" << sqlQuery
|
||||
<< ") failed", status));
|
||||
}
|
||||
|
||||
DatabaseRecord reply;
|
||||
reply.handle = UniqueMSIHANDLE(h);
|
||||
return reply;
|
||||
}
|
||||
|
||||
|
||||
DatabaseView& DatabaseView::modify(const DatabaseRecord& record,
|
||||
MSIMODIFY mode) {
|
||||
const UINT status = MsiViewModify(handle.get(), mode, record.handle.get());
|
||||
if (status != ERROR_SUCCESS) {
|
||||
JP_THROW(Error(tstrings::any() << "MsiViewModify(mode=" << mode
|
||||
<< ") failed", status));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace msi
|
194
src/jdk.jpackage/windows/native/common/MsiDb.h
Normal file
194
src/jdk.jpackage/windows/native/common/MsiDb.h
Normal file
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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 MsiDb_h
|
||||
#define MsiDb_h
|
||||
|
||||
#include <windows.h>
|
||||
#include <msiquery.h>
|
||||
|
||||
#include "MsiUtils.h"
|
||||
|
||||
|
||||
class Guid;
|
||||
|
||||
/**
|
||||
* Helpers to interact with MSI through database interface.
|
||||
*/
|
||||
|
||||
namespace msi {
|
||||
void closeDatabaseView(MSIHANDLE h);
|
||||
|
||||
struct MsiDbViewDeleter {
|
||||
typedef MSIHANDLE pointer;
|
||||
|
||||
void operator()(MSIHANDLE h) {
|
||||
closeDatabaseView(h);
|
||||
}
|
||||
};
|
||||
} // namespace msi
|
||||
|
||||
|
||||
typedef std::unique_ptr<MSIHANDLE, msi::MsiDbViewDeleter> UniqueDbView;
|
||||
|
||||
|
||||
namespace msi {
|
||||
|
||||
class CA;
|
||||
class DatabaseView;
|
||||
class DatabaseRecord;
|
||||
|
||||
|
||||
/**
|
||||
* Opens product's database to query properties.
|
||||
* The database is opened in R/O mode, i.e. it is safe to call this method
|
||||
* even if there is active install/uninstall session. Unlike MsiOpenProduct(),
|
||||
* it never fails with 1618 ('Another installation is
|
||||
* already in progress') error code.
|
||||
*
|
||||
* Database can be opened from product code GUID, path to msi package or from
|
||||
* custom action.
|
||||
*
|
||||
* If opened from CA the database is opened in R/W mode, however only adding
|
||||
* new temporary records is supported. It is forbidden to change data in
|
||||
* existing records.
|
||||
*/
|
||||
class Database {
|
||||
public:
|
||||
/**
|
||||
* Opens msi database from the given product code GUID.
|
||||
* Throws exception if fails.
|
||||
*/
|
||||
explicit Database(const Guid& productCode);
|
||||
|
||||
/**
|
||||
* Opens msi database from the given path to .msi file.
|
||||
* Throws exception if fails.
|
||||
*/
|
||||
explicit Database(const tstring& msiPath);
|
||||
|
||||
/**
|
||||
* Opens msi database from the given custom action.
|
||||
* Throws exception if fails.
|
||||
*/
|
||||
explicit Database(const CA& ca);
|
||||
|
||||
/**
|
||||
* Returns value of property with the given name.
|
||||
* Throws NoMoreItemsError if property with the given name doesn't exist
|
||||
* or Error if some error occurred.
|
||||
*/
|
||||
tstring getProperty(const tstring& name) const;
|
||||
|
||||
/**
|
||||
* Returns value of property with the given name.
|
||||
* Returns empty string if property with the given name doesn't exist or
|
||||
* if some error occurred.
|
||||
*/
|
||||
tstring getProperty(const std::nothrow_t&, const tstring& name) const;
|
||||
|
||||
friend class DatabaseView;
|
||||
|
||||
private:
|
||||
Database(const Database&);
|
||||
Database& operator=(const Database&);
|
||||
private:
|
||||
const tstring msiPath;
|
||||
UniqueMSIHANDLE dbHandle;
|
||||
};
|
||||
|
||||
typedef std::unique_ptr<Database> DatabasePtr;
|
||||
|
||||
|
||||
class DatabaseRecord {
|
||||
public:
|
||||
DatabaseRecord(): handle(MSIHANDLE(0)) {
|
||||
}
|
||||
|
||||
DatabaseRecord(const DatabaseRecord& other): handle(MSIHANDLE(0)) {
|
||||
handle.swap(other.handle);
|
||||
}
|
||||
|
||||
DatabaseRecord& operator=(const DatabaseRecord& other);
|
||||
|
||||
friend class DatabaseView;
|
||||
|
||||
explicit DatabaseRecord(unsigned fieldCount);
|
||||
|
||||
explicit DatabaseRecord(DatabaseView& view) {
|
||||
fetch(view);
|
||||
}
|
||||
|
||||
DatabaseRecord& fetch(DatabaseView& view);
|
||||
|
||||
DatabaseRecord& tryFetch(DatabaseView& view);
|
||||
|
||||
DatabaseRecord& setString(unsigned idx, const tstring& v);
|
||||
|
||||
DatabaseRecord& setInteger(unsigned idx, int v);
|
||||
|
||||
DatabaseRecord& setStreamFromFile(unsigned idx, const tstring& v);
|
||||
|
||||
unsigned getFieldCount() const;
|
||||
|
||||
tstring getString(unsigned idx) const;
|
||||
|
||||
int getInteger(unsigned idx) const;
|
||||
|
||||
void saveStreamToFile(unsigned idx, const tstring& path) const;
|
||||
|
||||
bool empty() const {
|
||||
return 0 == handle.get();
|
||||
}
|
||||
|
||||
MSIHANDLE getHandle() const {
|
||||
return handle.get();
|
||||
}
|
||||
|
||||
private:
|
||||
mutable UniqueMSIHANDLE handle;
|
||||
};
|
||||
|
||||
|
||||
class DatabaseView {
|
||||
public:
|
||||
DatabaseView(const Database& db, const tstring& sqlQuery,
|
||||
const DatabaseRecord& queryParam=DatabaseRecord());
|
||||
|
||||
DatabaseRecord fetch();
|
||||
|
||||
DatabaseRecord tryFetch();
|
||||
|
||||
DatabaseView& modify(const DatabaseRecord& record, MSIMODIFY mode);
|
||||
|
||||
private:
|
||||
tstring sqlQuery;
|
||||
const Database& db;
|
||||
UniqueDbView handle;
|
||||
};
|
||||
|
||||
} // namespace msi
|
||||
|
||||
#endif // #ifndef MsiDb_h
|
420
src/jdk.jpackage/windows/native/common/MsiUtils.cpp
Normal file
420
src/jdk.jpackage/windows/native/common/MsiUtils.cpp
Normal file
@ -0,0 +1,420 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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 "MsiUtils.h"
|
||||
#include "MsiDb.h"
|
||||
#include "Resources.h"
|
||||
#include "Dll.h"
|
||||
#include "UniqueHandle.h"
|
||||
#include "FileUtils.h"
|
||||
#include "WinErrorHandling.h"
|
||||
|
||||
|
||||
#pragma comment(lib, "msi.lib")
|
||||
|
||||
|
||||
namespace msi {
|
||||
|
||||
namespace {
|
||||
|
||||
template <class Func, class Arg1Type, class Arg2Type>
|
||||
tstring getProperty(Func func, const LPCSTR funcName, Arg1Type arg1,
|
||||
Arg2Type arg2) {
|
||||
|
||||
std::vector<TCHAR> buf(20);
|
||||
DWORD size = static_cast<DWORD>(buf.size());
|
||||
|
||||
UINT status = ERROR_MORE_DATA;
|
||||
while (ERROR_MORE_DATA ==
|
||||
(status = func(arg1, arg2, &*buf.begin(), &size))) {
|
||||
buf.resize(buf.size() * 2);
|
||||
size = static_cast<DWORD>(buf.size());
|
||||
}
|
||||
|
||||
if (status != ERROR_SUCCESS) {
|
||||
JP_THROW(Error(tstrings::any() << funcName << "(" << arg1
|
||||
<< ", " << arg2 << ") failed", status));
|
||||
}
|
||||
return tstring(buf.begin(), buf.begin() + size);
|
||||
}
|
||||
|
||||
template <class Func, class Arg1Type, class Arg2Type>
|
||||
tstring getProperty(const std::nothrow_t&, Func func, const LPCSTR funcName,
|
||||
Arg1Type arg1, Arg2Type arg2) {
|
||||
try {
|
||||
return getProperty(func, funcName, arg1, arg2);
|
||||
} catch (const std::exception&) {
|
||||
}
|
||||
return tstring();
|
||||
}
|
||||
|
||||
|
||||
tstring escapePropertyValue(const tstring& value) {
|
||||
// Escape quotes as described in
|
||||
// http://msdn.microsoft.com/en-us/library/aa367988.aspx
|
||||
tstring reply = tstrings::replace(value, _T("\""), _T("\"\""));
|
||||
|
||||
if (reply.empty()) {
|
||||
// MSDN: To clear a public property by using the command line,
|
||||
// set its value to an empty string.
|
||||
reply = _T("\"\"");
|
||||
}
|
||||
|
||||
if (reply.find_first_of(_T(" \t")) != tstring::npos) {
|
||||
reply = _T('"') + reply + _T('"');
|
||||
}
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
template <class It>
|
||||
tstring stringifyProperties(It b, It e) {
|
||||
tostringstream buf;
|
||||
for (; b != e; ++b) {
|
||||
const tstring value = escapePropertyValue(b->second);
|
||||
buf << _T(" ") << b->first << _T("=") << value;
|
||||
}
|
||||
|
||||
tstring reply = tstrings::trim(buf.str());
|
||||
return reply;
|
||||
}
|
||||
|
||||
|
||||
class CallbackTrigger {
|
||||
CallbackTrigger(const CallbackTrigger&);
|
||||
CallbackTrigger& operator=(const CallbackTrigger&);
|
||||
|
||||
enum { MESSAGE_FILTER = 0xffffffff };
|
||||
|
||||
static int WINAPI adapter(LPVOID ctx, UINT type, LPCWSTR msg) {
|
||||
Callback* callback = reinterpret_cast<Callback*>(ctx);
|
||||
if (!callback) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
JP_TRY;
|
||||
|
||||
// MSDN: Handling Progress Messages Using MsiSetExternalUI
|
||||
// http://msdn.microsoft.com/en-us/library/aa368786(v=vs.85).aspx
|
||||
const INSTALLMESSAGE mt = (INSTALLMESSAGE)(0xFF000000 & type);
|
||||
const UINT flags = 0x00FFFFFF & type;
|
||||
|
||||
if (msg) {
|
||||
callback->notify(mt, flags, tstrings::toWinString(msg));
|
||||
}
|
||||
|
||||
JP_CATCH_ALL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public:
|
||||
explicit CallbackTrigger(Callback& cb) {
|
||||
MsiSetExternalUIW(adapter, DWORD(MESSAGE_FILTER), &cb);
|
||||
}
|
||||
|
||||
~CallbackTrigger() {
|
||||
// Not restoring the original callback.
|
||||
// Just because the original message filter is unknown.
|
||||
MsiSetExternalUIW(0, 0, 0);
|
||||
}
|
||||
};
|
||||
|
||||
class LogFileTrigger {
|
||||
LogFileTrigger(const LogFileTrigger&);
|
||||
LogFileTrigger& operator=(const LogFileTrigger&);
|
||||
|
||||
public:
|
||||
explicit LogFileTrigger(const tstring& path) {
|
||||
if (path.empty()) {
|
||||
MsiEnableLog(0, NULL, 0);
|
||||
} else {
|
||||
MsiEnableLog(INSTALLLOGMODE_VERBOSE, path.c_str(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
~LogFileTrigger() {
|
||||
// Disable log
|
||||
MsiEnableLog(0, NULL, 0);
|
||||
}
|
||||
};
|
||||
|
||||
struct SuppressUI: public OverrideUI {
|
||||
SuppressUI(): OverrideUI(withoutUI()) {
|
||||
}
|
||||
};
|
||||
|
||||
class StateImpl: public ActionData::State {
|
||||
const OverrideUI overrideUi;
|
||||
LogFileTrigger logGuard;
|
||||
std::unique_ptr<CallbackTrigger> callbackGuard;
|
||||
|
||||
public:
|
||||
explicit StateImpl(const ActionData& data): overrideUi(data.uiMode),
|
||||
logGuard(data.logFile) {
|
||||
if (data.callback) {
|
||||
callbackGuard = std::unique_ptr<CallbackTrigger>(
|
||||
new CallbackTrigger(*data.callback));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
void closeMSIHANDLE(MSIHANDLE h) {
|
||||
if (h) {
|
||||
const auto status = MsiCloseHandle(h);
|
||||
if (status != ERROR_SUCCESS) {
|
||||
LOG_WARNING(tstrings::any() << "MsiCloseHandle("
|
||||
<< h << ") failed with error=" << status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// DatabaseRecord::getString() should live in MsiDb.cpp.
|
||||
// However it can't access handy msi::getProperty() from that location.
|
||||
tstring DatabaseRecord::getString(unsigned idx) const {
|
||||
return ::msi::getProperty(MsiRecordGetString, "MsiRecordGetString",
|
||||
handle.get(), UINT(idx));
|
||||
}
|
||||
|
||||
|
||||
tstring getProductInfo(const Guid& productCode, const tstring& prop) {
|
||||
const tstring id = productCode.toMsiString();
|
||||
return getProperty(MsiGetProductInfo, "MsiGetProductInfo", id.c_str(),
|
||||
prop.c_str());
|
||||
}
|
||||
|
||||
|
||||
tstring getProductInfo(const std::nothrow_t&, const Guid& productCode,
|
||||
const tstring& prop) {
|
||||
const tstring id = productCode.toMsiString();
|
||||
return getProperty(std::nothrow, MsiGetProductInfo, "MsiGetProductInfo",
|
||||
id.c_str(), prop.c_str());
|
||||
}
|
||||
|
||||
|
||||
tstring getPropertyFromCustomAction(MSIHANDLE h, const tstring& prop) {
|
||||
return getProperty(MsiGetProperty, "MsiGetProperty", h, prop.c_str());
|
||||
}
|
||||
|
||||
|
||||
tstring getPropertyFromCustomAction(const std::nothrow_t&, MSIHANDLE h,
|
||||
const tstring& prop) {
|
||||
return getProperty(std::nothrow, MsiGetProperty,"MsiGetProperty", h,
|
||||
prop.c_str());
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
std::string makeMessage(const std::string& msg, UINT errorCode) {
|
||||
std::ostringstream err;
|
||||
err << "MSI error [" << errorCode << "]";
|
||||
|
||||
const std::wstring msimsg_dll = tstrings::winStringToUtf16(FileUtils::combinePath(
|
||||
SysInfo::getSystem32Dir(), _T("msimsg.dll")));
|
||||
|
||||
// Convert MSI Error Code to Description String
|
||||
// http://msdn.microsoft.com/en-us/library/aa370315(v=vs.85).aspx
|
||||
Dll::Handle lib(LoadLibraryExW(msimsg_dll.c_str(), NULL,
|
||||
LOAD_LIBRARY_AS_DATAFILE));
|
||||
if (!lib.get()) {
|
||||
JP_THROW(SysError(tstrings::any() << "LoadLibraryExW(" <<
|
||||
msimsg_dll << ") failed", LoadLibraryExW));
|
||||
} else {
|
||||
tstring descr;
|
||||
try {
|
||||
descr = StringResource(errorCode, lib.get()).string();
|
||||
} catch (const std::exception &) {
|
||||
descr = _T("No description");
|
||||
}
|
||||
err << "(" << descr << ")";
|
||||
}
|
||||
|
||||
return joinErrorMessages(msg, err.str());
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Error::Error(const tstrings::any& msg, UINT ec): std::runtime_error(
|
||||
makeMessage(msg.str(), ec)), errorCode(ec) {
|
||||
}
|
||||
|
||||
|
||||
Error::Error(const std::string& msg, UINT ec): std::runtime_error(
|
||||
makeMessage(msg, ec)), errorCode(ec) {
|
||||
}
|
||||
|
||||
|
||||
tstring ActionData::getCmdLineArgs() const {
|
||||
tstring raw = tstrings::trim(rawCmdLineArgs);
|
||||
tstring strProperties = stringifyProperties(props.begin(), props.end());
|
||||
if (!raw.empty() && !strProperties.empty()) {
|
||||
raw += _T(' ');
|
||||
}
|
||||
return raw + strProperties;
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<ActionData::State> ActionData::createState() const {
|
||||
return std::unique_ptr<ActionData::State>(new StateImpl(*this));
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
bool isMsiStatusSuccess(const UINT status) {
|
||||
switch (status) {
|
||||
case ERROR_SUCCESS:
|
||||
case ERROR_SUCCESS_REBOOT_INITIATED:
|
||||
case ERROR_SUCCESS_REBOOT_REQUIRED:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ActionStatus handleMsiStatus(tstrings::any& logMsg, const UINT status) {
|
||||
if (!isMsiStatusSuccess(status)) {
|
||||
logMsg << "failed [" << status << "]";
|
||||
return ActionStatus(status, logMsg.str());
|
||||
}
|
||||
|
||||
logMsg << "succeeded";
|
||||
if (status != ERROR_SUCCESS) {
|
||||
logMsg << " [" << status << "]";
|
||||
}
|
||||
LOG_INFO(logMsg);
|
||||
return ActionStatus(status);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
ActionStatus::operator bool() const {
|
||||
return isMsiStatusSuccess(value);
|
||||
}
|
||||
|
||||
|
||||
void ActionStatus::throwIt() const {
|
||||
JP_THROW(Error(comment, value));
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
template <class T>
|
||||
ActionStatus msiAction(const T& obj, INSTALLSTATE state,
|
||||
const tstring& cmdLineArgs) {
|
||||
const tstring id = obj.getProductCode().toMsiString();
|
||||
const int level = INSTALLLEVEL_MAXIMUM;
|
||||
const UINT status = MsiConfigureProductEx(id.c_str(), level, state,
|
||||
cmdLineArgs.c_str());
|
||||
|
||||
tstrings::any logMsg;
|
||||
logMsg << "MsiConfigureProductEx("
|
||||
<< id
|
||||
<< ", " << level
|
||||
<< ", " << state
|
||||
<< ", " << cmdLineArgs
|
||||
<< ") ";
|
||||
|
||||
return handleMsiStatus(logMsg, status);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
||||
template <>
|
||||
ActionStatus action<uninstall>::execute(const uninstall& obj,
|
||||
const tstring& cmdLineArgs) {
|
||||
return msiAction(obj, INSTALLSTATE_ABSENT, cmdLineArgs);
|
||||
}
|
||||
|
||||
|
||||
template <>
|
||||
ActionStatus action<update>::execute(const update& obj,
|
||||
const tstring& cmdLineArgs) {
|
||||
return msiAction(obj, INSTALLSTATE_LOCAL, cmdLineArgs);
|
||||
}
|
||||
|
||||
|
||||
template <>
|
||||
ActionStatus action<install>::execute(const install& obj,
|
||||
const tstring& cmdLineArgs) {
|
||||
const tstring& msiPath = obj.getMsiPath();
|
||||
|
||||
const UINT status = MsiInstallProduct(msiPath.c_str(),
|
||||
cmdLineArgs.c_str());
|
||||
|
||||
tstrings::any logMsg;
|
||||
logMsg << "MsiInstallProduct(" << msiPath << ", " << cmdLineArgs << ") ";
|
||||
|
||||
return handleMsiStatus(logMsg, status);
|
||||
}
|
||||
|
||||
|
||||
uninstall::uninstall() {
|
||||
// Uninstall default behavior is to never reboot.
|
||||
setProperty(_T("REBOOT"), _T("ReallySuppress"));
|
||||
}
|
||||
|
||||
|
||||
bool waitForInstallationCompletion(DWORD timeoutMS)
|
||||
{
|
||||
// "_MSIExecute" mutex is used by the MSI installer service to prevent multiple installations at the same time
|
||||
// http://msdn.microsoft.com/en-us/library/aa372909(VS.85).aspx
|
||||
LPCTSTR mutexName = _T("Global\\_MSIExecute");
|
||||
UniqueHandle h(OpenMutex(SYNCHRONIZE, FALSE, mutexName));
|
||||
if (h.get() != NULL) {
|
||||
DWORD res = WaitForSingleObject(h.get(), timeoutMS);
|
||||
// log only if timeout != 0
|
||||
if (timeoutMS != 0) {
|
||||
LOG_INFO(tstrings::any() << "finish waiting for mutex: " << res);
|
||||
}
|
||||
if (res == WAIT_TIMEOUT) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool isProductInstalled(const Guid& productCode) {
|
||||
// Query any property. If product exists, query should succeed.
|
||||
try {
|
||||
getProductInfo(productCode, INSTALLPROPERTY_VERSIONSTRING);
|
||||
} catch (const Error& e) {
|
||||
switch (e.getReason()) {
|
||||
case ERROR_UNKNOWN_PRODUCT:
|
||||
// if the application being queried is advertised and not installed.
|
||||
case ERROR_UNKNOWN_PROPERTY:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace msi
|
337
src/jdk.jpackage/windows/native/common/MsiUtils.h
Normal file
337
src/jdk.jpackage/windows/native/common/MsiUtils.h
Normal file
@ -0,0 +1,337 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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 MsiUtils_h
|
||||
#define MsiUtils_h
|
||||
|
||||
#include <windows.h>
|
||||
#include <Msi.h>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
#include <new>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include "ErrorHandling.h"
|
||||
#include "Toolbox.h"
|
||||
#include "Guid.h"
|
||||
#include "Flag.h"
|
||||
#include "Log.h"
|
||||
|
||||
|
||||
namespace msi {
|
||||
|
||||
void closeMSIHANDLE(MSIHANDLE h);
|
||||
|
||||
struct MsiHandleDeleter {
|
||||
typedef MSIHANDLE pointer;
|
||||
|
||||
void operator()(MSIHANDLE h) {
|
||||
closeMSIHANDLE(h);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace msi
|
||||
|
||||
typedef std::unique_ptr<MSIHANDLE, msi::MsiHandleDeleter> UniqueMSIHANDLE;
|
||||
|
||||
namespace msi {
|
||||
|
||||
tstring getProductInfo(const Guid& productCode, const tstring& prop);
|
||||
|
||||
tstring getProductInfo(const std::nothrow_t&, const Guid& productCode,
|
||||
const tstring& prop);
|
||||
|
||||
tstring getPropertyFromCustomAction(MSIHANDLE h, const tstring& prop);
|
||||
|
||||
tstring getPropertyFromCustomAction(const std::nothrow_t&, MSIHANDLE h,
|
||||
const tstring& prop);
|
||||
|
||||
inline tstring getPropertyFromDeferredCustomAction(MSIHANDLE h) {
|
||||
return getPropertyFromCustomAction(h, _T("CustomActionData"));
|
||||
}
|
||||
|
||||
inline tstring getPropertyFromDeferredCustomAction(const std::nothrow_t&,
|
||||
MSIHANDLE h) {
|
||||
return getPropertyFromCustomAction(std::nothrow, h,
|
||||
_T("CustomActionData"));
|
||||
}
|
||||
|
||||
|
||||
// UI level flags
|
||||
class Tag {};
|
||||
typedef Flag<Tag, INSTALLUILEVEL> UiModeFlag;
|
||||
|
||||
inline UiModeFlag defaultUI() {
|
||||
return UiModeFlag(INSTALLUILEVEL_DEFAULT);
|
||||
}
|
||||
|
||||
inline UiModeFlag withoutUI() {
|
||||
return UiModeFlag(INSTALLUILEVEL_NONE);
|
||||
}
|
||||
|
||||
|
||||
// UI level control
|
||||
struct OverrideUI {
|
||||
explicit OverrideUI(const UiModeFlag& uiMode):
|
||||
origMsiUiLevel(MsiSetInternalUI(uiMode.value(), 0)) {
|
||||
}
|
||||
|
||||
~OverrideUI() {
|
||||
MsiSetInternalUI(origMsiUiLevel, 0);
|
||||
}
|
||||
|
||||
private:
|
||||
const INSTALLUILEVEL origMsiUiLevel;
|
||||
};
|
||||
|
||||
struct SuppressUI: public OverrideUI {
|
||||
SuppressUI(): OverrideUI(withoutUI()) {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// MSI Properties (KEY=VALUE)
|
||||
typedef std::pair<tstring, tstring> Property;
|
||||
typedef std::vector<Property> Properties;
|
||||
|
||||
|
||||
// Callback for MSI functions
|
||||
class Callback {
|
||||
public:
|
||||
virtual ~Callback() {}
|
||||
|
||||
virtual void notify(INSTALLMESSAGE msgType, UINT flags,
|
||||
const tstring& msg) = 0;
|
||||
};
|
||||
|
||||
|
||||
// MSI Error
|
||||
class Error : public std::runtime_error {
|
||||
public:
|
||||
Error(const tstrings::any& msg, UINT errorCode);
|
||||
Error(const std::string& msg, UINT errorCode);
|
||||
UINT getReason() const {
|
||||
return errorCode;
|
||||
}
|
||||
private:
|
||||
UINT errorCode;
|
||||
};
|
||||
|
||||
// "No more items" exception
|
||||
class NoMoreItemsError : public Error {
|
||||
public:
|
||||
NoMoreItemsError(const tstrings::any& msg)
|
||||
: Error(msg, ERROR_NO_MORE_ITEMS)
|
||||
{}
|
||||
};
|
||||
|
||||
struct ActionData {
|
||||
typedef std::map<tstring, tstring> PropertyMap;
|
||||
PropertyMap props;
|
||||
tstring rawCmdLineArgs;
|
||||
UiModeFlag uiMode;
|
||||
Callback* callback;
|
||||
tstring logFile;
|
||||
|
||||
struct State {
|
||||
virtual ~State() {}
|
||||
};
|
||||
|
||||
std::unique_ptr<State> createState() const;
|
||||
|
||||
tstring getCmdLineArgs() const;
|
||||
|
||||
ActionData(): uiMode(withoutUI()), callback(0) {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// MSI function execution status.
|
||||
class ActionStatus {
|
||||
public:
|
||||
ActionStatus(UINT value=ERROR_SUCCESS, const std::string& comment=""):
|
||||
value(value), comment(comment) {
|
||||
}
|
||||
|
||||
explicit operator bool() const;
|
||||
|
||||
UINT getValue() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
// Unconditionally converts this instance into msi::Error instance and
|
||||
// throws it.
|
||||
void throwIt() const;
|
||||
|
||||
const std::string& getComment() const {
|
||||
return comment;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string comment;
|
||||
UINT value;
|
||||
};
|
||||
|
||||
|
||||
// Some MSI action.
|
||||
template <class T>
|
||||
class action {
|
||||
public:
|
||||
T& setProperty(const Property& prop) {
|
||||
data.props[prop.first] = prop.second;
|
||||
return *static_cast<T*>(this);
|
||||
}
|
||||
|
||||
T& setProperty(const tstring& name, const tstring& value) {
|
||||
return setProperty(Property(name, value));
|
||||
}
|
||||
|
||||
template <class It>
|
||||
T& setProperties(It b, It e) {
|
||||
std::copy(b, e, std::inserter(data.props, data.props.end()));
|
||||
return *static_cast<T*>(this);
|
||||
}
|
||||
|
||||
T& setRawCmdLineArgs(const tstring& value) {
|
||||
data.rawCmdLineArgs = value;
|
||||
return *static_cast<T*>(this);
|
||||
}
|
||||
|
||||
T& setUiMode(const UiModeFlag& flag) {
|
||||
data.uiMode = flag;
|
||||
return *static_cast<T*>(this);
|
||||
}
|
||||
|
||||
T& setLogFile(const tstring& path=tstring()) {
|
||||
data.logFile = path;
|
||||
return *static_cast<T*>(this);
|
||||
}
|
||||
|
||||
T& setCallback(Callback* cb) {
|
||||
data.callback = cb;
|
||||
return *static_cast<T*>(this);
|
||||
}
|
||||
|
||||
tstring getCmdLineArgs() const {
|
||||
return data.getCmdLineArgs();
|
||||
}
|
||||
|
||||
void operator () () const {
|
||||
std::unique_ptr<ActionData::State> state(data.createState());
|
||||
const ActionStatus status = execute(*static_cast<const T*>(this),
|
||||
data.getCmdLineArgs());
|
||||
if (!status) {
|
||||
status.throwIt();
|
||||
}
|
||||
}
|
||||
|
||||
ActionStatus operator () (const std::nothrow_t&) const {
|
||||
JP_TRY;
|
||||
std::unique_ptr<ActionData::State> state(data.createState());
|
||||
const ActionStatus status = execute(*static_cast<const T*>(this),
|
||||
data.getCmdLineArgs());
|
||||
if (!status) {
|
||||
LOG_ERROR(status.getComment());
|
||||
}
|
||||
return status;
|
||||
JP_CATCH_ALL;
|
||||
return ActionStatus(ERROR_INTERNAL_ERROR, "Unknown error");
|
||||
}
|
||||
|
||||
private:
|
||||
static ActionStatus execute(const T& obj, const tstring& cmdLineArgs);
|
||||
|
||||
ActionData data;
|
||||
};
|
||||
|
||||
|
||||
// Function object to uninstall product with the given GUID
|
||||
class uninstall: public action<uninstall> {
|
||||
Guid productCode;
|
||||
public:
|
||||
uninstall();
|
||||
|
||||
uninstall& setProductCode(const Guid& pc) {
|
||||
productCode = pc;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const Guid& getProductCode() const {
|
||||
return productCode;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Function object to update installed product with the given GUID
|
||||
class update: public action<update> {
|
||||
Guid productCode;
|
||||
public:
|
||||
update& setProductCode(const Guid& pc) {
|
||||
productCode = pc;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const Guid& getProductCode() const {
|
||||
return productCode;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Function object to install package from the given msi file
|
||||
class install: public action<install> {
|
||||
tstring msiPath;
|
||||
public:
|
||||
install& setMsiPath(const tstring& path) {
|
||||
msiPath = path;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const tstring& getMsiPath() const {
|
||||
return msiPath;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Checks if there is some installation is in progress and waits until it completes.
|
||||
// returns true if there is no installation is in progress or the installation is completed.
|
||||
// returns false if timeout exceeded.
|
||||
// If timeout == 0, just checks that Windows Installer service is free.
|
||||
bool waitForInstallationCompletion(DWORD timeoutMS);
|
||||
|
||||
// Checks if there is some installation is in progress.
|
||||
inline bool isInstallationInProgress() {
|
||||
return !waitForInstallationCompletion(0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if product with the given product code is installed.
|
||||
*/
|
||||
bool isProductInstalled(const Guid& productCode);
|
||||
|
||||
} // namespace msi
|
||||
|
||||
#endif // #ifndef MsiUtils_h
|
@ -148,3 +148,21 @@ Resource::ByteArray Resource::binary() const {
|
||||
LPBYTE resPtr = (LPBYTE)getPtr(size);
|
||||
return ByteArray(resPtr, resPtr+size);
|
||||
}
|
||||
|
||||
|
||||
tstring StringResource::string() const
|
||||
{
|
||||
DWORD size = 0;
|
||||
// string are stored as UNICODE
|
||||
LPWSTR resPtr = reinterpret_cast<LPWSTR>(impl.getPtr(size));
|
||||
// size is in bytes;
|
||||
return tstrings::fromUtf16(std::wstring(resPtr, size / sizeof(wchar_t)));
|
||||
}
|
||||
|
||||
tstring StringResource::string(const std::nothrow_t &, const tstring &defValue) const throw()
|
||||
{
|
||||
JP_TRY;
|
||||
return string();
|
||||
JP_CATCH_ALL;
|
||||
return defValue;
|
||||
}
|
@ -29,6 +29,8 @@
|
||||
#include "WinSysInfo.h"
|
||||
|
||||
|
||||
class StringResource;
|
||||
|
||||
/**
|
||||
* Classes for resource loading.
|
||||
* Common use cases:
|
||||
@ -37,6 +39,17 @@
|
||||
* if (res.available()) {
|
||||
* res.saveToFile(_T("c:\\temp\\my_resource.bin"));
|
||||
* }
|
||||
*
|
||||
* - get string resource:
|
||||
* 1) if the resource is not available, exception is thrown:
|
||||
* tstring str = StringResource(MAKEINTRESOURCE(resID)).string();
|
||||
*
|
||||
* 2) nothrow method (returns default value if the resource is not available):
|
||||
* a) returns empty string on error:
|
||||
* tstring str = StringResource(MAKEINTRESOURCE(resID)).string(std::nothrow);
|
||||
*
|
||||
* b) returns provided default value on error:
|
||||
* tstring str = StringResource(MAKEINTRESOURCE(resID)).string(std::nothrow, _T("defaultValue"));
|
||||
*/
|
||||
|
||||
class Resource {
|
||||
@ -62,6 +75,8 @@ public:
|
||||
// returns the resource as byte array
|
||||
ByteArray binary() const;
|
||||
|
||||
friend class StringResource;
|
||||
|
||||
private:
|
||||
std::wstring nameStr;
|
||||
LPCWSTR namePtr; // can be integer value or point to nameStr.c_str()
|
||||
@ -82,4 +97,40 @@ private:
|
||||
Resource& operator = (const Resource&);
|
||||
};
|
||||
|
||||
|
||||
// Note: string resources are returned utf16 or utf8 encoded.
|
||||
// To get Windows-encoded string (utf16/ACP) use tstrings::toWinString().
|
||||
class StringResource {
|
||||
public:
|
||||
// string resource is always identified by integer id
|
||||
StringResource(UINT resourceId, HINSTANCE moduleHandle = SysInfo::getCurrentModuleHandle())
|
||||
: impl(resourceId, RT_STRING, moduleHandle) {}
|
||||
|
||||
// returns the resource as string
|
||||
tstring string() const;
|
||||
// nothrow version (logs error)
|
||||
tstring string(const std::nothrow_t &, const tstring &defValue = tstring()) const throw();
|
||||
|
||||
bool available() const throw() {
|
||||
return impl.available();
|
||||
}
|
||||
|
||||
unsigned size() const {
|
||||
return impl.size();
|
||||
}
|
||||
|
||||
static tstring load(UINT resourceId,
|
||||
HINSTANCE moduleHandle = SysInfo::getCurrentModuleHandle()) {
|
||||
return StringResource(resourceId, moduleHandle).string();
|
||||
}
|
||||
|
||||
static tstring load(const std::nothrow_t &, UINT resourceId,
|
||||
HINSTANCE moduleHandle = SysInfo::getCurrentModuleHandle()) throw () {
|
||||
return StringResource(resourceId, moduleHandle).string(std::nothrow);
|
||||
}
|
||||
|
||||
private:
|
||||
Resource impl;
|
||||
};
|
||||
|
||||
#endif // RESOURCES_H
|
139
src/jdk.jpackage/windows/native/common/WinApp.cpp
Normal file
139
src/jdk.jpackage/windows/native/common/WinApp.cpp
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include "WinApp.h"
|
||||
#include "Log.h"
|
||||
#include "SysInfo.h"
|
||||
#include "FileUtils.h"
|
||||
#include "ErrorHandling.h"
|
||||
|
||||
|
||||
// MessageBox
|
||||
#pragma comment(lib, "user32")
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
class LastErrorGuiLogAppender : public LogAppender {
|
||||
public:
|
||||
virtual void append(const LogEvent& v) {
|
||||
JP_TRY;
|
||||
|
||||
const std::wstring msg = (tstrings::any()
|
||||
<< app::lastErrorMsg()).wstr();
|
||||
MessageBox(0, msg.c_str(),
|
||||
FileUtils::basename(SysInfo::getProcessModulePath()).c_str(),
|
||||
MB_ICONERROR | MB_OK);
|
||||
|
||||
JP_CATCH_ALL;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Console {
|
||||
public:
|
||||
Console() {
|
||||
if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
|
||||
// Failed to connect to parent's console. Create our own.
|
||||
if (!AllocConsole()) {
|
||||
// We already have a console, no need to redirect std I/O.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
stdoutChannel = std::unique_ptr<Channel>(new Channel(stdout));
|
||||
stderrChannel = std::unique_ptr<Channel>(new Channel(stderr));
|
||||
}
|
||||
|
||||
struct FileCloser {
|
||||
typedef FILE* pointer;
|
||||
|
||||
void operator()(pointer h) {
|
||||
::fclose(h);
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::unique_ptr<
|
||||
FileCloser::pointer,
|
||||
FileCloser
|
||||
> UniqueFILEHandle;
|
||||
|
||||
private:
|
||||
class Channel {
|
||||
public:
|
||||
Channel(FILE* stdFILEHandle): stdFILEHandle(stdFILEHandle) {
|
||||
const char* stdFileName = "CONOUT$";
|
||||
const char* openMode = "w";
|
||||
if (stdFILEHandle == stdin) {
|
||||
stdFileName = "CONIN$";
|
||||
openMode = "r";
|
||||
}
|
||||
|
||||
FILE* fp = 0;
|
||||
freopen_s(&fp, stdFileName, openMode, stdFILEHandle);
|
||||
|
||||
fileHandle = UniqueFILEHandle(fp);
|
||||
|
||||
std::ios_base::sync_with_stdio();
|
||||
}
|
||||
|
||||
virtual ~Channel() {
|
||||
JP_TRY;
|
||||
|
||||
FILE* fp = 0;
|
||||
fileHandle = UniqueFILEHandle(fp);
|
||||
std::ios_base::sync_with_stdio();
|
||||
|
||||
JP_CATCH_ALL;
|
||||
}
|
||||
|
||||
private:
|
||||
UniqueFILEHandle fileHandle;
|
||||
FILE *stdFILEHandle;
|
||||
};
|
||||
|
||||
std::unique_ptr<Channel> stdoutChannel;
|
||||
std::unique_ptr<Channel> stderrChannel;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
namespace app {
|
||||
int wlaunch(const std::nothrow_t&, LauncherFunc func) {
|
||||
std::unique_ptr<Console> console;
|
||||
JP_TRY;
|
||||
if (app::isWithLogging()) {
|
||||
console = std::unique_ptr<Console>(new Console());
|
||||
}
|
||||
JP_CATCH_ALL;
|
||||
|
||||
LastErrorGuiLogAppender lastErrorLogAppender;
|
||||
TeeLogAppender logAppender(&app::defaultLastErrorLogAppender(),
|
||||
&lastErrorLogAppender);
|
||||
return app::launch(std::nothrow, func, &logAppender);
|
||||
}
|
||||
} // namespace app
|
38
src/jdk.jpackage/windows/native/common/WinApp.h
Normal file
38
src/jdk.jpackage/windows/native/common/WinApp.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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 WinApp_h
|
||||
#define WinApp_h
|
||||
|
||||
#include "app.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
int wlaunch(const std::nothrow_t&, LauncherFunc func);
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif // WinApp_h
|
@ -26,6 +26,7 @@
|
||||
#ifndef JNIUTILS_H
|
||||
#define JNIUTILS_H
|
||||
|
||||
#include <memory>
|
||||
#include "jni.h"
|
||||
#include "tstrings.h"
|
||||
|
||||
|
@ -93,12 +93,12 @@ public:
|
||||
ResourceEditor& id(LPCWSTR v);
|
||||
|
||||
/**
|
||||
* Relaces resource configured in the given binary with the given data stream.
|
||||
* Replaces 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
|
||||
* Replaces resource configured in the given binary with contents of
|
||||
* the given binary file.
|
||||
*/
|
||||
ResourceEditor& apply(const FileLock& dstBinary, const std::wstring& srcFile);
|
||||
|
@ -196,12 +196,14 @@ void VersionInfo::fillBuffer(std::ostream& buf) const {
|
||||
writeWORD(buf, 0); // wValueLength
|
||||
writeWORD(buf, 1); // wType
|
||||
|
||||
const std::wstring strLangId = (std::wstringstream()
|
||||
std::wstringstream strLangIdBuf;
|
||||
strLangIdBuf
|
||||
<< std::uppercase
|
||||
<< std::hex
|
||||
<< std::setw(8)
|
||||
<< std::setfill(L'0')
|
||||
<< engLangId).str();
|
||||
<< engLangId;
|
||||
const std::wstring strLangId = strLangIdBuf.str();
|
||||
write(buf, strLangId); // szKey
|
||||
add32bitPadding(buf); // Padding
|
||||
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "IconSwap.h"
|
||||
#include "VersionInfo.h"
|
||||
#include "JniUtils.h"
|
||||
#include "MsiDb.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -140,9 +141,17 @@ extern "C" {
|
||||
|
||||
const std::wstring msiPath = jni::toUnicodeString(pEnv, jmsiPath);
|
||||
|
||||
// Put msi file in resources.
|
||||
const ResourceEditor::FileLock lock(reinterpret_cast<HANDLE>(jResourceLock));
|
||||
ResourceEditor().id(L"msi").type(RT_RCDATA).apply(lock, msiPath);
|
||||
|
||||
// Get product code of the msi being embedded
|
||||
const Guid productCode = Guid(msi::Database(msiPath).getProperty(L"ProductCode"));
|
||||
|
||||
// Save product code in resources
|
||||
std::istringstream in(tstrings::toUtf8(productCode.toString()));
|
||||
ResourceEditor().id(L"product_code").type(RT_RCDATA).apply(lock, in);
|
||||
|
||||
return 0;
|
||||
|
||||
JP_CATCH_ALL;
|
||||
|
@ -26,17 +26,34 @@
|
||||
#include <algorithm>
|
||||
#include <windows.h>
|
||||
|
||||
#include "WinApp.h"
|
||||
#include "Guid.h"
|
||||
#include "SysInfo.h"
|
||||
#include "MsiUtils.h"
|
||||
#include "FileUtils.h"
|
||||
#include "WinFileUtils.h"
|
||||
#include "Executor.h"
|
||||
#include "Resources.h"
|
||||
#include "WinErrorHandling.h"
|
||||
|
||||
|
||||
int __stdcall WinMain(HINSTANCE, HINSTANCE, LPSTR lpCmdLine, int nShowCmd)
|
||||
{
|
||||
JP_TRY;
|
||||
namespace {
|
||||
int exitCode = -1;
|
||||
|
||||
void launchApp() {
|
||||
const auto cmdline = SysInfo::getCommandArgs();
|
||||
if (std::find(cmdline.begin(), cmdline.end(), L"uninstall") != cmdline.end()) {
|
||||
// This is uninstall request.
|
||||
|
||||
// Get product code of the product to uninstall.
|
||||
const auto productCodeUtf8 = Resource(L"product_code", RT_RCDATA).binary();
|
||||
const Guid productCode = Guid(std::string(
|
||||
(const char*)productCodeUtf8.data(), productCodeUtf8.size()));
|
||||
|
||||
// Uninstall product.
|
||||
msi::uninstall().setProductCode(productCode)();
|
||||
exitCode = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Create temporary directory where to extract msi file.
|
||||
const auto tempMsiDir = FileUtils::createTempDirectory();
|
||||
@ -60,9 +77,12 @@ int __stdcall WinMain(HINSTANCE, HINSTANCE, LPSTR lpCmdLine, int nShowCmd)
|
||||
});
|
||||
|
||||
// Install msi file.
|
||||
return msiExecutor.execAndWaitForExit();
|
||||
|
||||
JP_CATCH_ALL;
|
||||
|
||||
return -1;
|
||||
exitCode = msiExecutor.execAndWaitForExit();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
||||
int __stdcall WinMain(HINSTANCE, HINSTANCE, LPSTR lpCmdLine, int nShowCmd) {
|
||||
app::wlaunch(std::nothrow, launchApp);
|
||||
return exitCode;
|
||||
}
|
||||
|
@ -114,14 +114,18 @@ public class WindowsHelper {
|
||||
}
|
||||
|
||||
static PackageHandlers createExePackageHandlers() {
|
||||
PackageHandlers exe = new PackageHandlers();
|
||||
// can't have install handler without also having uninstall handler
|
||||
// so following is commented out for now
|
||||
// exe.installHandler = cmd -> {
|
||||
// cmd.verifyIsOfType(PackageType.WIN_EXE);
|
||||
// new Executor().setExecutable(cmd.outputBundle()).execute();
|
||||
// };
|
||||
BiConsumer<JPackageCommand, Boolean> installExe = (cmd, install) -> {
|
||||
cmd.verifyIsOfType(PackageType.WIN_EXE);
|
||||
Executor exec = new Executor().setExecutable(cmd.outputBundle());
|
||||
if (!install) {
|
||||
exec.addArgument("uninstall");
|
||||
}
|
||||
runMsiexecWithRetries(exec);
|
||||
};
|
||||
|
||||
PackageHandlers exe = new PackageHandlers();
|
||||
exe.installHandler = cmd -> installExe.accept(cmd, true);
|
||||
exe.uninstallHandler = cmd -> installExe.accept(cmd, false);
|
||||
return exe;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user