diff options
author | Simon Rozman <simon@rozman.si> | 2020-11-18 12:21:05 +0100 |
---|---|---|
committer | Jason A. Donenfeld <Jason@zx2c4.com> | 2020-11-18 14:30:02 +0100 |
commit | 4cb5b214f74e269f3656e7caadc11be65767e4e0 (patch) | |
tree | 6c59235c6fc9f02c06224e6245b8040f78587d66 | |
parent | installer: prevent the process killing on upgrade cleanup (diff) | |
download | wireguard-windows-4cb5b214f74e269f3656e7caadc11be65767e4e0.tar.xz wireguard-windows-4cb5b214f74e269f3656e7caadc11be65767e4e0.zip |
installer: refactor custom actions to appear in chronological order
...in the source code.
Signed-off-by: Simon Rozman <simon@rozman.si>
-rw-r--r-- | installer/customactions.c | 390 | ||||
-rw-r--r-- | installer/wireguard.wxs | 28 |
2 files changed, 209 insertions, 209 deletions
diff --git a/installer/customactions.c b/installer/customactions.c index 78ac62d4..b61fd554 100644 --- a/installer/customactions.c +++ b/installer/customactions.c @@ -82,30 +82,106 @@ static void log_errorf(MSIHANDLE installer, enum log_level level, DWORD error_co LocalFree(system_message); } -__declspec(dllexport) UINT __stdcall LaunchApplicationAndAbort(MSIHANDLE installer) +__declspec(dllexport) UINT __stdcall CheckWow64(MSIHANDLE installer) { - UINT ret = ERROR_INSTALL_FAILURE; - TCHAR path[MAX_PATH]; - DWORD path_len = _countof(path); - PROCESS_INFORMATION pi; - STARTUPINFO si = { .cb = sizeof(STARTUPINFO) }; + UINT ret = ERROR_SUCCESS; + bool is_com_initialized = SUCCEEDED(CoInitialize(NULL)); + HMODULE kernel32 = GetModuleHandle(TEXT("kernel32.dll")); + BOOL(WINAPI *IsWow64Process2)(HANDLE hProcess, USHORT *pProcessMachine, USHORT *pNativeMachine); + USHORT process_machine, native_machine; + BOOL is_wow64_process; - ret = MsiGetProperty(installer, TEXT("WireGuardFolder"), path, &path_len); - if (ret != ERROR_SUCCESS) { - log_errorf(installer, LOG_LEVEL_WARN, ret, TEXT("MsiGetProperty(\"WireGuardFolder\") failed")); + if (!kernel32) { + ret = GetLastError(); + log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("Failed to get kernel32.dll handle")); goto out; } - if (!path[0] || !PathAppend(path, TEXT("wireguard.exe"))) + IsWow64Process2 = (void *)GetProcAddress(kernel32, "IsWow64Process2"); + if (IsWow64Process2) { + if (!IsWow64Process2(GetCurrentProcess(), &process_machine, &native_machine)) { + ret = GetLastError(); + log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("Failed to determine Wow64 status from IsWow64Process2")); + goto out; + } + if (process_machine == IMAGE_FILE_MACHINE_UNKNOWN) + goto out; + } else { + if (!IsWow64Process(GetCurrentProcess(), &is_wow64_process)) { + ret = GetLastError(); + log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("Failed to determine Wow64 status from IsWow64Process")); + goto out; + } + if (!is_wow64_process) + goto out; + } + log_messagef(installer, LOG_LEVEL_MSIERR, TEXT("You must use the native version of WireGuard on this computer.")); + ret = ERROR_INSTALL_FAILURE; +out: + if (is_com_initialized) + CoUninitialize(); + return ret; +} + +extern NTAPI __declspec(dllimport) void RtlGetNtVersionNumbers(DWORD *MajorVersion, DWORD *MinorVersion, DWORD *BuildNumber); + +__declspec(dllexport) UINT __stdcall CheckKB2921916(MSIHANDLE installer) +{ + bool is_com_initialized = SUCCEEDED(CoInitialize(NULL)); + UINT ret = ERROR_SUCCESS; + DWORD maj, min, build, len; + HKEY packageKey; + TCHAR subkeyName[0x1000], uiLevel[10]; + MSIHANDLE record; + + RtlGetNtVersionNumbers(&maj, &min, &build); + if (maj != 6 || min != 1) goto out; - log_messagef(installer, LOG_LEVEL_INFO, TEXT("Launching %1"), path); - if (!CreateProcess(path, TEXT("wireguard"), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) { - log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("Failed to create \"%1\" process"), path); + + ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\Packages"), 0, KEY_ENUMERATE_SUB_KEYS, &packageKey); + if (ret != ERROR_SUCCESS) { + log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("Unable to open Component Based Servicing\\Packages registry key")); goto out; } - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); + for (DWORD i = 0;; ++i) { + len = _countof(subkeyName); + ret = RegEnumKeyEx(packageKey, i, subkeyName, &len, NULL, NULL, NULL, NULL); + if (ret == ERROR_NO_MORE_ITEMS) + break; + if (ret == ERROR_MORE_DATA) + continue; + if (ret != ERROR_SUCCESS) { + log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("Unable to enumerate Component Based Servicing\\Packages registry key")); + goto close_key; + } + if (_tcsstr(subkeyName, TEXT("KB2921916"))) + goto close_key; + } + ret = ERROR_INSTALL_FAILURE; + + len = _countof(uiLevel); + if (MsiGetProperty(installer, TEXT("UILevel"), uiLevel, &len) != ERROR_SUCCESS || _tcstoul(uiLevel, NULL, 10) < INSTALLUILEVEL_BASIC) { + log_messagef(installer, LOG_LEVEL_MSIERR, TEXT("Use of WireGuard on Windows 7 requires KB2921916.")); + goto close_key; + } + +#ifdef _WIN64 + static const TCHAR url[] = TEXT("https://download.wireguard.com/windows-toolchain/distfiles/Windows6.1-KB2921916-x64.msu"); +#else + static const TCHAR url[] = TEXT("https://download.wireguard.com/windows-toolchain/distfiles/Windows6.1-KB2921916-x86.msu"); +#endif + record = MsiCreateRecord(2); + MsiRecordSetString(record, 0, TEXT("[1]")); + MsiRecordSetString(record, 1, TEXT("Missing Windows Hotfix\n\nUse of WireGuard on Windows 7 requires KB2921916. Would you like to download the hotfix in your web browser?")); + if (MsiProcessMessage(installer, INSTALLMESSAGE_USER | MB_ICONWARNING | MB_YESNO, record) == IDYES) + ShellExecute(GetForegroundWindow(), NULL, url, NULL, NULL, SW_SHOWNORMAL); + MsiCloseHandle(record); + +close_key: + RegCloseKey(packageKey); out: - return ERROR_INSTALL_USEREXIT; + if (is_com_initialized) + CoUninitialize(); + return ret; } static UINT insert_service_control(MSIHANDLE installer, MSIHANDLE view, const TCHAR *service_name, bool start) @@ -156,62 +232,6 @@ out: return ret; } -static bool remove_directory_recursive(MSIHANDLE installer, TCHAR path[MAX_PATH], unsigned int max_depth) -{ - HANDLE find_handle; - WIN32_FIND_DATA find_data; - TCHAR *path_end; - - if (!max_depth) { - log_messagef(installer, LOG_LEVEL_WARN, TEXT("Too many levels of nesting at \"%1\""), path); - return false; - } - - path_end = path + _tcsnlen(path, MAX_PATH); - if (!PathAppend(path, TEXT("*.*"))) { - log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("PathAppend(\"%1\", \"*.*\") failed"), path); - return false; - } - find_handle = FindFirstFileEx(path, FindExInfoBasic, &find_data, FindExSearchNameMatch, NULL, 0); - if (find_handle == INVALID_HANDLE_VALUE) { - log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("FindFirstFileEx(\"%1\") failed"), path); - return false; - } - do { - if (find_data.cFileName[0] == TEXT('.') && (find_data.cFileName[1] == TEXT('\0') || (find_data.cFileName[1] == TEXT('.') && find_data.cFileName[2] == TEXT('\0')))) - continue; - - path_end[0] = TEXT('\0'); - if (!PathAppend(path, find_data.cFileName)) { - log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("PathAppend(\"%1\", \"%2\") failed"), path, find_data.cFileName); - continue; - } - - if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - remove_directory_recursive(installer, path, max_depth - 1); - continue; - } - - if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) && !SetFileAttributes(path, find_data.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY)) - log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("SetFileAttributes(\"%1\") failed"), path); - - if (DeleteFile(path)) - log_messagef(installer, LOG_LEVEL_INFO, TEXT("Deleted \"%1\""), path); - else - log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("DeleteFile(\"%1\") failed"), path); - } while (FindNextFile(find_handle, &find_data)); - FindClose(find_handle); - - path_end[0] = TEXT('\0'); - if (RemoveDirectory(path)) { - log_messagef(installer, LOG_LEVEL_INFO, TEXT("Removed \"%1\""), path); - return true; - } else { - log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("RemoveDirectory(\"%1\") failed"), path); - return false; - } -} - __declspec(dllexport) UINT __stdcall EvaluateWireGuardServices(MSIHANDLE installer) { UINT ret = ERROR_INSTALL_FAILURE; @@ -285,6 +305,32 @@ out: return ret == ERROR_SUCCESS ? ret : ERROR_INSTALL_FAILURE; } +__declspec(dllexport) UINT __stdcall LaunchApplicationAndAbort(MSIHANDLE installer) +{ + UINT ret = ERROR_INSTALL_FAILURE; + TCHAR path[MAX_PATH]; + DWORD path_len = _countof(path); + PROCESS_INFORMATION pi; + STARTUPINFO si = { .cb = sizeof(STARTUPINFO) }; + + ret = MsiGetProperty(installer, TEXT("WireGuardFolder"), path, &path_len); + if (ret != ERROR_SUCCESS) { + log_errorf(installer, LOG_LEVEL_WARN, ret, TEXT("MsiGetProperty(\"WireGuardFolder\") failed")); + goto out; + } + if (!path[0] || !PathAppend(path, TEXT("wireguard.exe"))) + goto out; + log_messagef(installer, LOG_LEVEL_INFO, TEXT("Launching %1"), path); + if (!CreateProcess(path, TEXT("wireguard"), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) { + log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("Failed to create \"%1\" process"), path); + goto out; + } + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); +out: + return ERROR_INSTALL_USEREXIT; +} + __declspec(dllexport) UINT __stdcall EvaluateWireGuardComponents(MSIHANDLE installer) { UINT ret = ERROR_INSTALL_FAILURE; @@ -337,27 +383,6 @@ out: return ret == ERROR_SUCCESS ? ret : ERROR_INSTALL_FAILURE; } -__declspec(dllexport) UINT __stdcall RemoveConfigFolder(MSIHANDLE installer) -{ - LSTATUS ret; - TCHAR path[MAX_PATH]; - DWORD path_len = _countof(path); - bool is_com_initialized = SUCCEEDED(CoInitialize(NULL)); - - ret = MsiGetProperty(installer, TEXT("CustomActionData"), path, &path_len); - if (ret != ERROR_SUCCESS) { - log_errorf(installer, LOG_LEVEL_WARN, ret, TEXT("MsiGetProperty(\"CustomActionData\") failed")); - goto out; - } - if (!path[0] || !PathAppend(path, TEXT("Data"))) - goto out; - remove_directory_recursive(installer, path, 10); -out: - if (is_com_initialized) - CoUninitialize(); - return ERROR_SUCCESS; -} - struct file_id { DWORD volume, index_high, index_low; }; static bool calculate_file_id(const TCHAR *path, struct file_id *id) @@ -446,6 +471,83 @@ out: return ERROR_SUCCESS; } +static bool remove_directory_recursive(MSIHANDLE installer, TCHAR path[MAX_PATH], unsigned int max_depth) +{ + HANDLE find_handle; + WIN32_FIND_DATA find_data; + TCHAR *path_end; + + if (!max_depth) { + log_messagef(installer, LOG_LEVEL_WARN, TEXT("Too many levels of nesting at \"%1\""), path); + return false; + } + + path_end = path + _tcsnlen(path, MAX_PATH); + if (!PathAppend(path, TEXT("*.*"))) { + log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("PathAppend(\"%1\", \"*.*\") failed"), path); + return false; + } + find_handle = FindFirstFileEx(path, FindExInfoBasic, &find_data, FindExSearchNameMatch, NULL, 0); + if (find_handle == INVALID_HANDLE_VALUE) { + log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("FindFirstFileEx(\"%1\") failed"), path); + return false; + } + do { + if (find_data.cFileName[0] == TEXT('.') && (find_data.cFileName[1] == TEXT('\0') || (find_data.cFileName[1] == TEXT('.') && find_data.cFileName[2] == TEXT('\0')))) + continue; + + path_end[0] = TEXT('\0'); + if (!PathAppend(path, find_data.cFileName)) { + log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("PathAppend(\"%1\", \"%2\") failed"), path, find_data.cFileName); + continue; + } + + if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + remove_directory_recursive(installer, path, max_depth - 1); + continue; + } + + if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) && !SetFileAttributes(path, find_data.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY)) + log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("SetFileAttributes(\"%1\") failed"), path); + + if (DeleteFile(path)) + log_messagef(installer, LOG_LEVEL_INFO, TEXT("Deleted \"%1\""), path); + else + log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("DeleteFile(\"%1\") failed"), path); + } while (FindNextFile(find_handle, &find_data)); + FindClose(find_handle); + + path_end[0] = TEXT('\0'); + if (RemoveDirectory(path)) { + log_messagef(installer, LOG_LEVEL_INFO, TEXT("Removed \"%1\""), path); + return true; + } else { + log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("RemoveDirectory(\"%1\") failed"), path); + return false; + } +} + +__declspec(dllexport) UINT __stdcall RemoveConfigFolder(MSIHANDLE installer) +{ + LSTATUS ret; + TCHAR path[MAX_PATH]; + DWORD path_len = _countof(path); + bool is_com_initialized = SUCCEEDED(CoInitialize(NULL)); + + ret = MsiGetProperty(installer, TEXT("CustomActionData"), path, &path_len); + if (ret != ERROR_SUCCESS) { + log_errorf(installer, LOG_LEVEL_WARN, ret, TEXT("MsiGetProperty(\"CustomActionData\") failed")); + goto out; + } + if (!path[0] || !PathAppend(path, TEXT("Data"))) + goto out; + remove_directory_recursive(installer, path, 10); +out: + if (is_com_initialized) + CoUninitialize(); + return ERROR_SUCCESS; +} + __declspec(dllexport) UINT __stdcall RemoveAdapters(MSIHANDLE installer) { UINT ret; @@ -511,105 +613,3 @@ out: CoUninitialize(); return ERROR_SUCCESS; } - -__declspec(dllexport) UINT __stdcall CheckWow64(MSIHANDLE installer) -{ - UINT ret = ERROR_SUCCESS; - bool is_com_initialized = SUCCEEDED(CoInitialize(NULL)); - HMODULE kernel32 = GetModuleHandle(TEXT("kernel32.dll")); - BOOL(WINAPI *IsWow64Process2)(HANDLE hProcess, USHORT *pProcessMachine, USHORT *pNativeMachine); - USHORT process_machine, native_machine; - BOOL is_wow64_process; - - if (!kernel32) { - ret = GetLastError(); - log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("Failed to get kernel32.dll handle")); - goto out; - } - IsWow64Process2 = (void *)GetProcAddress(kernel32, "IsWow64Process2"); - if (IsWow64Process2) { - if (!IsWow64Process2(GetCurrentProcess(), &process_machine, &native_machine)) { - ret = GetLastError(); - log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("Failed to determine Wow64 status from IsWow64Process2")); - goto out; - } - if (process_machine == IMAGE_FILE_MACHINE_UNKNOWN) - goto out; - } else { - if (!IsWow64Process(GetCurrentProcess(), &is_wow64_process)) { - ret = GetLastError(); - log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("Failed to determine Wow64 status from IsWow64Process")); - goto out; - } - if (!is_wow64_process) - goto out; - } - log_messagef(installer, LOG_LEVEL_MSIERR, TEXT("You must use the native version of WireGuard on this computer.")); - ret = ERROR_INSTALL_FAILURE; -out: - if (is_com_initialized) - CoUninitialize(); - return ret; -} - -extern NTAPI __declspec(dllimport) void RtlGetNtVersionNumbers(DWORD *MajorVersion, DWORD *MinorVersion, DWORD *BuildNumber); - -__declspec(dllexport) UINT __stdcall CheckKB2921916(MSIHANDLE installer) -{ - bool is_com_initialized = SUCCEEDED(CoInitialize(NULL)); - UINT ret = ERROR_SUCCESS; - DWORD maj, min, build, len; - HKEY packageKey; - TCHAR subkeyName[0x1000], uiLevel[10]; - MSIHANDLE record; - - RtlGetNtVersionNumbers(&maj, &min, &build); - if (maj != 6 || min != 1) - goto out; - - ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\Packages"), 0, KEY_ENUMERATE_SUB_KEYS, &packageKey); - if (ret != ERROR_SUCCESS) { - log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("Unable to open Component Based Servicing\\Packages registry key")); - goto out; - } - for (DWORD i = 0;; ++i) { - len = _countof(subkeyName); - ret = RegEnumKeyEx(packageKey, i, subkeyName, &len, NULL, NULL, NULL, NULL); - if (ret == ERROR_NO_MORE_ITEMS) - break; - if (ret == ERROR_MORE_DATA) - continue; - if (ret != ERROR_SUCCESS) { - log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("Unable to enumerate Component Based Servicing\\Packages registry key")); - goto close_key; - } - if (_tcsstr(subkeyName, TEXT("KB2921916"))) - goto close_key; - } - ret = ERROR_INSTALL_FAILURE; - - len = _countof(uiLevel); - if (MsiGetProperty(installer, TEXT("UILevel"), uiLevel, &len) != ERROR_SUCCESS || _tcstoul(uiLevel, NULL, 10) < INSTALLUILEVEL_BASIC) { - log_messagef(installer, LOG_LEVEL_MSIERR, TEXT("Use of WireGuard on Windows 7 requires KB2921916.")); - goto close_key; - } - -#ifdef _WIN64 - static const TCHAR url[] = TEXT("https://download.wireguard.com/windows-toolchain/distfiles/Windows6.1-KB2921916-x64.msu"); -#else - static const TCHAR url[] = TEXT("https://download.wireguard.com/windows-toolchain/distfiles/Windows6.1-KB2921916-x86.msu"); -#endif - record = MsiCreateRecord(2); - MsiRecordSetString(record, 0, TEXT("[1]")); - MsiRecordSetString(record, 1, TEXT("Missing Windows Hotfix\n\nUse of WireGuard on Windows 7 requires KB2921916. Would you like to download the hotfix in your web browser?")); - if (MsiProcessMessage(installer, INSTALLMESSAGE_USER | MB_ICONWARNING | MB_YESNO, record) == IDYES) - ShellExecute(GetForegroundWindow(), NULL, url, NULL, NULL, SW_SHOWNORMAL); - MsiCloseHandle(record); - -close_key: - RegCloseKey(packageKey); -out: - if (is_com_initialized) - CoUninitialize(); - return ret; -} diff --git a/installer/wireguard.wxs b/installer/wireguard.wxs index 31f57426..ba0ee46e 100644 --- a/installer/wireguard.wxs +++ b/installer/wireguard.wxs @@ -110,19 +110,19 @@ </InstallExecuteSequence> <!-- - Launch wireguard.exe on product reconfiguration (starting same MSI again) + Evaluate WireGuard services and populate ServiceControl table --> - <CustomAction Id="LaunchApplicationAndAbort" BinaryKey="customactions.dll" DllEntry="LaunchApplicationAndAbort" /> + <CustomAction Id="EvaluateWireGuardServices" BinaryKey="customactions.dll" DllEntry="EvaluateWireGuardServices" /> <InstallExecuteSequence> - <Custom Action="LaunchApplicationAndAbort" After="CostFinalize">ProductState=5 AND NOT REMOVE AND NOT DO_NOT_LAUNCH</Custom> + <Custom Action="EvaluateWireGuardServices" After="FindRelatedProducts" /> </InstallExecuteSequence> <!-- - Evaluate WireGuard services and populate ServiceControl table + Launch wireguard.exe on product reconfiguration (starting same MSI again) --> - <CustomAction Id="EvaluateWireGuardServices" BinaryKey="customactions.dll" DllEntry="EvaluateWireGuardServices" /> + <CustomAction Id="LaunchApplicationAndAbort" BinaryKey="customactions.dll" DllEntry="LaunchApplicationAndAbort" /> <InstallExecuteSequence> - <Custom Action="EvaluateWireGuardServices" After="FindRelatedProducts" /> + <Custom Action="LaunchApplicationAndAbort" After="CostFinalize">ProductState=5 AND NOT REMOVE AND NOT DO_NOT_LAUNCH</Custom> </InstallExecuteSequence> <!-- @@ -134,6 +134,14 @@ </InstallExecuteSequence> <!-- + Kill lingering processes + --> + <CustomAction Id="KillWireGuardProcesses" BinaryKey="customactions.dll" DllEntry="KillWireGuardProcesses" Execute="deferred" Impersonate="no" /> + <InstallExecuteSequence> + <Custom Action="KillWireGuardProcesses" After="StopServices" /> + </InstallExecuteSequence> + + <!-- Clear out our config folder on uninstall --> <CustomAction Id="RemoveConfigFolder" BinaryKey="customactions.dll" DllEntry="RemoveConfigFolder" Execute="deferred" Impersonate="no" /> @@ -150,14 +158,6 @@ </InstallExecuteSequence> <!-- - Kill lingering processes - --> - <CustomAction Id="KillWireGuardProcesses" BinaryKey="customactions.dll" DllEntry="KillWireGuardProcesses" Execute="deferred" Impersonate="no" /> - <InstallExecuteSequence> - <Custom Action="KillWireGuardProcesses" After="StopServices" /> - </InstallExecuteSequence> - - <!-- Launch wireguard.exe after setup complete --> <CustomAction Id="LaunchApplication" HideTarget="yes" Impersonate="no" Execute="deferred" FileKey="wireguard.exe" ExeCommand="" Return="asyncNoWait" /> |