[Openvpn-devel] Add a MSI custom actions to close and relaunch OpenVPN GUI
Commit Message
This custom actions are used by MSI setup to close OpenVPN GUI before
performing an upgrade and relaunch it afterwards.
---
src/openvpnmsica/Makefile.am | 2 +-
src/openvpnmsica/openvpnmsica.c | 102 ++++++++++++++++++++++++++++++++
src/openvpnmsica/openvpnmsica.h | 25 ++++++++
3 files changed, 128 insertions(+), 1 deletion(-)
Comments
Acked-by: Gert Doering <gert@greenie.muc.de>
Looks reasonable (the big picture "how does the installer know if there
is a GUI in the first place? does it ask?" is missing, but I assume this
is in the WiX pieces). Test compiled on Ubuntu 16.04/mingw.
Your patch has been applied to the master branch.
commit 597972bcc3124964df3ccc484df4c0efa6ec0dbc
Author: Simon Rozman
Date: Tue Nov 27 01:28:08 2018 +0100
Add a MSI custom actions to close and relaunch OpenVPN GUI
Acked-by: Gert Doering <gert@greenie.muc.de>
Message-Id: <20181127002808.19200-1-simon@rozman.si>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg17959.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>
--
kind regards,
Gert Doering
@@ -41,7 +41,7 @@ libopenvpnmsica_la_CFLAGS = \
-municode -D_UNICODE \
-UNTDDI_VERSION -U_WIN32_WINNT \
-D_WIN32_WINNT=_WIN32_WINNT_VISTA
-libopenvpnmsica_la_LDFLAGS = -ladvapi32 -lole32 -lmsi -lsetupapi -liphlpapi -lshlwapi -lversion -no-undefined -avoid-version
+libopenvpnmsica_la_LDFLAGS = -ladvapi32 -lole32 -lmsi -lsetupapi -liphlpapi -lshell32 -lshlwapi -lversion -no-undefined -avoid-version
endif
libopenvpnmsica_la_SOURCES = \
@@ -37,6 +37,7 @@
#include <malloc.h>
#include <memory.h>
#include <msiquery.h>
+#include <shellapi.h>
#include <shlwapi.h>
#include <stdbool.h>
#include <stdlib.h>
@@ -44,6 +45,7 @@
#ifdef _MSC_VER
#pragma comment(lib, "iphlpapi.lib")
+#pragma comment(lib, "shell32.lib")
#pragma comment(lib, "shlwapi.lib")
#pragma comment(lib, "version.lib")
#endif
@@ -475,6 +477,106 @@ cleanup_CoInitialize:
}
+UINT __stdcall
+CloseOpenVPNGUI(_In_ MSIHANDLE hInstall)
+{
+#ifdef _MSC_VER
+#pragma comment(linker, DLLEXP_EXPORT)
+#endif
+ UNREFERENCED_PARAMETER(hInstall); /* This CA is does not interact with MSI session (report errors, access properties, tables, etc.). */
+
+ openvpnmsica_debug_popup(TEXT(__FUNCTION__));
+
+ /* Find OpenVPN GUI window. */
+ HWND hWnd = FindWindow(TEXT("OpenVPN-GUI"), NULL);
+ if (hWnd)
+ {
+ /* Ask it to close and wait for 100ms. Unfortunately, this will succeed only for recent OpenVPN GUI that do not run elevated. */
+ SendMessage(hWnd, WM_CLOSE, 0, 0);
+ Sleep(100);
+ }
+
+ return ERROR_SUCCESS;
+}
+
+
+UINT __stdcall
+StartOpenVPNGUI(_In_ MSIHANDLE hInstall)
+{
+#ifdef _MSC_VER
+#pragma comment(linker, DLLEXP_EXPORT)
+#endif
+
+ openvpnmsica_debug_popup(TEXT(__FUNCTION__));
+
+ UINT uiResult;
+ BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
+
+ /* Set MSI session handle in TLS. */
+ struct openvpnmsica_tls_data *s = (struct openvpnmsica_tls_data *)TlsGetValue(openvpnmsica_tlsidx_session);
+ s->hInstall = hInstall;
+
+ /* Create and populate a MSI record. */
+ MSIHANDLE hRecord = MsiCreateRecord(1);
+ if (!hRecord)
+ {
+ uiResult = ERROR_INVALID_HANDLE;
+ msg(M_NONFATAL, "%s: MsiCreateRecord failed", __FUNCTION__);
+ goto cleanup_CoInitialize;
+ }
+ uiResult = MsiRecordSetString(hRecord, 0, TEXT("\"[#bin.openvpn_gui.exe]\""));
+ if (uiResult != ERROR_SUCCESS)
+ {
+ SetLastError(uiResult); /* MSDN does not mention MsiRecordSetString() to set GetLastError(). But we do have an error code. Set last error manually. */
+ msg(M_NONFATAL | M_ERRNO, "%s: MsiRecordSetString failed", __FUNCTION__);
+ goto cleanup_MsiCreateRecord;
+ }
+
+ /* Format string. */
+ TCHAR szStackBuf[MAX_PATH];
+ DWORD dwPathSize = _countof(szStackBuf);
+ LPTSTR szPath = szStackBuf;
+ uiResult = MsiFormatRecord(hInstall, hRecord, szPath, &dwPathSize);
+ if (uiResult == ERROR_MORE_DATA)
+ {
+ /* Allocate buffer on heap (+1 for terminator), and retry. */
+ szPath = (LPTSTR)malloc((++dwPathSize) * sizeof(TCHAR));
+ uiResult = MsiFormatRecord(hInstall, hRecord, szPath, &dwPathSize);
+ }
+ if (uiResult != ERROR_SUCCESS)
+ {
+ SetLastError(uiResult); /* MSDN does not mention MsiFormatRecord() to set GetLastError(). But we do have an error code. Set last error manually. */
+ msg(M_NONFATAL | M_ERRNO, "%s: MsiFormatRecord failed", __FUNCTION__);
+ goto cleanup_malloc_szPath;
+ }
+
+ /* Launch the OpenVPN GUI. */
+ SHELLEXECUTEINFO sei = {
+ .cbSize = sizeof(SHELLEXECUTEINFO),
+ .fMask = SEE_MASK_FLAG_NO_UI, /* Don't show error UI, we'll display it. */
+ .lpFile = szPath,
+ .nShow = SW_SHOWNORMAL
+ };
+ if (!ShellExecuteEx(&sei))
+ {
+ uiResult = GetLastError();
+ msg(M_NONFATAL | M_ERRNO, "%s: ShellExecuteEx(%s) failed", __FUNCTION__, szPath);
+ goto cleanup_malloc_szPath;
+ }
+
+ uiResult = ERROR_SUCCESS;
+
+cleanup_malloc_szPath:
+ if (szPath != szStackBuf)
+ free(szPath);
+cleanup_MsiCreateRecord:
+ MsiCloseHandle(hRecord);
+cleanup_CoInitialize:
+ if (bIsCoInitialized) CoUninitialize();
+ return uiResult;
+}
+
+
UINT __stdcall
EvaluateTAPInterfaces(_In_ MSIHANDLE hInstall)
{
@@ -91,6 +91,31 @@ DLLEXP_DECL UINT __stdcall
FindTAPInterfaces(_In_ MSIHANDLE hInstall);
+/**
+ * Find OpenVPN GUI window and send it a WM_CLOSE message.
+ *
+ * @param hInstall Handle to the installation provided to the DLL custom action
+ *
+ * @return ERROR_SUCCESS on success; An error code otherwise
+ * See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa368072.aspx
+ */
+DLLEXP_DECL UINT __stdcall
+CloseOpenVPNGUI(_In_ MSIHANDLE hInstall);
+
+
+/**
+ * Launches OpenVPN GUI. It's path is obtained by expanding the `[#bin.openvpn_gui.exe]`
+ * therefore, its Id field in File table must be "bin.openvpn_gui.exe".
+ *
+ * @param hInstall Handle to the installation provided to the DLL custom action
+ *
+ * @return ERROR_SUCCESS on success; An error code otherwise
+ * See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa368072.aspx
+ */
+DLLEXP_DECL UINT __stdcall
+StartOpenVPNGUI(_In_ MSIHANDLE hInstall);
+
+
/**
* Evaluate the TAPInterface table of the MSI package database and prepare a list of TAP
* interfaces to install/remove.