Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MSI/WIX: How to (self-)update a running service

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?

like image 440
uceumern Avatar asked Oct 22 '25 21:10

uceumern


1 Answers

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.

like image 117
uceumern Avatar answered Oct 25 '25 12:10

uceumern