[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