I have to write an auto update service that updates our companies application(s) on our client PCs. One of the applications to update is the updater itself. I deploy all applications with a MSI packages created with WIX.
The service then spanws a process with "msiexec.exe /q /i " to start a silent install.
This works fine for the other products, but when I want to update the running service, the service is the one which started the process calling the installer. Hence I am trying to update a running process.
How would I go about this? "Fork" the installer process and exit the service? Use some clever Windows built-in method?
Thanks for the input, here is what I came up with:
I am using a WIX installer with MajorUpgrade support and a ServiceInstall element to install the new service. This will cause MSI to stop the service and upgrade the installation.
Now, to update the service from within I need to start the installer asynchronously and then allow the running service to be stopped.
Basically we need to call:
msiexec /package path_to_msi /quiet
Since CreateProcess needs the full path the executable, I use SHGetKnownFolderPath to retrieve the SYSTEM32 path on the system
// note: FOLDERID_SystemX86 will return 32 bit version of system32 regardless of application type
PWSTR str = nullptr;
if (SHGetKnownFolderPath(FOLDERID_SystemX86, KF_FLAG_DEFAULT, NULL, &str) != S_OK)
throw std::runtime_error("failed to retrieve FOLDERID_SystemX86");
std::string exe = ...path to msiexec...;
std::string options = " /package \"path_to_msi\" /quiet";
Now, we start the process:
// start process
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
if (!CreateProcess(exe.c_str(), // application name
options.c_str(), // Command line options
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi)) // Pointer to PROCESS_INFORMATION structure
throw std::runtime_error("CreateProcess failed");
And we're done.
The installer will now signal the service to stop, make sure this is handled properly!
The new service will be installed and hopefully be back in action in matter of seconds.
Done ;-)
If anyone needs more details, just ask away.