oleg-shilo / wixsharp

Framework for building a complete MSI or WiX source code by using script files written with C# syntax.
MIT License
1.12k stars 175 forks source link

[Question] Get installed Version of programm #1645

Closed Torchok19081986 closed 1 month ago

Torchok19081986 commented 2 months ago

Morning, i ran into problem in MSI , if i want to get installed version of programm. I added following code to project.load


Version mindVersion = new Version(24, 1); <--- this line

if (installedcobraVersion.Major == mindVersion.Major && installedcobraVersion.Minor == mindVersion.Minor)
{
    e.Session.Log("Compatible Versions. Installation of plugin can continue. ");
}
else
{
    e.Session.Log("Not compatible Versions. Installation of plugin cant continue. ");

    e.ManagedUI.Shell.ErrorDetected = true;

}

Any posibility to set Version mindVersion = new Version(24, 1) line of code for searchversion not const or hardcoded ? xml, config json - Files or something else ?

Many thanks for suggestions.

oleg-shilo commented 2 months ago

You can use AppSearch class for that. It is a single-level wrapper around MSI interop: image

Torchok19081986 commented 2 months ago

ok, thanks again. I dont know Upgrade code for programm. Only Name of .exe. If this app installed, it set registrykeys and create SystemDir Key with path to .exe. For search of version , i need upgrade or productcode, which i dont have. I done it by registery read and it works, but my struggle is here dynamically check this exe. I dont know if somehow possbile add this dynamically check. Config or Manifest File is not possbile in msi.

oleg-shilo commented 2 months ago

you can always read the manifest file if you have access to it. and if you are about a binary file version then you can always use FileVersionInfo

Torchok19081986 commented 2 months ago

morning, i do until now nothing with manifest file for msi. How can i add it to managed project in WixSharp ? I had tried also add .condig file to current project, but my testoutput msi, doesnt want read it. I added .config file from Visual Studio 2022 Project in Tab where also i can internal variables. Standard is value "String 1" . Dont know, what name was it. 😵‍💫😵‍💫😵‍💫

Torchok19081986 commented 2 months ago

after some tries and long research , i solve my problem mit embeded xml file. Code is a mess, but works. Liitle bit overhead, but it works and for my purposes is enough. I poste here, if anyone need it.


public static void Project_Load(SetupEventArgs e)
{

    bool isfileExtractin = ExtractFile(e);

    if(isfileExtractin == true)
    {
        string tempPath = Path.GetTempPath();

        string targetFilePath = Path.Combine(tempPath, "<embededFile>.xml");

        string appVersion = ReadCobraVersionXml(targetFilePath, e);

        Version allowed_version = Version.Parse(appVersion);

        if (allowed_version != null)
        {
            CheckCobraCompatibilitaetFuerInstallation(allowed_version, e);
        }
        else
        {
            e.Session.Log("Version von den installierten Programm wurde nicht bestimmt.");
            e.ManagedUI.Shell.ErrorDetected = true;
        }

    }
    else
    {
        e.Session.Log("Embeded File wurde nicht gefunden.");
        e.ManagedUI.Shell.ErrorDetected = true;
    }

}

private static bool ExtractFile(SetupEventArgs e)
{
    try
    {
        // Get the path to the temporary folder
        string tempPath = Path.GetTempPath();

        // Define the target path for the extracted JSON file
        string targetFilePath = Path.Combine(tempPath, "embeded-File.xml");

        // Get the current assembly (your custom action DLL)
        var assembly = System.Reflection.Assembly.GetExecutingAssembly();

        // Extract the JSON file from the embedded resources
        using (Stream stream = assembly.GetManifestResourceStream("NameSpace.Resources.Embeded_File.xml"))
        {
            if (stream == null)
            {
                throw new FileNotFoundException("Embedded resource not found.");
            }

            using (var fileStream = new FileStream(targetFilePath, FileMode.Create, FileAccess.Write))
            {
                stream.CopyTo(fileStream);
            }
        }

        e.Session.Log($"XML file extracted to: {targetFilePath}");

        return true;
    }
    catch (Exception ex)
    {
        e.Session.Log($"Error in ExtractJsonFile: {ex.Message}");
        e.ManagedUI.Shell.ErrorDetected = true;

        return false;
    }
}

private static void CheckCobraCompatibilitaetFuerInstallation(Version v,SetupEventArgs e)
{
    try
    {

        RegistryKey localMachine;

        localMachine = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default);

        RegistryKey appKey = localMachine.OpenSubKey(@"SOFTWARE\app");

        if (appKey != null)
        {
            string ProgramDir = appKey.GetValue("ProgramDir").ToString();

            //MessageBox.Show(cobraProgramDir);

            e.Session.Log("ProgramDir Pfad : " + ProgramDir);

            if (string.IsNullOrEmpty(ProgramDir))
            {
                e.Session.Log("Programm ist nicht installiert.");
                e.ManagedUI.Shell.ErrorDetected = true;
            }

            FileVersionInfo fi = FileVersionInfo.GetVersionInfo(ProgramDir + "\\app.exe");

            e.Session["APPVERSION"] = fi.FileVersion;

            string cobraVersion = e.Session["APPVERSION"];

            Version installedVersion = cobraVersion.ToRawVersion();

            if (installedcobraVersion != null)
            {
                e.Session.Log("Ermittelte cobra Version " + installedVersion.ToString());

                //cobra Versionsanpassung notwendig.

                Version mindVersion = new Version(v.Major, v.Minor);

                if (installedVersion.Major == mindVersion.Major && installedVersion.Minor == mindVersion.Minor)
                {
                    e.Session.Log("Die System hat alle Voraussetzungen für das Installation von Plug-in erfüllt.");
                }
                else
                {
                    e.Session.Log("Die System hat die Voraussetzungen nicht erfüllt. Dieser Version von Plugin ist nicht kompatibel für installierte cobra CRM Version.");
                    e.ManagedUI.Shell.ErrorDetected = true;
                }
            }
            else
            {
                e.Session.Log("Die System hat die Voraussetzungen nicht erfüllt. Version konnte nicht ermittelt werden.");
                e.ManagedUI.Shell.ErrorDetected = true;
            }
        }
        else
        {
            e.Session.Log("Programm ist nicht installiert oder RegistryKey Path wurde nicht gefunden.");
            e.ManagedUI.Shell.ErrorDetected = true;
        }
    }
    catch (NullReferenceException nullrefex)
    {
        e.ManagedUI.Shell.ErrorDetected = true;
        e.Session.Log("NullReferenceExcepton " + nullrefex.ToString());

    }
    catch (Exception ex)
    {
        e.ManagedUI.Shell.ErrorDetected = true;
        e.Session.Log("Excepton " + ex.ToString());

    }
}

public static string ReadCobraVersionXml(string path, SetupEventArgs e)
{
    try
    {
        XDocument doc = XDocument.Load(path);

        string version = doc.Root.Element("AppVersion").Element("Version").Value;

        return version;

    }
    catch (Exception ex)
    {
        e.Session.Log(ex.ToString());
        return "";
    }
}
oleg-shilo commented 1 month ago

You can simplify your solution by embedding the exml file as MSI binary instead of dll resource:

var project = new ManagedProject("ManagedSetup",
                  new Binary(new Id("embeded_xml"), @".\NameSpace.Resources.Embeded_File.xml"),
                  . . .

static void project_Load(SetupEventArgs e)
{
     byte[] data = e.Session.ReadBinary("embeded_xml");

    using (var memoryStream = new MemoryStream(data))
    {
        var xDoc = XDocument.Load(memoryStream);
        var version = xDoc.ReadTheVersion();
        . . .

    }
}
Torchok19081986 commented 1 month ago

morning, i tried also in this way , works like charm. Is there way to do it in BA Installer ? I reference to WPF Setup Project, where on WelcomeScreen in BA, current Version of Installed Product is isntalled. how can i do it here in WPF C# ? Any posibiility to check current Product Version and some time later, 2 - 5 sec. for my plugin installed third pary software ?

Sample is WixSharp.Samples\Wix# Samples\External_UI\WpfSetup. This sample i use for WPF Setup.

oleg-shilo commented 1 month ago

Since you can have your BA implemented with a custom UI you can do any sort of checking with that custom UI:

image

And you can pass any information obtained in the custom UI to the MSI by channelling it through the Bootstrapper variables

Torchok19081986 commented 1 month ago

Ok, many thanks again for quick answer. Last question here, after that , close this question. Is there some limit for use third party dll into BA and what is best way to load or use it in BA that targets some custom actions in MSI ?

oleg-shilo commented 1 month ago

Is there some limit for use third party dll into BA and what is best way to load

You can use the assembly resources as you did in some other cases. But the standard WiX way is to add the dependency assemblies as payload elements in the parent application element:

image

In WixSharp you do it by specifying the path of the dependency assembly paths in the Application constructor"

bundle.Application = new ManagedBootstrapperApplication(customBa_Wix5_Sample, @"my_asm.dll");

After that, you can call assembly methods from your custom BA.

use it in BA that targets some custom actions in MSI

I am not sure what you mean "targets". Can you elaborate?

Torchok19081986 commented 1 month ago

Thanks again. I try to explain, maybe i thinking wrong way. BA is only UI where use can choose some option, BUT i need to - somehow - transfer this Choose to MSI if option is choosed. Session is only available in CA or in Wixsharp Project for Events etc. If i use wix BA variables , these are reflected to UI , not to MSI. For example if BA has embeded SQLSERVER where UI also has option install it or not. if yes then start install sql server before installation of app and if no then jump to app installtion. I dont seen any examples for this scenario. That i mean in Targets msi. In MSI self i can create CA where i can get user choose from Dialog, but in BA dont.

oleg-shilo commented 1 month ago

In the WixBootstrapper example you can see how you can tunnel the Bundle variable to the MSI property. Mind you MSI property has to be public (the name is in all capitals)

bootstrapper.Variables = new[]
{
    new Variable("LogFileLocation", @"C:\temp\setup.log") { Overridable = true },
    new Variable("MyCheckbox", "0", VariableType.numeric) { Overridable = true },
    new Variable("MyCheckboxLabel", "Install CRT?", VariableType.@string) { Overridable = true },
    // note 'InstallFolder' is a special built-in variable that can be changed by the user from the options page
    new Variable("InstallFolder", installDir) { Overridable = true }
};
. . .
new MsiPackage(crtMsi)
{
    Visible = true,
    MsiProperties = "INSTALLDIR=[InstallFolder]",
    InstallCondition = "MyCheckbox<>0"
}