diff --git a/src/native/interop.cpp b/src/native/interop.cpp index 6320002..de3601d 100644 --- a/src/native/interop.cpp +++ b/src/native/interop.cpp @@ -8,6 +8,7 @@ #include "objbase.h" #include "objidl.h" #include "shlguid.h" +#include "shlobj.h" // https://stackoverflow.com/questions/52101827/windows-10-getsyscolor-does-not-get-dark-ui-color-theme extern "C" int isDarkThemeActive() { @@ -32,60 +33,65 @@ extern "C" int saveShortcut( const char *description, const char *path, const char *args, - const char *workingDir) { - char* errStr = NULL; + const char *workingDir) +{ + char *errStr = NULL; HRESULT h; - IShellLink* shellLink = NULL; - IPersistFile* persistFile = NULL; + IShellLink *shellLink = NULL; + IPersistFile *persistFile = NULL; #ifdef _WIN64 - wchar_t wName[MAX_PATH+1]; + wchar_t wName[MAX_PATH + 1]; #else - WORD wName[MAX_PATH+1]; + WORD wName[MAX_PATH + 1]; #endif int id; // Initialize the COM library h = CoInitialize(NULL); - if (FAILED(h)) { + if (FAILED(h)) + { errStr = "Failed to initialize COM library"; goto err; } - h = CoCreateInstance( CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, - IID_IShellLink, (PVOID*)&shellLink ); - if (FAILED(h)) { + h = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, + IID_IShellLink, (PVOID *)&shellLink); + if (FAILED(h)) + { errStr = "Failed to create IShellLink"; goto err; } - h = shellLink->QueryInterface(IID_IPersistFile, (PVOID*)&persistFile); - if (FAILED(h)) { + h = shellLink->QueryInterface(IID_IPersistFile, (PVOID *)&persistFile); + if (FAILED(h)) + { errStr = "Failed to get IPersistFile"; goto err; } //Append the shortcut name to the folder - MultiByteToWideChar(CP_UTF8,0,shortcutPath,-1,wName,MAX_PATH); + MultiByteToWideChar(CP_UTF8, 0, shortcutPath, -1, wName, MAX_PATH); // Load the file if it exists, to get the values for anything // that we do not set. Ignore errors, such as if it does not exist. h = persistFile->Load(wName, 0); // Set the fields for which the application has set a value - if (description!=NULL) + if (description != NULL) shellLink->SetDescription(description); - if (path!=NULL) + if (path != NULL) shellLink->SetPath(path); - if (args!=NULL) + if (args != NULL) shellLink->SetArguments(args); - if (workingDir!=NULL) + if (workingDir != NULL) shellLink->SetWorkingDirectory(workingDir); //Save the shortcut to disk h = persistFile->Save(wName, TRUE); - if (FAILED(h)) { + if (FAILED(h)) + { errStr = "Failed to save shortcut"; goto err; } @@ -105,3 +111,46 @@ err: return h; } +// NOTE: app and cmdline cannot be constant when Unicode support is enabled +extern "C" int spawnDetached(const char *app, const char *cmdline) +{ + // TODO: Unicode support + STARTUPINFOA si; + PROCESS_INFORMATION pi; + + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); + + if (!CreateProcessA(app, // No module name (use command line) + (LPSTR)cmdline, // Command line, no unicode allowed + NULL, // Process handle not inheritable + NULL, // Thread handle not inheritable + FALSE, // Set handle inheritance to FALSE + CREATE_NO_WINDOW, // Create without window + NULL, // Use parent's environment block + NULL, // Use parent's starting directory + &si, // Pointer to STARTUPINFO structure + &pi) // Pointer to PROCESS_INFORMATION structure + ) + { + return GetLastError(); + } + + // Close process and thread handles. + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return 0; +} + +extern "C" HRESULT getSystemFolder(wchar_t *out_path) +{ + PWSTR path = NULL; + HRESULT result = SHGetKnownFolderPath(FOLDERID_System, 0, NULL, &path); + if (result == S_OK) + { + wcscpy_s(out_path, MAX_PATH + 1, path); + CoTaskMemFree(path); + } + return result; +} diff --git a/src/native/mod.rs b/src/native/mod.rs index 1c2cc0d..dab7f8a 100644 --- a/src/native/mod.rs +++ b/src/native/mod.rs @@ -20,10 +20,10 @@ mod natives { use logging::LoggingErrors; use std::env; - use std::process::Command; use winapi::shared::minwindef::{DWORD, FALSE, MAX_PATH}; use winapi::um::processthreadsapi::OpenProcess; + use winapi::shared::winerror::HRESULT; use winapi::um::psapi::{ EnumProcessModulesEx, GetModuleFileNameExW, K32EnumProcesses, LIST_MODULES_ALL, }; @@ -41,6 +41,15 @@ mod natives { ) -> ::std::os::raw::c_int; pub fn isDarkThemeActive() -> ::std::os::raw::c_uint; + + pub fn spawnDetached( + app: *const ::std::os::raw::c_char, + cmdline: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; + + pub fn getSystemFolder( + out_path: *mut ::std::os::raw::c_ushort + ) -> HRESULT; } // Needed here for Windows interop @@ -109,15 +118,35 @@ mod natives { .log_expect("Unable to convert log path to string") .replace(" ", "\\ "); - let target_arguments = format!("ping 127.0.0.1 -n 3 > nul && del {} {}", tool, log); + let target_arguments = format!("/C choice /C Y /N /D Y /T 2 & del {} {}", tool, log); info!("Launching cmd with {:?}", target_arguments); - Command::new("C:\\Windows\\system32\\cmd.exe") - .arg("/C") - .arg(&target_arguments) - .spawn() - .log_expect("Unable to start child process"); + #[allow(unsafe_code)] + let spawn_result : i32 = unsafe { + let mut cmd_path = [0u16; MAX_PATH + 1]; + let result = getSystemFolder(cmd_path.as_mut_ptr()); + let mut pos = 0; + for x in cmd_path.iter() { + if *x == 0 { + break; + } + pos += 1; + } + if result != winapi::shared::winerror::S_OK { + return; + } + spawnDetached( + CString::new(format!("{}\\cmd.exe", String::from_utf16_lossy(&cmd_path[..pos]))) + .log_expect("Unable to convert Windows system folder name to string") + .as_ptr(), + CString::new(target_arguments).log_expect("Unable to convert arguments").as_ptr(), + ) + }; + + if spawn_result != 0 { + warn!("Unable to start child process"); + } } #[allow(unsafe_code)]