[wix-users] Custom Action to Create and Install a File?
Russell Haley
russ.haley at gmail.com
Mon Mar 23 20:20:09 PDT 2020
On Sun, Mar 22, 2020 at 2:49 AM Rob Mensching via wix-users <
wix-users at lists.wixtoolset.org> wrote:
> Do consider rollback (install rollback, repair rollback, uninstall
> rollback, upgrade rollback) whenever writing a custom action that modifies
> system state.
>
I was that obvious, was I?
The way I (hope) I have implemented the installer, my install custom action
only runs after the installation sequence is completed. I'm hoping it will
only fire if the installation completes.
My remove custom action should happen after the uninstall is complete so
again, I'm hoping all rollbacks (the uninstall gets canceled?) happen prior
to my custom action. So if I was to consider a rollback, that would mean
ensuring that my custom action *doesn't* fire on cancellation of unintsall?
Cheers,
Russ
> 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/
>
> ____________________________________________________________________
> WiX Toolset Users Mailing List provided by FireGiant
> http://www.firegiant.com/
>
More information about the wix-users
mailing list