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

ElevatedManagedAction does not work with Platform.x64 #1651

Closed kipamgs closed 1 month ago

kipamgs commented 1 month ago

WixSharp-wix4.WPF 2.4.0.1

When I create a project (Custom WPF UI Wix4) with an ElevatedManagedAction and add project.Platform = Platform.x64; the installation fails. The custom action is not executed.

Part of the installation log:

Info: SFXCA: Binding to CLR version v4.0.30319
Info: Calling custom action MyInstaller!MyInstaller.Program+CustomActions.ExtractZip
Info: Error: could not load custom action class MyInstaller.Program+CustomActions from assembly: MyInstaller
Info: System.BadImageFormatException: The file or assembly "MyInstaller" or a dependencycould not be found. An attempt was made to load a file with an incorrect format.
Dateiname: "MyInstaller"
   bei System.Reflection.RuntimeAssembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
   bei System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, Evidence assemblySecurity, RuntimeAssembly reqAssembly, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
   bei System.Reflection.RuntimeAssembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean forIntrospection)
   bei System.Reflection.RuntimeAssembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)
   bei System.AppDomain.Load(String assemblyString)
   bei WixToolset.Dtf.WindowsInstaller.CustomActionProxy.GetCustomActionMethod(Session session, String assemblyName, String className, String methodName)
using System;
using System.Windows.Forms;
using WixSharp;
using WixSharp.CommonTasks;
using WixSharp.UI.WPF;
using WixToolset.Dtf.WindowsInstaller;

namespace MyInstaller
{
    public class Program
    {
        static void Main()
        {
            var project = new ManagedProject("MyProduct",
                              new Dir(@"%ProgramFiles%\My Company\My Product",
                                  new File("Program.cs")
                                  )
                              );

            project.Platform = Platform.x64; //without this line the installation executes without any errors.

            project.GUID = new Guid("ffcdd07e-a199-4212-94f2-4beccbb618c9");

            // project.ManagedUI = ManagedUI.DefaultWpf; // all stock UI dialogs

            project.AddAction(new ElevatedManagedAction(CustomActions.ExtractZip, Return.check, When.Before, Step.StartServices, Condition.NOT_Installed)//, Sequence.InstallExecuteSequence)
            {
                UsesProperties = $"EXTRACT_DIR=[INSTALLDIR], FILENAME=CustomAction.txt",
            });

            //custom set of UI WPF dialogs
            project.ManagedUI = new ManagedUI();

            project.ManagedUI.InstallDialogs.Add<XRangeEyeInstaller.WelcomeDialog>()
                                            .Add<XRangeEyeInstaller.LicenceDialog>()
                                            .Add<XRangeEyeInstaller.FeaturesDialog>()
                                            .Add<XRangeEyeInstaller.InstallDirDialog>()
                                            .Add<XRangeEyeInstaller.ProgressDialog>()
                                            .Add<XRangeEyeInstaller.ExitDialog>();

            project.ManagedUI.ModifyDialogs.Add<XRangeEyeInstaller.MaintenanceTypeDialog>()
                                           .Add<XRangeEyeInstaller.FeaturesDialog>()
                                           .Add<XRangeEyeInstaller.ProgressDialog>()
                                           .Add<XRangeEyeInstaller.ExitDialog>();

            //project.SourceBaseDir = "<input dir path>";
            //project.OutDir = "<output dir path>";

            project.BuildMsi();
        }
    }

    public class CustomActions
    {
        [CustomAction]
        public static ActionResult ExtractZip(Session session)
        {

            System.Diagnostics.Debugger.Launch(); //does not trigger with project.Platform = Platform.x64

            string targetPath = session?.Property("EXTRACT_DIR");
            string filename = session?.Property("FILENAME");

            string filePath = System.IO.Path.Combine(targetPath, filename);

            using (System.IO.File.Create(filePath)) //create empty file for testing
            {
            }

            return ActionResult.Success;
        }
    }

}

Full installation log:

Info: === Logging started: 02/10/2024  11:55:37 ===
CommonData: Message type: 0, Argument: 1033, 1252
CommonData: Message type: 1, Argument: MyProduct
ActionStart: Action 11:55:37: INSTALL. 
Info: Action start 11:55:37: INSTALL.
CommonData: 1: 0 2: 1033 3: 1252 
CommonData: 1: 0 2: 1033 3: 1252 
CommonData: 1: MyProduct
Info: === Logging started: 02/10/2024  11:55:39 ===
CommonData: Message type: 0, Argument: 1033, 1252
CommonData: Message type: 1, Argument: MyProduct
ActionStart: Action 11:55:39: INSTALL. 
Info: Action start 11:55:39: INSTALL.
InstallStart: 1: MyProduct 2: {0AABDD35-41FD-4E97-BFFD-767A3CC93140} 
ActionStart: Action 11:55:39: FindRelatedProducts. Searching for related applications
Info: Action start 11:55:39: FindRelatedProducts.
Info: Action ended 11:55:39: FindRelatedProducts. Return value 1.
ActionStart: Action 11:55:39: WixSharp_InitRuntime_Action. 
Info: Action start 11:55:39: WixSharp_InitRuntime_Action.
Info: SFXCA: Extracting custom action to temporary directory: C:\Users\kpayer\AppData\Local\Temp\SFXCAB8429F14084FB5487AFABD123CDBF15C\
Info: SFXCA: Binding to CLR version v4.0.30319
Info: Calling custom action WixSharp!WixSharp.ManagedProjectActions.WixSharp_InitRuntime_Action
Info: Action ended 11:55:40: WixSharp_InitRuntime_Action. Return value 1.
ActionStart: Action 11:55:40: AppSearch. Searching for installed applications
Info: Action start 11:55:40: AppSearch.
Info: Action ended 11:55:40: AppSearch. Return value 0.
ActionStart: Action 11:55:40: LaunchConditions. Evaluating launch conditions
Info: Action start 11:55:40: LaunchConditions.
Info: Action ended 11:55:40: LaunchConditions. Return value 1.
ActionStart: Action 11:55:40: ValidateProductID. 
Info: Action start 11:55:40: ValidateProductID.
Info: Action ended 11:55:40: ValidateProductID. Return value 1.
ActionStart: Action 11:55:40: CostInitialize. Computing space requirements
Info: Action start 11:55:40: CostInitialize.
Info: Action ended 11:55:40: CostInitialize. Return value 1.
ActionStart: Action 11:55:40: FileCost. Computing space requirements
Info: Action start 11:55:40: FileCost.
Info: Action ended 11:55:40: FileCost. Return value 1.
ActionStart: Action 11:55:40: CostFinalize. Computing space requirements
Info: Action start 11:55:40: CostFinalize.
Info: Action ended 11:55:40: CostFinalize. Return value 1.
ActionStart: Action 11:55:40: MigrateFeatureStates. Migrating feature states from related applications
Info: Action start 11:55:40: MigrateFeatureStates.
Info: Action ended 11:55:40: MigrateFeatureStates. Return value 0.
ActionStart: Action 11:55:40: InstallValidate. Validating install
Info: Action start 11:55:40: InstallValidate.
Info: Action ended 11:55:40: InstallValidate. Return value 1.
ActionStart: Action 11:55:40: RemoveExistingProducts. Removing applications
Info: Action start 11:55:40: RemoveExistingProducts.
Info: Action ended 11:55:40: RemoveExistingProducts. Return value 1.
ActionStart: Action 11:55:40: InstallInitialize. 
Info: Action start 11:55:40: InstallInitialize.
Info: Action ended 11:55:40: InstallInitialize. Return value 1.
ActionStart: Action 11:55:40: ProcessComponents. Updating component registration
Info: Action start 11:55:40: ProcessComponents.
ActionStart: Action 11:55:40: GenerateScript. Generating script operations for action:
ActionData: Updating component registration
Info: Action ended 11:55:40: ProcessComponents. Return value 1.
ActionStart: Action 11:55:40: UnpublishFeatures. Unpublishing Product Features
Info: Action start 11:55:40: UnpublishFeatures.
Info: Action ended 11:55:40: UnpublishFeatures. Return value 1.
ActionStart: Action 11:55:40: RemoveFiles. Removing files
Info: Action start 11:55:40: RemoveFiles.
Info: Action ended 11:55:40: RemoveFiles. Return value 0.
ActionStart: Action 11:55:40: RemoveFolders. Removing folders
Info: Action start 11:55:40: RemoveFolders.
Info: Action ended 11:55:40: RemoveFolders. Return value 0.
ActionStart: Action 11:55:40: CreateFolders. Creating folders
Info: Action start 11:55:40: CreateFolders.
Info: Action ended 11:55:40: CreateFolders. Return value 0.
ActionStart: Action 11:55:40: InstallFiles. Copying new files
Info: Action start 11:55:40: InstallFiles.
ActionData: File: Copying new files,  Directory: ,  Size: 
Info: Action ended 11:55:40: InstallFiles. Return value 1.
ActionStart: Action 11:55:40: Set_ExtractZip_Props. 
Info: Action start 11:55:40: Set_ExtractZip_Props.
Info: Action ended 11:55:40: Set_ExtractZip_Props. Return value 1.
ActionStart: Action 11:55:40: ExtractZip. 
Info: Action start 11:55:40: ExtractZip.
ActionData: 
Info: Action ended 11:55:40: ExtractZip. Return value 1.
ActionStart: Action 11:55:40: StartServices. Starting services
Info: Action start 11:55:40: StartServices.
Info: Action ended 11:55:40: StartServices. Return value 1.
ActionStart: Action 11:55:40: RegisterUser. Registering user
Info: Action start 11:55:40: RegisterUser.
Info: Action ended 11:55:40: RegisterUser. Return value 1.
ActionStart: Action 11:55:40: RegisterProduct. Registering product
Info: Action start 11:55:40: RegisterProduct.
ActionData: Registering product
Info: Action ended 11:55:40: RegisterProduct. Return value 1.
ActionStart: Action 11:55:40: PublishFeatures. Publishing Product Features
Info: Action start 11:55:40: PublishFeatures.
ActionData: Feature: Publishing Product Features
Info: Action ended 11:55:40: PublishFeatures. Return value 1.
ActionStart: Action 11:55:40: PublishProduct. Publishing product information
Info: Action start 11:55:40: PublishProduct.
ActionData: 
Info: Action ended 11:55:40: PublishProduct. Return value 1.
ActionStart: Action 11:55:40: Set_CancelRequestHandler_Props. 
Info: Action start 11:55:40: Set_CancelRequestHandler_Props.
Info: Action ended 11:55:40: Set_CancelRequestHandler_Props. Return value 1.
ActionStart: Action 11:55:40: CancelRequestHandler. 
Info: Action start 11:55:40: CancelRequestHandler.
ActionData: 
Info: Action ended 11:55:40: CancelRequestHandler. Return value 1.
ActionStart: Action 11:55:40: InstallFinalize. 
Info: Action start 11:55:40: InstallFinalize.
CommonData: Message type: 0, Argument: 1033, 1252
CommonData: Message type: 1, Argument: MyProduct
ActionStart: Action 11:55:40: ProcessComponents. Updating component registration
ActionData: 1: {0AABDD35-41FD-4E97-BFFD-767A3CC93140} 2: {0AABDD35-41FD-4E97-BFFD-767AC8838398} 3: C:\Program Files\My Company\My Product\Program.cs 
ActionStart: Action 11:55:40: InstallFiles. Copying new files
ActionData: File: Program.cs,  Directory: C:\Program Files\My Company\My Product\,  Size: 2757
ActionStart: Action 11:55:40: ExtractZip. 
Info: SFXCA: Extracting custom action to temporary directory: C:\windows\Installer\SFXCA697978E3E94743BB106AF13B6704B564\
Info: SFXCA: Binding to CLR version v4.0.30319
Info: Calling custom action MyInstaller!MyInstaller.Program+CustomActions.ExtractZip
Info: Error: could not load custom action class MyInstaller.Program+CustomActions from assembly: MyInstaller
Info: System.BadImageFormatException: Die Datei oder Assembly "MyInstaller" oder eine Abhängigkeit davon wurde nicht gefunden. Es wurde versucht, eine Datei mit einem falschen Format zu laden.
Dateiname: "MyInstaller"
   bei System.Reflection.RuntimeAssembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
   bei System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, Evidence assemblySecurity, RuntimeAssembly reqAssembly, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
   bei System.Reflection.RuntimeAssembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean forIntrospection)
   bei System.Reflection.RuntimeAssembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)
   bei System.AppDomain.Load(String assemblyString)
   bei WixToolset.Dtf.WindowsInstaller.CustomActionProxy.GetCustomActionMethod(Session session, String assemblyName, String className, String methodName)

WRN: Protokollierung der Assemblybindung ist AUS.
Sie können die Protokollierung der Assemblybindungsfehler aktivieren, indem Sie den Registrierungswert  (DWORD) auf 1 festlegen.
Hinweis: Die Protokollierung der Assemblybindungsfehler führt zu einer gewissen Leistungseinbuße.
Sie können dieses Feature deaktivieren, indem Sie den Registrierungswert  entfernen.

Info: CustomAction ExtractZip returned actual error code 1603 (note this may not be 100% accurate if translation happened inside sandbox)
Info: Action ended 11:57:41: InstallFinalize. Return value 3.
CommonData: Message type: 2, Argument: 0
CommonData: Message type: 2, Argument: 0
CommonData: Message type: 0, Argument: 1033, 1252
CommonData: Message type: 1, Argument: MyProduct
ActionStart: Action 11:57:41: Rollback. Rolling back action:
ActionData: ExtractZip
ActionData: Copying new files
ActionData: Updating component registration
CommonData: Message type: 2, Argument: 1
CommonData: Message type: 2, Argument: 1
Info: Action ended 11:57:41: INSTALL. Return value 3.
InstallEnd: 1: MyProduct 2: {0AABDD35-41FD-4E97-BFFD-767A3CC93140} 3: 3 
CommonData: 1: 2 2: 0 
CommonData: 1: 2 2: 1 
Info: Action ended 11:57:41: INSTALL. Return value 3.

MyInstaller.zip

Torchok19081986 commented 1 month ago

morning, really intresting one. Am i right, if i think, you want to extract zip from msi to dest dir? If so, your zip has to be binary and embeded ressource in project. CA for ExtractZip is ElevatedAction, but why you use it ? If you use Before or AfterInstall event, is automatically elevated.

i had same issue for extracting file.


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

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

        if (System.IO.File.Exists(targetFilePath) == true)
        {
            System.IO.File.Delete(targetFilePath);
        }

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

        // Extract the Embeded file from the embedded resources
        using (Stream stream = assembly.GetManifestResourceStream("<YourNamespace>.Resources.<your_file>"))
        {
            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 ExtractFile: {ex.Message}");
        e.ManagedUI.Shell.ErrorDetected = true;

        return false;
    }
}

you could try it. For me it works, i also tested it as zip, rar and exe.

oleg-shilo commented 1 month ago

@Torchok19081986, you can also use that alternative approach that I shared with you a few days ago. The one that avoids using resources. Even though there is nothing wrong with that using resources.

https://github.com/oleg-shilo/wixsharp/issues/1645#issuecomment-2381258983

kipamgs commented 1 month ago

For testing and to keep it simple i just created a custom action that creates an empty txt file. It works for a 32 bit installation but as soon as i set it to 64 bit the installation waits for the timeout of the custom action and then fails.

The issue is also reproducible wiht an empty custom action. It also happens for ManagedAction if they are executed deffered. The only difference is that there seems to be no timeout for ManagedAction.

oleg-shilo commented 1 month ago

It's unlikely the problem is caused by the configuration (x64 vs x86). Most likely it's all about what you do in your custom action.

Put an exception handler there and log the error. You can debug it - even better. Most likely you will see that the timeout is caused by your custom routine but not by the CA itself.

Debugging seems to work. You just need to ensure your debugger is elevated and you attach to the correct process (rundll32.exe): image

I also checked the DTF_(ManagedCA) sample for debugging. It's also OK. So maybe first try and see that you can debug the sample. Just add this to the sample code:

image

As a side note, I prefer to use events (e.g., project.AfterInstall), which is just easier to work with.

And AfterInstall event is nothing else but a deferred ManagedAction. And being deferred is what makes a CA to be elevated.

kipamgs commented 1 month ago

I had to edit my .csproj file. I had to remove the line <RuntimeIdentifier>win-x86</RuntimeIdentifier> . If I understand it correctly this caused a mismatch of 32 and 64 bit between the installer and the custom action.