I am trying to create my install which offers users to install both for per user and for all users as well.
Now in case lets say John has installed the software at user level and then Andy is trying to install it as machine level on same system, how can i detect that it is already installed at user level for john and either uninstall that or abort the installation?
As i have different functionalities when it is installed at machine level and user level. I don't want conflicting situation on a machine.
Ideally i want to check if software is installed at user level then install it from user level when installing at machine level.
I Am using InstallShield 2016 for creating installers.
Admin Rights: You need to run the below with admin rights for it to work. Launch Visual Studio with admin rights (right click shortcut and go run as adminstrator).
Retrieve Per-User Installations: Didn't have time to clean this up properly, it is quite "dog's breakfast" in terms of mixing all kinds of string types and I smeared it together from github.com snippets, but this seems to work to find the per-user installations for a specified user SID:
#include "pch.h"
#include <windows.h>
#include "msi.h" // Windows Installer
#include <atlstr.h> // ATL CString
#pragma comment (lib, "msi.lib")
int main()
{
//
// Admin rights required!
//
UINT result = 0;
DWORD dwIndex = 0;
TCHAR szInstalledProductCode[39] = { 0 };
TCHAR szSid[128] = { 0 };
DWORD cchSid;
MSIINSTALLCONTEXT dwInstalledContext;
DWORD cchProductName = MAX_PATH + 1;
WCHAR* lpProductName = new WCHAR[cchProductName];
// Fake, sample SID. Replace:
CString userSID = _T("S-1-5-21-6780625448-452764730-4189743271-1542");
while (ERROR_SUCCESS == (result = MsiEnumProductsEx(NULL, userSID, MSIINSTALLCONTEXT_USERMANAGED | MSIINSTALLCONTEXT_USERUNMANAGED, dwIndex, szInstalledProductCode, &dwInstalledContext, szSid, &cchSid)))
{
UINT uiReturn = MsiGetProductInfoEx(szInstalledProductCode, userSID, dwInstalledContext, INSTALLPROPERTY_INSTALLEDPRODUCTNAME, lpProductName, &cchProductName);
MessageBox(NULL, _T("Product Code: ") + (CString)szInstalledProductCode + _T("\r\n\r\nProduct Name: ") + lpProductName, _T("Product Name:"), MB_OK);
dwIndex++;
}
return 0;
}
Procedure: You need to do a few things before the above has any hope of working.
whoami /user
from the command line as explained here: How to get active session user SID?CString userSID = "S-1-5-21-etc..."
line to have the user SID in question retrieved from your system.In order to get the list of user SIDs from the system I am not sure what to do. If you have a good approach please share.
There are methods and properties available for VBScripting as well that I tried, but I was not able to retrieve other user's packages. I probably just lacks a few parameters, I am not sure.