[wix-users] Custom Action to Create and Install a File?

Rob Mensching rob at firegiant.com
Sun Mar 22 02:48:49 PDT 2020


Do consider rollback (install rollback, repair rollback, uninstall rollback, upgrade rollback) whenever writing a custom action that modifies system state.

Short replies here. Complete answers here: https://www.firegiant.com/services/

-----Original Message-----
From: wix-users <wix-users-bounces at lists.wixtoolset.org> On Behalf Of Russell Haley via wix-users
Sent: Saturday, March 21, 2020 10:21 PM
To: Edwin Castro <egcastr at gmail.com>
Cc: Russell Haley <russ.haley at gmail.com>; WiX Toolset Users Mailing List <wix-users at lists.wixtoolset.org>
Subject: Re: [wix-users] Custom Action to Create and Install a File?

On Wed, Feb 19, 2020 at 10:33 AM Edwin Castro <egcastr at gmail.com> wrote:

> I think you should start with designing your deferred custom action. 
> It will need to read all data it needs from CustomActionData. Begin in 
> the deferred custom action so you can define all the bits of data you 
> need and how they will be marshalled through CustomActionData. I think 
> the managed API provides a mechanism where the CustomActionData is 
> effectively marshalled as key-value pairs in a dictionary. It's been 
> nearly a decade since I've worked on managed custom actions so bare with me.
>
> In your WiX source, you will want to declare your deferred custom 
> action using the CustomAction element but you may not want to schedule 
> it in InstallExecuteSequence. You may want to let your immediate 
> custom action do the scheduling. Your immediate custom action would be 
> responsible for making all decisions and writing all the required data 
> into CustomActionData which is "passed" to the deferred custom action 
> when the immediate custom action schedules the deferred custom action 
> to run. If you find out you need a rollback custom action, then the 
> immediate custom action is the right place to make decisions about 
> rollback data that may need to be passed to the rollback custom action through CustomActionData.
> Depending on your needs you make need to assemble separate 
> CustomActionData for your deferred custom action and your rollback 
> custom action. Remember rollback custom actions are scheduled before 
> (first) deferred custom actions.
>
> If you do not have a lot of decision making the a "full blown" 
> immediate custom action might be overkill. In some cases, you can use 
> simple SetProperty custom actions to directly set the CustomActionData 
> for your rollback and deferred custom actions. In that case, you use 
> something like SetProperty to set a property with the same name as the 
> target deferred/rollback custom action. That property becomes the 
> CustomActionData for the deferred/rollback custom action. If you 
> format the contents just right, then the managed API can properly 
> parse the CustomActionData and provide it to you through the managed 
> API. In a case like this you would explicitly schedule your 
> deferred/rollback custom actions in InstallExecuteSequence since there 
> is no immediate custom action responsible for doing that work.
>
> Let me try a concrete example for the SetProperty approach. NOTE: I 
> have not checked this code and make no guarantees. This is meant as a 
> starting point so you can do some research and fix it for your own purposes.
>
> <Fragment>
>     <Property Id="LuaVersion2" Value="5.3" />
>     <Property Id="LuaRocksConfigFileName" Value="config-[LuaVersion2].lua"
> />
>
>     <!-- Construct required data for deferred custom action. These are 
> not strictly necessary as they can be used inline in the SetProperty 
> for Configure.LuaRocks but I think this easier to understand. -->
>     <SetProperty Id="LuaRocksConfigPath"
> Value="[LUAROCKS_INSTALLLOCATION]\[LuaRocksConfigFileName]"
> Before="SetConfigure.LuaRocks" />
>     <SetProperty Id="LuaRocksRootPath" Value="[LUAROCKS_INSTALLLOCATION]"
> Before="SetConfigure.LuaRocks" />
>     <SetProperty Id="LuaRootPath" Value="[LUA_INSTALLLOCATION]"
> Before="SetConfigure.LuaRocks" />
>     <SetProperty Id="ToolsPath" Value="[LLVMMINGW_INSTALLLOCATION]"
> Before="SetConfigure.LuaRocks" />
>
>     <!-- Set "CustomActionData" for Configure.LuaRocks deferred custom 
> action. I think for managed custom actions the format is 
> key=value;key=value -->
>     <SetProperty Id="Configure.LuaRocks"
> Value="LuaRocksConfigPath=[LuaRocksConfigPath];LuaRocksRootPath=[LuaRocksRootPath];LuaRootPath=[LuaRootPath];ToolsPath=[ToolsPath]"
> />
>
>     <!-- The deferred custom action -->
>     <CustomAction Id="Configure.LuaRocks"
> BinaryKey="WinLua.Installer.CustomAction" DllEntry="ConfigureLuaRocks"
> Execute="deferred" Return="Check" />
>
>     <!-- Schedule the deferred custom action between InstallInitialize 
> and InstallFinalize -->
>     <InstallExecuteSequence>
>         <!-- You may need a condition to make sure this does not run 
> during uninstall -->
>         <Custom Action="Configure.LuaRocks" After="InstallFiles" />
>     </InstallExecuteSequence>
> </Fragment>
>
> If you have a need for a rollback custom action then it must be 
> scheduled before Configure.LuaRocks and any data it needs will need to 
> be set in a similar fashion. If you create a back up file in the 
> deferred custom action that is meant to be used in the rollback custom 
> action for rollback purposes, then you will likely want to pass the 
> back up file path to the deferred custom action and rollback custom action through CustomActionData.
> You would also want a commit custom action to clean up the back up 
> file (during rollback the rollback custom action would be responsible 
> for cleaning up the back up file.
>
> The managed code for the deferred custom action just reads 
> LuaRocksConfigPath, LuaRocksRootPath, LuaRootPath and ToolsPath from 
> session.CustomActionData (?) or something similar and uses the values 
> to write the config file. Any other custom actions you need would work 
> similarly but do whatever work is appropriate. I'd start out with the 
> deferred custom action first but please do consider (and test) 
> rollback scenarios for install, repair, uninstall and upgrade!
>
> Adding a row to the RemoveFile table is a little more involved. You'd 
> want an immediate custom action that runs during uninstall and is 
> scheduled before RemoveFiles. This immediate custom action needs to 
> insert a row into the RemoveFile table so it will use a SQL query to 
> do so but since you need to provide dynamic data you will use a Record 
> to provide that data when the query is executed. The details here are 
> a little more involved and you must understand the RemoveFile table 
> format. All that said, you may not need to do all that. Perhaps it is 
> as simple as adding a RemoveFile element to a component associated 
> with the config file. Something like
>
> <Directory Id="luarocksbin" Name="bin">
>     <Component Id="..." Guid="...">
>         <File Id="..." Source="...\luarocks.exe" KeyPath="yes" />
>         <RemoveFile Id="RemoveConfigFile"
> Property="LUAROCKS_INSTALLLOCATION" Name="[LuaRocksConfigFileName]"
> On="uninstall" />
>     </Component>
> </Directory>
>
> I'm less sure about this and it might have some odd interactions with 
> your use of merge modules. BUT if you can do something like that then 
> you don't need an immediate custom action to dynamically add a row to 
> the RemoveFile table which would be a win.
>
Hello again Edwin, et all,

I've done a fair bit of testing and the install custom action is working to expectation. I appreciate all your help getting through that. My next, and hopefully final step(s) are custom actions to remove two things: the config file created by the install custom action, and a directory of components (called a Rocktree) that may or may not have been created by the user.
These components live in the installation directory but are not under the control of the installer; they've been added after the fact.

My preference is to have a checkbox asking if the config file and rocktree should be removed. I *think* that can be done with the WIXUI_EXITDIALOGOPTIONALCHECKBOX, but I don't know if that checkbox can be used just for an uninstall sequence.

I have Two problems:
1) I noted that the SetProperty calls for the paths are always before "SetConfigure.LuaRocks". How would I get the properties to set before both the SetConfigure.LuaRocks and the new SetRemove.ConfigAndRocktree.LuaRocks?

2) My Configure.LuaRocks custom action is running on the remove step
*after* the Remove.ConfigAndRocktree.LuaRocks.

 Anyway, this is what I've roughed in so far (I've disabled the exit dialog checkbox for the moment):

...
    <!--Properties for the LuaRocks configuraiton file that we generate with Configure.LuaRocks custom Action-->
    <SetProperty Id="LuaVersion" Value="$(var.LuaVersion)"
Before="SetConfigure.LuaRocks" Sequence="execute" />
    <SetProperty Id="LuaRootPath" Value="[LUA_INSTALLLOCATION]"
Before="SetConfigure.LuaRocks" Sequence="execute" />

    <SetProperty Id="LuaRocksRootPath" Value="[LUAROCKS_INSTALLLOCATION]"
Before="SetConfigure.LuaRocks" Sequence="execute">
      <![CDATA[Installed OR ((&LuaRocks=3) AND NOT(!LuaRocks=3)) OR
((&LuaRocks=2) AND (!LuaRocks=3))]]>
    </SetProperty>

    <!--Don't set this property if the LLVM compiler is not being
installed-->
    <SetProperty Id="ToolsPath" Value="[LLVMMINGW_INSTALLLOCATION]"
Before="SetConfigure.LuaRocks" Sequence="execute">
      <![CDATA[Installed OR ((&LLVM_MinGW=3) AND NOT(!LLVM_MinGW=3)) OR
((&LLVM_MinGW=2) AND (!LLVM_MinGW=3))]]>
    </SetProperty>
    <SetProperty Id="Configure.LuaRocks"
Value="LuaVersion=[LuaVersion];LuaRocksRootPath=[LuaRocksRootPath];LuaRootPath=[LuaRootPath];ToolsPath=[ToolsPath]"
Before="Configure.LuaRocks" Sequence="execute" />
    <SetProperty Id="Remove.ConfigAndRocktree.LuaRocks"
Value="LuaVersion=[LuaVersion];LuaRocksRootPath=[LuaRocksRootPath];LuaRootPath=[LuaRootPath];ToolsPath=[ToolsPath]"
Before="Remove.ConfigAndRocktree.LuaRocks" Sequence="execute" />

    <!--<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="Remove LuaRocks Options"  />-->

    <!-- The deferred custom action -->
    <Binary Id='WinLua.Installer.CustomAction'
SourceFile='..\Winlua.Installer.CustomAction\bin\Release\Winlua.Installer.CustomAction.CA.dll'/>
    <CustomAction Id="Configure.LuaRocks"
BinaryKey="WinLua.Installer.CustomAction" DllEntry="ConfigureLuaRocks"
Impersonate="no" Execute="deferred" Return="check" />
    <CustomAction Id="Remove.ConfigAndRocktree.LuaRocks"
 BinaryKey="WinLua.Installer.CustomAction" DllEntry="RemoveLuaRocksConfig"
Impersonate="no" Execute="deferred" Return="check" />
      <!-- Schedule the deferred custom action between InstallInitialize and InstallFinalize -->
      <InstallExecuteSequence>
        <!-- Only run this when the LuaRocks feature is installed-->
        <Custom Action="Configure.LuaRocks" After="InstallFiles">
          <![CDATA[Installed OR (&LuaRocks=3) AND NOT(!LuaRocks=3)]]>
        </Custom>
        <!--Option to Luarocks configuration file and Rocktree-->
        <Custom Action="Remove.ConfigAndRocktree.LuaRocks"
Before="RemoveFiles">
          <!--<![CDATA[REMOVE and (WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1)]]>-->
          <![CDATA[REMOVE]]>
        </Custom>
    </InstallExecuteSequence>
...

In the custom deferred action, I'm hoping to forgo the SQL at this point and just delete the files in C#, including the left over WinLua directories. Thoughts?

As usual, all the code is on github. The installer files are here:
https://github.com/WinLua/WinLua-Source-Code/tree/master/WinLua-Release3/WinLua-Installer.
The above WIX code is in
https://github.com/WinLua/WinLua-Source-Code/blob/master/WinLua-Release3/WinLua-Installer/WinLua-Installer/Code/Product.wxs

Cheers!
Russ

>
> Lots to think about and lots to try. Let me know how it goes and I'll 
> try to help more.
>
> --
> Edwin G. Castro
>
>
> On Tue, Feb 18, 2020 at 11:17 PM Russell Haley <russ.haley at gmail.com>
> wrote:
>
>>
>> On Tue, Feb 18, 2020 at 9:51 AM Edwin Castro <egcastr at gmail.com> wrote:
>>
>>> Am I reading this correctly? It appears the WriteConfigFile method 
>>> creates a file with static content. I am less sure about what the 
>>> RunConfigScript method actually does.
>>>
>>>
>>> https://github.com/WinLua/WinLua-Source-Code/blob/master/WinLua-Rele
>>> ase3/WinLua-Installer/Winlua.Installer.CustomAction/CustomAction.cs
>>>
>>
>> Hi Edwin, thanks so much for responding! The current code is static 
>> because I'm testing, but the real file contains paths that need to 
>> come from the installer. WriteConfigFile was my attempt to do that.
>> RunConfigScript is an existing hack to run an external script that 
>> creates the file I need (by elevating privilege). I don't like that; 
>> I want to internalize the process in the installer, with hopes to have a "modify"
>> option to re-generate the file (not yet though, future feature).
>>
>>
>>>
>>>
>>> If the file content is static then would it be possible to author 
>>> the config file with a static filename, something like 
>>> config-version.lua, and use an immediate custom action to update 
>>> just the FileName column in the appropriate row in the File table? 
>>> Something like that would get you all the content in the right 
>>> places and then you just fix up the one bit of data that varies by version at runtime.
>>>
>>> If the content for the config file really is dynamic, then could you 
>>> just generate the file at install time as you already do and use an 
>>> immediate custom action at uninstall time to add a row to the 
>>> RemoveFile table to remove the config file?
>>>
>> Yes, I'd be satisfied with the second suggestion (dynamic) if I can 
>> make it work. Thank you for the background information on 
>> Immediate/Deferred actions. If I understand, it seems that I need to 
>> write some properties during the immediate action and then in a 
>> deferred action, create the file based on the properties?
>> I found something here:
>> https://vadmyst.blogspot.com/2006/05/deferred-custom-actions-with-wix
>> .html but I want to set those properties in code as well?
>>
>> I don't know anything about adding a row to the RemoveFiles table yet.
>> Would that be done via SQL (or via a Record)?
>>
>> This is an innocuous question: Is this easier than writing a new file 
>> to the database and letting the installer write the file (assuming 
>> that's even going to work)? I suppose I would still need to manually remove the file?
>>
>> Your time is greatly appreciated. This has been hanging over my head 
>> for years!
>>
>> Regards,
>> Russ
>>
>>>
>>> --
>>> Edwin G. Castro
>>>
>>>
>>> On Tue, Feb 18, 2020 at 9:07 AM Edwin Castro <egcastr at gmail.com> wrote:
>>>
>>>> Hi Russell,
>>>>
>>>> I haven't quite followed exactly what you are trying to accomplish 
>>>> but I think I can give you a little context.
>>>>
>>>> The wcautil library is indeed a C++ library. It is a small wrapper 
>>>> library over most of the MSI API that you could read about on MSDN. 
>>>> I think the C# API you are using already provides all the goodies 
>>>> you need to do the transformations you want.
>>>>
>>>> The concept of a semi custom action is that you write an immediate 
>>>> mode custom action to change the msi database temporarily to add 
>>>> entries to msi tables. Those tables are then processed normally by 
>>>> the Windows Installer engine to do the "real" work. An example 
>>>> would be to dynamically add files to the RemoveFile table to delete 
>>>> files generated by the installed application at runtime. WiX has 
>>>> this custom action already baked in so you would not need to write it yourself but it is a good example.
>>>>
>>>> Immediate custom actions run in user context and cannot change 
>>>> protected system resources. Deferred custom actions can change 
>>>> protected system resources but they cannot change the msi database 
>>>> nor read/write most properties. Rollback custom actions are like 
>>>> deferred custom actions but they are used to "undo" actions taken by a deferred custom action.
>>>> There are also commit custom actions but I have not used them yet. 
>>>> The main point is that custom actions run at different times and 
>>>> have different responsibilities. You'll need to understand that 
>>>> context first before you go writing custom actions. See the following for more information.
>>>>
>>>>
>>>> http://lists.wixtoolset.org/pipermail/wix-users-wixtoolset.org/2018
>>>> -May/006932.html
>>>>
>>>> The Saw Tooth diagram article is missing but it can be found at
>>>>
>>>>
>>>> https://web.archive.org/web/20140412115309/http://flaming.com/image
>>>> s/SawTooth.PNG
>>>>
>>>> as mentioned in
>>>>
>>>>
>>>> http://lists.wixtoolset.org/pipermail/wix-users-wixtoolset.org/2019
>>>> -November/008511.html
>>>>
>>>> In any case, it sounds like you wrote an immediate custom action so 
>>>> you could modify the database but could not change the system by design.
>>>>
>>>> Let me re-read the thread more carefully and see if I can provide 
>>>> more tailored advice. I have a gut feeling that a semi custom 
>>>> action may not be appropriate if you feel you need to insert rows 
>>>> into the Component, FeatureComponent and File tables. Perhaps there 
>>>> is an easier way to handle all this.
>>>>
>>>> --
>>>> Edwin G. Castro
>>>>
>>>>
>>>> On Sun, Feb 16, 2020, 22:58 Russell Haley via wix-users < 
>>>> wix-users at lists.wixtoolset.org> wrote:
>>>>
>>>>> On Sun, Feb 16, 2020 at 8:44 PM Russell Haley 
>>>>> <russ.haley at gmail.com>
>>>>> wrote:
>>>>>
>>>>> > Thank you for the response, I didn't see this come through!l 
>>>>> > That's
>>>>> an
>>>>> > interesting post from 13 years
>>>>> >
>>>>> Thanks again for this but it seems to be a long shot. I have not 
>>>>> been able to find the wcautil library mentioned in the article:
>>>>> https://www.joyofsetup.com/2007/07/01/semi-custom-actions/. This 
>>>>> seems to be C++, is there a C# frontend? My attempts below seem to 
>>>>> be a failure...
>>>>>
>>>>> >
>>>>> > I started drilling into the MSI database information here:
>>>>> >
>>>>> https://docs.microsoft.com/en-us/windows/win32/msi/about-the-insta
>>>>> ller-database
>>>>> >
>>>>> > From what I can tell I need my custom action to run after the 
>>>>> > feature selection and then create my file. If my desired feature 
>>>>> > is
>>>>> selected, then
>>>>> > I need to insert into Component, FeatureComponent and File. I
>>>>> started going
>>>>> > down the SQL route. Here is a test custom action that is 
>>>>> > supposed to
>>>>> create
>>>>> > a new config file and add it to "Feature2" (NOTE: This code 
>>>>> > doesn't
>>>>> work
>>>>> > yet...or at all?).
>>>>> >
>>>>>
>>>>> > public class CustomActions
>>>>> >     {
>>>>> >         [CustomAction]
>>>>> >         public static ActionResult CustomAction1(Session session)
>>>>> >         {
>>>>> >             session.Log("Begin CustomAction1");
>>>>> >             if(session.Features.Contains("Feature2"))
>>>>> >             {
>>>>> >                 session.Log("TESTING: Found Feature2");
>>>>> >                 File.WriteAllText(@"C:\temp\mytemp1.txt", "This 
>>>>> > is a
>>>>> test
>>>>> > of the public broadcast system.");
>>>>> >                 var db = session.Database;
>>>>> >
>>>>> >                 Guid id = Guid.NewGuid();
>>>>> >                 session.Log("TESTING: id = " + id); //Used a 
>>>>> > string builder just because I thought the queries would get
>>>>> long
>>>>> >                 StringBuilder sb = new StringBuilder();
>>>>> >                 sb.Append("INSERT INTO `Component` 
>>>>> > (`ComponentId`,
>>>>> > `Directory_`) VALUES(?, ?)");
>>>>> >                 session.Log("TESTING: " + sb.ToString());
>>>>> >                 db.Execute(sb.ToString(), id, 
>>>>> > "feature2folder2");
>>>>> >
>>>>> >                 sb.Clear();
>>>>> >                 sb.Append("INSERT INTO `FeatureComponent`
>>>>> (`Feature_`,
>>>>> > `Component_`) VALUES(?, ?)");
>>>>> >                 session.Log("TESTING: FeatureComponent " +
>>>>> sb.ToString());
>>>>> >                 db.Execute(sb.ToString(), "Feature2", id);
>>>>> >
>>>>> >                 db.Execute("INSERT INTO `File` (`Component_`,
>>>>> `FileName`,
>>>>> > `FileSize`) VALUES (?, ?, ?)",
>>>>> >                     id, "config-5.3.lua", 1024);
>>>>> >                 session.Log("TESTING: File");
>>>>> >                 db.Commit();
>>>>> >             }
>>>>> >             return ActionResult.Success;
>>>>> >         }
>>>>> > }
>>>>> >
>>>>>
>>>>> My code doesn't seem to work. I cleaned up the SQL and made it one hard
>>>>> coded string.   I also queried the directory table and realized the
>>>>> folder
>>>>> name has the package id appended:
>>>>>
>>>>>     public class CustomActions
>>>>>     {
>>>>>         [CustomAction]
>>>>>         public static ActionResult CustomAction1(Session session)
>>>>>         {
>>>>>             session.Log("Begin CustomAction1");
>>>>>             if(session.Features.Contains("Feature2"))
>>>>>             {
>>>>>                 session.Log("TESTING: Found Feature2");
>>>>>                 File.WriteAllText(@"C:\temp\mytemp1.txt", "This is 
>>>>> a test of the public broadcast system.");
>>>>>                 var db = session.Database;
>>>>>
>>>>>                 PrintView(session);
>>>>>                 Guid id = Guid.NewGuid();
>>>>>                 session.Log("TESTING: id = " + id);
>>>>>
>>>>>                 //string InsertString = "INSERT INTO `Component` 
>>>>> (`ComponentId`, `Directory_`) VALUES(?, ?)";
>>>>>                 //session.Log("TESTING: " + InsertString);
>>>>>                 //var compRecord = session.Database.CreateRecord(2);
>>>>>                 //compRecord.SetString(1, id.ToString());
>>>>>                 //compRecord.SetString(2, "feature2folder2");
>>>>>                 //db.Execute(InsertString, compRecord);
>>>>>
>>>>>                 string InsertString =
>>>>>                     string.Format("INSERT INTO `Component` 
>>>>> (`ComponentId`,
>>>>> `Directory_`) VALUES('{0}', '{1}')",
>>>>>                     id.ToString(), 
>>>>> "feature2folder2.76597108_E928_4F11_BE09_3DC27AFA3AA3");
>>>>>
>>>>>                 session.Log("TESTING: " + InsertString);
>>>>>
>>>>>                 db.Execute(InsertString);
>>>>>
>>>>>                 InsertString = "INSERT INTO `FeatureComponent` 
>>>>> (`Feature_`,
>>>>> `Component_`) VALUES(?, ?)";
>>>>>                 session.Log("TESTING: FeatureComponent " + 
>>>>> InsertString);
>>>>>                 var FCRecord = db.CreateRecord(2);
>>>>>                 FCRecord.SetString(1, "Feature2");
>>>>>                 FCRecord.SetString(2, id.ToString());
>>>>>                 db.Execute(InsertString, FCRecord);
>>>>>
>>>>>                 db.Execute("INSERT INTO `File` (`Component_`, 
>>>>> `FileName`,
>>>>> `FileSize`) VALUES (?, ?, ?)",
>>>>>                     id, "config-5.3.lua", 1024);
>>>>>                 session.Log("TESTING: File");
>>>>>
>>>>>                 db.Commit();
>>>>>             }
>>>>>             return ActionResult.Success;
>>>>>         }
>>>>>
>>>>>         public static void PrintView(Session session)
>>>>>         {
>>>>>             //Database db2 = ins.OpenDatabase(strFileMsi, 
>>>>> WindowsInstaller.MsiOpenDatabaseMode.msiOpenDatabaseModeDirect);
>>>>>             View vw2 = session.Database.OpenView(@"Select * FROM 
>>>>> Directory");
>>>>>
>>>>>             vw2.Execute(null);
>>>>>             session.Log("Woot woot");
>>>>>             Record rcrd2 = vw2.Fetch();
>>>>>             while (rcrd2 != null)
>>>>>             {
>>>>>                 session.Log("Directory: " + rcrd2.GetString(1));
>>>>>                 session.Log("Parent_Directory: " + rcrd2.GetString(2));
>>>>>                 session.Log("DEFAULT: " + rcrd2.GetString(3));
>>>>>                 //rcrd2.set_StringData(1, "No data");
>>>>>
>>>>> //vw2.Modify(WindowsInstaller.MsiViewModify.msiViewModifyUpdate,
>>>>> rcrd2);
>>>>>
>>>>>                 rcrd2 = vw2.Fetch();
>>>>>
>>>>>             }
>>>>>         }
>>>>>
>>>>> Execution of the insert into component gives me "Function failed 
>>>>> during execution.". Here is my log files when I execute my custom query.
>>>>>
>>>>> TESTING: INSERT INTO `Component` (`ComponentId`, `Directory_`) 
>>>>> VALUES('8f26ef55-767b-4b27-aac5-b673694ddfb3',
>>>>> 'feature2folder2.76597108_E928_4F11_BE09_3DC27AFA3AA3')
>>>>> MSI (s) (88!F4) [21:35:14:523]: Note: 1: 2259 2:  3:  4:
>>>>> Exception thrown by custom action:
>>>>> System.Reflection.TargetInvocationException: Exception has been 
>>>>> thrown by the target of an invocation. --->
>>>>> Microsoft.Deployment.WindowsInstaller.InstallerException: Function 
>>>>> failed during execution.
>>>>>    at Microsoft.Deployment.WindowsInstaller.View.Execute(Record
>>>>> executeParams)
>>>>>    at 
>>>>> Microsoft.Deployment.WindowsInstaller.Database.Execute(String
>>>>> sql,
>>>>> Record record)
>>>>>    at 
>>>>> Microsoft.Deployment.WindowsInstaller.Database.Execute(String
>>>>> sqlFormat, Object args)
>>>>>    at CustomActionCreateFile.CustomActions.CustomAction1(Session
>>>>> session)
>>>>> in
>>>>>
>>>>> C:\Users\russh\source\repos\TestCreateActionInstaller\CustomAction
>>>>> CreateFile\CustomAction.cs:line
>>>>> 40
>>>>>    --- End of inner exception stack trace ---
>>>>>    at System.RuntimeMethodHandle.InvokeMethod(Object target, 
>>>>> Object arguments, Signature sig, Boolean constructor)
>>>>>    at 
>>>>> System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object
>>>>> obj,
>>>>> Object parameters, Object arguments)
>>>>>    at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, 
>>>>> BindingFlags invokeAttr, Binder binder, Object parameters, 
>>>>> CultureInfo culture)
>>>>>    at
>>>>>
>>>>> Microsoft.Deployment.WindowsInstaller.CustomActionProxy.InvokeCust
>>>>> omAction(Int32 sessionHandle, String entryPoint, IntPtr 
>>>>> remotingDelegatePtr) CustomAction CustomActionCreateFile returned 
>>>>> actual error code 1603 (note this may not be 100% accurate if 
>>>>> translation happened inside sandbox) Action ended 21:35:14: 
>>>>> CustomActionCreateFile. Return value 3.
>>>>>
>>>>> I fear this problem is a black hole of my time. I have in front of 
>>>>> me three possible options:
>>>>>
>>>>> - This option of adding to the database which seems fraught with 
>>>>> unknowns and half answers. A article from 13 years ago that 
>>>>> mentions an undocumented feature and solves a completely different 
>>>>> problem than mine is a little hard to put faith in.
>>>>> - I also have a custom action to simply create a file in the 
>>>>> installation folder but it doesn't work. The problem I am having 
>>>>> is my custom action doesn't seem to have administrative privileges 
>>>>> and I get an accessed denied error.
>>>>> - I have a third and final option which is to execute an external 
>>>>> Lua script with elevated privileges in the InstallFinalize step of 
>>>>> my installer. This has been tested and works, but it just feels 
>>>>> like cheating and my uninstall leaves files and folders around. I 
>>>>> can try and run a custom action to remove the left over but... :(
>>>>>
>>>>> I suppose in the end I'll have to settle for a hard coded file 
>>>>> name and copy the contents into the file with elevated privileges 
>>>>> after the fact.
>>>>> How is it that we have this huge "feature rich" MSI system and my 
>>>>> simple use case evades all but the most convoluted solutions? I'd 
>>>>> be relieved to know the answer is "You're doing it wrong" if 
>>>>> someone could also point me in the correct direction...
>>>>>
>>>>> Regards,
>>>>> Russ
>>>>>
>>>>>
>>>>> > However, what I don't know is how to point to the source file in 
>>>>> > the database? Do I need to put it in the database as a blob or a 
>>>>> > varchar
>>>>> or
>>>>> > something? Or can I just add a reference from the local temp
>>>>> directory?
>>>>> >
>>>>> > Thanks again!
>>>>> > Russ
>>>>> >
>>>>> >
>>>>> > On Thu, Feb 6, 2020 at 5:34 PM Blair Murri via wix-users < 
>>>>> > wix-users at lists.wixtoolset.org> wrote:
>>>>> >
>>>>> >> In a word: yes, for both questions. Look for Bob's blog entry 
>>>>> >> about "semi-custom" actions.
>>>>> >>
>>>>> >> Blair
>>>>> >>
>>>>> >> Get Outlook for Android<https://aka.ms/ghei36>
>>>>> >>
>>>>> >> ________________________________
>>>>> >> From: wix-users <wix-users-bounces at lists.wixtoolset.org> on 
>>>>> >> behalf
>>>>> of
>>>>> >> Russell Haley via wix-users <wix-users at lists.wixtoolset.org>
>>>>> >> Sent: Sunday, February 2, 2020 11:42:46 AM
>>>>> >> To: WiX Toolset Users Mailing List 
>>>>> >> <wix-users at lists.wixtoolset.org>
>>>>> >> Cc: Russell Haley <russ.haley at gmail.com>
>>>>> >> Subject: [wix-users] Custom Action to Create and Install a File?
>>>>> >>
>>>>> >> Hi,
>>>>> >>
>>>>> >> I'm pretty chuffed. I have a new installer for my WinLua
>>>>> distribution that
>>>>> >> now includes the Lua package manager called LuaRocks and a 
>>>>> >> custom
>>>>> compiler
>>>>> >> toolchain using llvm-mingw. My current installer has a 
>>>>> >> CustomAction
>>>>> that
>>>>> >> creates a config file for the package manager named for the
>>>>> appropriate
>>>>> >> version of Lua. The current version of Lua is 5.3.5, so the 
>>>>> >> config
>>>>> file
>>>>> >> needs to be named Config-5.3.lua. I currently save this file 
>>>>> >> under
>>>>> the
>>>>> >> install directory of <program files>\WinLua\LuaRocks.  This 
>>>>> >> custom
>>>>> action
>>>>> >> runs POST installation so I have the paths I need for creating 
>>>>> >> the contents of the file. I do not want this file in a user 
>>>>> >> accessible directory.
>>>>> >>
>>>>> >> My questions:
>>>>> >>
>>>>> >> 1) Can I use a custom action to create the file before the
>>>>> installation
>>>>> >> completes and ADD the config file to the list of files to be
>>>>> removed (I
>>>>> >> think that's termed the installer database)?
>>>>> >>
>>>>> >> 2) What about things like environment variables? Can I create 
>>>>> >> them
>>>>> based
>>>>> >> on
>>>>> >> installer information prior to the completion of the install
>>>>> process and
>>>>> >> have the installer remove them? Specifically I want to create 
>>>>> >> an environment variable called LUAROCKS_SYSCONFDIR and store 
>>>>> >> the path
>>>>> of the
>>>>> >> config-5.3.lua mentioned above. However, the name and path 
>>>>> >> won't be
>>>>> known
>>>>> >> until AFTER the file is created? I can already add to the PATH
>>>>> variable
>>>>> >> based on the installer paths, but this feels different.
>>>>> >>
>>>>> >> All my source code can be found here:
>>>>> >>
>>>>> >>
>>>>> https://github.com/WinLua/WinLua-Source-Code/tree/master/WinLua-Re
>>>>> lease3/WinLua-Installer
>>>>> >>
>>>>> >> Regards,
>>>>> >> Russell
>>>>> >>
>>>>> >> _______________________________________________________________
>>>>> >> _____ WiX Toolset Users Mailing List provided by FireGiant 
>>>>> >> http://www.firegiant.com/
>>>>> >>
>>>>> >> _______________________________________________________________
>>>>> >> _____ WiX Toolset Users Mailing List provided by FireGiant 
>>>>> >> http://www.firegiant.com/
>>>>> >>
>>>>> >
>>>>>
>>>>> __________________________________________________________________
>>>>> __ WiX Toolset Users Mailing List provided by FireGiant 
>>>>> http://www.firegiant.com/
>>>>>
>>>>

____________________________________________________________________
WiX Toolset Users Mailing List provided by FireGiant http://www.firegiant.com/



More information about the wix-users mailing list