Issue using ManagedAction that references another assembly

May 4, 2015 at 1:25 AM
The error in the MSI log is:
MSI (s) (14:98) [19:07:58:124]: Doing action: Action1_InstallDatabase
MSI (s) (14:98) [19:07:58:124]: Note: 1: 2205 2:  3: ActionText 
Action start 19:07:58: Action1_InstallDatabase.
MSI (s) (14:D8) [19:07:58:233]: Invoking remote custom action. DLL: C:\WINDOWS\Installer\MSIACD1.tmp, Entrypoint: InstallDatabase
MSI (s) (14:0C) [19:07:58:233]: Generating random cookie.
MSI (s) (14:0C) [19:07:58:233]: Created Custom Action Server with PID 5080 (0x13D8).
MSI (s) (14:A0) [19:07:58:249]: Running as a service.
MSI (s) (14:A0) [19:07:58:249]: Hello, I'm your 32bit Impersonated custom action server.
SFXCA: Extracting custom action to temporary directory: C:\WINDOWS\Installer\MSIACD1.tmp-\
SFXCA: Failed to extract to temporary directory. Cabinet error code 1.
CustomAction Action1_InstallDatabase returned actual error code 1603 (note this may not be 100% accurate if translation happened inside sandbox)
Action ended 19:07:58: Action1_InstallDatabase. Return value 3.
My project definition includes:
                new ManagedAction("InstallDatabase", Return.check, When.Before, Step.InstallFinalize, Condition.Create("&Database_components=3"))
                {
                    RefAssemblies = new[] { @"files\Sql\redacted.Services.LedDisplay.Sql.dll"}
                }

...


        [CustomAction]
        public static ActionResult InstallDatabase(Session session)
        {
            return WixCLRDialog.ShowAsMsiDialog(new InstallDatabase(session));
        }
The InstallDatabase class invokes a static method in another assembly (the one referenced above).

The actual message when I run the installer is
Could not load type 'redacted.Services.LedDisplay.Sql.SqlScription from assembly redacted.Services.LedDisplay.Sql.
How do I correctly invoke a static method in another assembly?
May 4, 2015 at 5:54 AM
I modified the <Wix# Samples>\Custom_UI\CustomCLRDialog sample to implement your scenario.
I haven't made the release yet so you will need to use Git to get the sample code.
May 4, 2015 at 2:50 PM
Edited May 4, 2015 at 3:11 PM
Thank you Oleg, I made the following change, and it worked perfectly.
new ManagedAction("InstallDatabase", Return.check, When.Before, Step.InstallFinalize, Condition.Create("&Database_components=3"))
{
    RefAssemblies = new[] { @"files\Sql\redacted.Services.LedDisplay.Sql.dll"}
}
to
 new ManagedAction("InstallDatabase", Return.check, When.Before, Step.InstallFinalize, Condition.Create("&Database_components=3"))
{
    RefAssemblies = new[] { typeof(redacted.Services.LedDisplay.Sql.SqlScripting).Assembly.Location }
}
Thank you for the prompt response, and a great product.
Marked as answer by TwoRedCells on 5/5/2015 at 6:17 AM
May 4, 2015 at 3:27 PM
Edited May 4, 2015 at 3:27 PM
I also noticed that if you create one ManagedAction that references an external assembly, others need to reference the same assembly, even if they do not use it, otherwise you get the error:
System.ApplicationException: ManagedAction assembly '%this%' is declared multipl
e times with the different (inconsistent) set of referenced assemblies. Ensure t
hat all declarations have the same referenced assemblies by either using identic
al declarations or by using Project.DefaultRefAssemblies.
   at WixSharp.ProjectValidator.Validate(Project project)
   at WixSharp.Compiler.GenerateWixProj(Project project)
   at WixSharp.Compiler.BuildWxs(Project project, String path, OutputType type)
   at WixSharp.Compiler.BuildWxs(Project project, OutputType type)
   at WixSharp.Compiler.Build(Project project, String path, OutputType type)
   at WixSharp.Compiler.Build(Project project, OutputType type)
   at REDACTED.Services.LedDisplay.Setup.Script.Main(String[] args) in e:\50126.Co
ndor.sign\REDACTED.Services.LedDisplay.Setup\setup.cs:line 126
When I tried:
new ManagedAction("ShowDatabaseDialog"),
new ManagedAction("InstallDatabase", Return.check, When.Before, Step.InstallFinalize, Condition.Create("&Database_components=3"))
{
    RefAssemblies = new[] { @"files\Sql\redacted.Services.LedDisplay.Sql.dll"}
}
This doesn't make sense to me, but adding the assembly to Project.DefaultRefAssemblies is a workaround.
May 5, 2015 at 5:38 AM
Edited May 5, 2015 at 5:50 AM
It is related to the Issue#16
Just to recapture...

MSI runtime treats custom action (and ref assemblies) on the per-action base. Meaning that for every action it will config an environment to be executed. In the case of managed actions it means that the assembly and it's dependency will be copied in to a temp dir where they will be executed from.

Consider the following scenario:
new ManagedAction("actionA", "myAsm.dll"),
new ManagedAction("actionB", "myAsm.dll"),
new ManagedAction("actionC", "myAsm.dll") { RefAssemblies = new [] { "externAsm.dll" } },
In order to avoid embedding tree separate assembly sets as myAsm.dll, myAsm.dll, and myAsm.dll+externAsm.dll you would rather embed a single set as myAsm.dll+externAsm.dll and link all three actions to it. That is why the solution is to declare your actions (that share the same implementation assembly) in the way that they share the same set of dependencies. This can be achieved as follows:
new MyManagedAction("actionA", "myAsm.dll", "externAsm.dll"),
new MyManagedAction("actionB", "myAsm.dll", "externAsm.dll"),
new MyManagedAction("actionC", "myAsm.dll", "externAsm.dll"),

//or
new MyManagedAction("actionA", "myAsm.dll"),
new MyManagedAction("actionB", "myAsm.dll"),
new MyManagedAction("actionC", "myAsm.dll"),
...
project.DefaultRefAssemblies.Add("externAsm.dll");

//or
new MyManagedAction("actionA"),
new MyManagedAction("actionB"),
new MyManagedAction("actionC"),
...
project.DefaultRefAssemblies.Add("myAsm.dll");
project.DefaultRefAssemblies.Add("externAsm.dll");
Your case is not different just your action assembly is "%this%".
May 9, 2015 at 10:19 AM
Edited May 9, 2015 at 1:58 PM
I actually decided to automate combining the ref assemblies for a given CA assembly.

Starting from v1.0.19.0 Compiler will always analyse all cases of ManagedActions using the assembly before packing it and it will always pack the combined set of the ref assemblies. Thus the original code (as below) will be compiled just fine:
new ManagedAction("ShowDatabaseDialog"),
new ManagedAction("InstallDatabase", Return.check, When.Before, Step.InstallFinalize, Condition.Create("&Database_components=3"))
{
    RefAssemblies = new[] { @"files\Sql\redacted.Services.LedDisplay.Sql.dll"}
}