diff options
Diffstat (limited to 'windows/nsis-plugins/src/msiutil/msiutil.cpp')
| -rw-r--r-- | windows/nsis-plugins/src/msiutil/msiutil.cpp | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/windows/nsis-plugins/src/msiutil/msiutil.cpp b/windows/nsis-plugins/src/msiutil/msiutil.cpp new file mode 100644 index 0000000000..4374832505 --- /dev/null +++ b/windows/nsis-plugins/src/msiutil/msiutil.cpp @@ -0,0 +1,210 @@ +#include "stdafx.h" +#include <msi.h> +#include <windows.h> +#include <nsis/pluginapi.h> +#include "../error.h" +#include <log/log.h> +#include <libcommon/string.h> +#include <stdexcept> + +namespace +{ + +std::wstring PopString() +{ + // + // NSIS functions popstring() and popstringn() require that you definitely size the buffer + // before popping the string. Let's do it ourselves instead. + // + + if (!g_stacktop || !*g_stacktop) + { + throw std::runtime_error("NSIS variable stack is corrupted"); + } + + stack_t *th = *g_stacktop; + + std::wstring copy(th->text); + + *g_stacktop = th->next; + GlobalFree((HGLOBAL)th); + + return copy; +} + +EXTERN_C IMAGE_DOS_HEADER __ImageBase; + +void PinDll() +{ + // + // Apparently NSIS loads and unloads the plugin module for EVERY call it makes to the plugin. + // This makes it kind of difficult to maintain state. + // + // We can work around this by incrementing the module reference count. + // When NSIS calls FreeLibrary() the reference count decrements and becomes one. + // + + wchar_t self[MAX_PATH]; + + if (0 == GetModuleFileNameW((HINSTANCE)&__ImageBase, self, _countof(self))) + { + throw std::runtime_error("Failed to pin plugin module"); + } + + // + // For some reason, NSIS frees this particular DLL more times than it loads it + // so we have to up the reference count significantly. + // + for (int i = 0; i < 100; ++i) + { + LoadLibraryW(self); + } +} + +int WINAPI InstallerHandler( + LPVOID context, + UINT type, + LPCWSTR message +) +{ + PluginLog(message); + // return 0 to pass it on to the installer + return 0; +} + +} // anonymous namespace + + +// +// SilentInstall "installer.msi" +// +// Performs a silent install and logs the results. +// +// Return: Empty string and NsisStatus::SUCCESS on success. +// Otherwise an error string and NsisStatus::GENERAL_ERROR. +// + +void __declspec(dllexport) NSISCALL SilentInstall +( + HWND hwndParent, + int string_size, + LPTSTR variables, + stack_t **stacktop, + extra_parameters *extra, + ... +) +{ + EXDLL_INIT(); + + try + { + const auto msiFile = PopString(); + + MsiSetInternalUI(INSTALLUILEVEL_NONE, nullptr); + MsiSetExternalUIW( + InstallerHandler, + INSTALLLOGMODE_INFO | + INSTALLLOGMODE_WARNING | + INSTALLLOGMODE_ERROR | + INSTALLLOGMODE_FATALEXIT | + INSTALLLOGMODE_OUTOFDISKSPACE | + INSTALLLOGMODE_RMFILESINUSE | + INSTALLLOGMODE_FILESINUSE, + nullptr + ); + + const auto installResult = MsiInstallProductW( + msiFile.c_str(), + L"ACTION=INSTALL " + L"REBOOT=ReallySuppress" + ); + + if (ERROR_SUCCESS != installResult) + { + std::wstringstream ss; + ss << L"Install failed: " << installResult; + pushstring(ss.str().c_str()); + pushint(NsisStatus::GENERAL_ERROR); + } + + pushstring(L""); + pushint(NsisStatus::SUCCESS); + } + catch (std::exception & err) + { + pushstring(common::string::ToWide(err.what()).c_str()); + pushint(NsisStatus::GENERAL_ERROR); + } + catch (...) + { + pushstring(L"Unspecified error"); + pushint(NsisStatus::GENERAL_ERROR); + } +} + +// +// SilentUninstall "installer.msi" +// +// Performs a silent uninstall and logs the results. +// +// Return: Empty string and NsisStatus::SUCCESS on success. +// Otherwise an error string and NsisStatus::GENERAL_ERROR. +// + +void __declspec(dllexport) NSISCALL SilentUninstall +( + HWND hwndParent, + int string_size, + LPTSTR variables, + stack_t **stacktop, + extra_parameters *extra, + ... +) +{ + EXDLL_INIT(); + + try + { + const auto msiFile = PopString(); + + MsiSetInternalUI(INSTALLUILEVEL_NONE, nullptr); + MsiSetExternalUIW( + InstallerHandler, + INSTALLLOGMODE_INFO | + INSTALLLOGMODE_WARNING | + INSTALLLOGMODE_ERROR | + INSTALLLOGMODE_FATALEXIT | + INSTALLLOGMODE_OUTOFDISKSPACE | + INSTALLLOGMODE_RMFILESINUSE | + INSTALLLOGMODE_FILESINUSE, + nullptr + ); + + const auto installResult = MsiInstallProductW( + msiFile.c_str(), + L"REMOVE=ALL " + L"REBOOT=ReallySuppress" + ); + + if (ERROR_SUCCESS != installResult) + { + std::wstringstream ss; + ss << L"Install failed: " << installResult; + pushstring(ss.str().c_str()); + pushint(NsisStatus::GENERAL_ERROR); + } + + pushstring(L""); + pushint(NsisStatus::SUCCESS); + } + catch (std::exception & err) + { + pushstring(common::string::ToWide(err.what()).c_str()); + pushint(NsisStatus::GENERAL_ERROR); + } + catch (...) + { + pushstring(L"Unspecified error"); + pushint(NsisStatus::GENERAL_ERROR); + } +} |
