50% OFF!!!

Showing posts with label msi. Show all posts
Showing posts with label msi. Show all posts

Thursday, February 10, 2011

MSI | Embed images inside MSI installer file

In this post I show how to embed images inside MSI installer file.

The first try i made was to try adding splash image and banners to the installer through Visual-Studio User-Interface. (just added two jpg files into "Common Files Folder", and set the SplashBitmap and BannerBitmap attributes). But doing so, created a problem - when changing the image, new version installer did NOT changed the images (or the images disappeared).
So i needed another solution...

The next solution is just to INSERT those images (banner&splash) directly into the MSI file created by Visual-Studio.
I created file run.js file, which automaticlly runs when the build process finished, and edits the MSI. This is achieved by setting the PostBuildEvent on the Windows Installer project.

The event that added is to the PostBuildEvent: (remember to copy the file to the main folder of the Windows Installer project)
cscript.exe "$(ProjectDir)run.js" "$(BuiltOuputPath)"

In this way, you don't have to add any image file to your project, and it s embedded in the msi file (which u can edit any time).

The run.js file:
// run.js // Performs a post-build fixup of an msi

if (WScript.Arguments.Length != 1)
{
       WScript.StdErr.WriteLine(WScript.ScriptName + " file");
       WScript.Quit(1);
}

var filespec = WScript.Arguments(0);
var installer = WScript.CreateObject("WindowsInstaller.Installer");
var database = installer.OpenDatabase(filespec, 1/*msiOpenDatabaseModeTransact*/);

var sql
var view
var record

try {

    WScript.Echo("UPDATE default banner image");

    sql = "UPDATE `Binary` SET `Data` = ? WHERE `Name`= 'DefBannerBitmap'";
    view = database.OpenView(sql)
    record = installer.CreateRecord(1);
    record.SetStream(1, "C:\\installer_banner_img.jpg");
    view.Execute(record);
    view.Close();
       WScript.Echo("ADD splash image");
    sql = "INSERT INTO `Binary` (`Name`, `Data`) VALUES ('DefSplashBitmap', ?)";
    view = database.OpenView(sql)
    record = installer.CreateRecord(1);
    record.SetStream(1, "C:\\Installer_splash_img.jpg");
    view.Execute(record);
    view.Close();

    WScript.Echo("Update splash control");
    sql = "UPDATE `Control` SET `Text`='DefSplashBitmap' WHERE `Dialog_`='SplashForm' AND `Control`='SplashBmp'";
    view = database.OpenView(sql)
    view.Execute();
    view.Close();
       database.Commit();
}
catch (e) {
    WScript.StdErr.Write("|||ERROR||| ");
    WScript.StdErr.WriteLine(e.Message);
    WScript.Quit(1);
}

Note:
Before running this script, add a splash screen to the installer (using User-Interface view in Visual Studio)

(*) this is also a good example how to add/update binary(image) into your MSI using a sql script.

Best regard,
MDB-Blog

Wednesday, December 8, 2010

C# | Check if app is installed for “All users” or "Just me"


Today I bring a very HARD-to-find topic about getting an installed application "Installation context".
An installation context may be Per-Machine Installation Context (ALLUSERS=1) and Per-User Installation Context (ALLUSERS="").

When installing an application, the user may select one of two options:
a. Install [APP] for yourself
b. or for anyone who uses this computer

[a] installs only for the current user(just me) while [b] installs for the Local System user which is for all-users(everyone).

In this post I will give a code for checking is an installed application was installed for "Everyone" or "Just me".

The validation process is check if the APPLICATION-NAME exists in the registry keys under UserData.
(*) Everyone - check under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\ (S-1-5-18 represents the Local System user)

(*) Just me (Current user) - check under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-?!?!?!?!\Products\ (S-?!?!?!?! represents the Current user. this value found by the code: WindowsIdentity.GetCurrent().User.Value.


[Code] Method's return type:
private enum InstallationContexts { NotInstalled, Everyone, JustMe };

[Code] Method declaration:
private InstallationContexts GetInstalledContext(string p_appDisplayName)
{
    //The S-1-5-18  =
Local System user (Everyone) | HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\

    //The S-XXXXXX  =
Current user (Just me)       |
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-?!?!?!?!\Products\

    string key;
    string keyFormat = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\{0}\Products\";
 
    // Check if installed for -> Everyone
    key = string.Format(keyFormat, "S-1-5-18");
    bool res = GetInstalledContext_IsRegKeyExists(key, p_appDisplayName, StringComparison.OrdinalIgnoreCase);
    if (res == true)
    {
        return InstallationContexts.Everyone;
    }
 
    // Check if installed for -> Just me
    key = string.Format(keyFormat, WindowsIdentity.GetCurrent().User.Value);
    res = GetInstalledContext_IsRegKeyExists(key, p_appDisplayName, StringComparison.OrdinalIgnoreCase);
    if (res == true)
    {
        return InstallationContexts.JustMe;
    }
 
    return InstallationContexts.NotInstalled;
}
private bool GetInstalledContext_IsRegKeyExists(string p_regKey, string p_appDisplayName, StringComparison p_scompare)
{
    using (RegistryKey regkey = Registry.LocalMachine.OpenSubKey(p_regKey))
    {
        if (regkey != null)
        {
            RegistryKey rk;
            string[] arrProducs = regkey.GetSubKeyNames();
            for (int i = 0; i < arrProducs.Length; i++)
            {

               using (rk = regkey.OpenSubKey(arrProducs[i] + @"\InstallProperties"))
               {
if (rk != null &&
                        p_appDisplayName.Equals(rk.GetValue("DisplayName").ToString(), p_scompare) == true)
                   {
                        return true;
                   }
               }
            }
        }
    }
 
    return false;
}

[Code] Method's usage example:
InstallationContexts appContext = GetInstalledContext("APP-DISPLAY-NAME");

This code gives the ability to detect previously installed application or previously installed software is installed to ALLUSERS (everyone) or only to CURRENT USER (just me).



Addition on 19.12.2010:
(*) This checking operation is valid ONLY for an application and software which were installed using Microsoft's Windows Installer.
For general checking for installed application, check my post about check if program/application is installed.


Enjoy...
MDB-Blog