I have a scenario in which I need to update a ini file during installation. I am using the IniFileElement thusly:
<Component Id="TestIni"
Guid="{058D0B2B-0145-4743-969F-C5AACA992936}" Directory="INSTALLFOLDER">
<CreateFolder />
<IniFile Id="Ini2" Action="addLine"
Directory="INSTALLFOLDER" Name="File.conf"
Key="theKey"
Value="[ComputerName]" />
</Component>
But it does not work since the Section attribute is missing. Is there a way I can write to the "default" section of the IniFile? My Ini file does not have sections defined.
Here's what I can tell you... the WiX XSD for the section attribute to exist and have a value. The underlying Windows Installer IniFile table also says that the Section column cannot be null. It also says it's of type Formatted which means I can give it a property name for a value. Just to really push the limites I tried to give it a null property ( [FAKE] ) and see what would happen. It failed with an error 2109 which is Section missing for .ini action.
So I'll admit, I'm getting a little old and forgetful.... In all my years, I don't recall INI values without sections. According to Wikipedia "Keys may (but need not) be grouped into arbitrarily named sections." I don't believe this though. There are no references for this assertion and the references that are listed are for ".conf" files (which are not .INI files) and a bunch of suspect web sites.
Let's dig a little deeper. GetPrivateProfileString function says "The name of the section containing the key name. If this parameter is NULL, the GetPrivateProfileString function copies all section names in the file to the supplied buffer."
WritePrivateProfileString function says for lpAppName "The name of the section to which the string will be copied. If the section does not exist, it is created. The name of the section is case-independent; the string can be any combination of uppercase and lowercase letters." It does not say that it can be null.
So, in my opinion INI files cannot / should not have keys without sections. Your ".conf" file may look like an INI file but it is not an INI file and you'll need custom actions to do search and replaces. Either that or you redesign your application (if possible) to conform to INI specifications.
Related
I am using Wix to make an installer and I was wondering how I indirectly get a property's value.
Like if I have a Property WIXUI_INSTALLDIR that is preset with the INSTALLFOLDER value
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER" />
and this Property then is changed through a a Dialog from user input and I after that would want to get what the user changed the path to, how do I get the "value" out of WIXUI_INSTALLDIR ?
I would like to send this value (a string I suppose) to either the registry or a custom action.
If I try [WIXUI_INSTALLDIR] I get a warning for illegal indirection if used with a custom action and if I try to send it to registry it just becomes "[WIXUI_INSTALLDIR]" as a string.
EDIT:
My problem is when also using my own made up properties like this:
Property Id="MyOwnProperty" Value="Hello There"
and let's say a Dialogue changes the value of MyOwnProperty, how do I the access the new Value ? Like sending [MyOwnProperty] to a CustomAction as parameter will only get "[MyOwnProperty]" as a literal string.
Try getting the value of the Property (or Directory) that WIXUI_INSTALLDIR references. In this case, use [INSTALLFOLDER] in the formatted field where you'd like the value.
I am facing issue for RegistrySearch for multistring, where as string search working fine.
Checked in installation logs
Action start 13:40:07: AppSearch. MSI (s) (40:E0) [13:40:07:381]:
PROPERTY CHANGE: Adding MYKEY property. Its value is ''. MSI (s)
(40:E0) [13:40:07:381]: PROPERTY CHANGE: Adding MYSERVICE property.
Its value is 'myvalue2'.
I have trimmed some logs here
Action ended 13:40:51:
ScheduleReboot. Return value 1. Action ended 13:40:51: INSTALL. Return
value 1. You must restart your system for the configuration changes
made to XXXXX to take effect. Click Yes to restart now or No if you
plan to manually restart later. Property(S): UpgradeCode =
{XXXXXX-XXXX-XXX-XXXX-XXXXXXXX}
Property(S): MYKEY = [~]myvalue1[~] Property(S): MYSERVICE = myvalue2
At end of installation it seems it has correctly evaluated the MYKEY but not during AppSearch, resulting in failing my Condition evaluation
<Feature Id="MyFeature" Level="" Display="" Title="" Description="" AllowAdvertise="no" ConfigurableDirectory="INSTALLDIR">
<MergeRef Id="MyFeature" Primary="yes"/>
<Condition Level="0">((MsiNTProductType=1) OR
(MYKEY="[~]MyValue[~]") OR
(MYSERVICE="MyService" AND MYKEY=""))</Condition>
</Condition>
</Feature>
<Property Id="MYKEY" Secure="yes">
<RegistrySearch Id="MyKey"
Root="HKLM"
Key="SYSTEM\CurrentControlSet\Services\MyService"
Name="mykey"
Type="raw" />
</Property>
<Property Id="MYSERVICE" Secure="yes">
<RegistrySearch Id="MYSERVICE"
Root="HKLM"
Key="SYSTEM\CurrentControlSet\Services\MyService"
Name="DisplayName"
Type="raw" />
</Property>
UPDATE: I might have missed you stating it already, but when checking whether the property set by the AppSearch search has any assigned value at all using simply PROPERTYNAME as condition, the condition shows up as true - meaning that "something" exists in the property in question, the text is just not displayed.
Is it sufficient to test just for the presence of a value, or do you need to check the specific value of MYKEY? If the mere presence of a value is enough, then you may be able to use this condition:
((MsiNTProductType=1) OR (MYKEY) OR (MYSERVICE="MyService" AND MYKEY=""))
I guess this answer from Rob Mensching from the WiX-users mailing list answers the question with certainty. Multi-string is simply not supported for AppSearch.
There is no need to doubt the accuracy of this since Rob was on the original MSI team. You need to abandon this approach. Sorry to say. Unless the above workaround that I just added could work (check not the value, but if there is a value at all being retrieved from the registry).
A couple of other, potential workarounds:
You could read the multi-string from a custom action. I just verified that it works with a test VBScript - the forbidden MSI tool :-).
Could you search for a file or directory on disk that would signify the same thing you retrieve from the registry with this multi string?
As my motto goes every now and then: let's obsess over this (as opposed to: "careful, we don't want to learn from this" - which is another motto of mine - which tends to be the better option).
It is truly odd, that I can replicate what you state about your log file. I see a CommandLine entry which shows the multi-sting correctly, albeit with several extra null characters (slightly shortened log entry):
CommandLine: NORMALSTRING="sample regular string" MULTISTRING="[~~~]String 1[~~~]String 2[~~~]String 3[~~~]" INSTALLFOLDER="C:\Program Files (x86)\WiX3_GenericTestProject\" TARGETDIR="C:\" ACTION="INSTALL" EXECUTEACTION="INSTALL" ROOTDRIVE="C:\" INSTALLLEVEL="1" SECONDSEQUENCE="1" ADDLOCAL=Empty,Modules,ProductFeature
and also, later in the log file, after InstallFinalize:
Property(S): MULTISTRING = [~]String 1[~]String 2[~]String 3[~]
I really don't understand how that comes about. Somehow the AppSearch must have really set the property in question even if it didn't look like it did - the property just can't be retrieved correctly (or formatted correctly), and hence doesn't work in (feature) conditions either?
Maybe the underlying data model in Windows Installer has stored the retrieved registry multi string value as a BSTR (the abomination of a COM string format which allows embedded nulls and can be compiled and linked without being properly allocated / constructed via SysAllocateString - "burnt child, smells burnt - and all that...").
Anyway, I suppose AppSearch expectes a regular, null-terminated string buffer and interprets the BSTR as such? Hence stumbling on the first null value which is the first character of the data string section of the BSTR (not the length prefix section - the BSTR pointer points 4 bytes into the allocated BSTR memory) and reports an empty string overall? The property values that show up in the log file must have been read directly from the underlying data model by other means? I would assume the MSI Win32 C++ functions? But wouldn't that also be the case for AppSearch? Something is wrong with how this property string - with embedded nulls - is being displayed and used in conditions.
So in summary: maybe the retrieval of the multi string actually works, but the exposed value via Session.Property("PROP") erroneously reads the potential, native BSTR as a null-terminated string buffer and interprets the leading null as the end of the string buffer? Sort of doesn't make sense considering Session.Property is a COM call and should definitely understand a BSTR? Theories like these are never correct, but maybe they can help create some new ideas at least. What seemed like a missing Windows Installer feature, sort of smells like a bug I think. Or as it is in the real world: a technical problem, not easily fixed and hence seen and accepted as a missing feature.
Let me link together your questions on this issue for reference (and a couple of other answers):
RegistryValue Element of type multiString.
Failing condition wix.
Passing multiString values to installer through command-line.
I have a Merge Module which installs a file. I would like to use a property passed to the Merge Module during MSI creation. Something like:
<Configuration Name='FileNameProperty'
Format='Text'
DefaultValue='[FileNameProperty]' />
<Substitution Table='CustomAction'
Row='SetFileName'
Column='Target'
Value='[=FileNameProperty]' />
<CustomAction Id='SetFileName'
Property='MYFILENAME'
Value='[MYFILENAME]' />
<InstallExecuteSequence>
<Custom Action='SetFileName'
Before='LaunchConditions'>1</Custom>
</InstallExecuteSequence>
...
<File Name="[MYFILENAME]"
Source="my-file.exe" />
Currently I am using a Custom Action, namely Type 51, which works when substituting property values for other element attributes, such as ServiceInstall DisplayName. However, in this instance the file is deployed as [MYFILENAME].
I've looked through the other Custom Actions provided by MSDN, but can't find anything that fits this situation. Any suggestions or idea if this is even possible?
My last ditch option is to include File elements for each variation of file name and select the desired file based off condition, but I would like to avoid that.
File names aren't formatted so properties can't be used. If you use multiple files, WiX's smart cabbing ensures the copies don't take up extra space in your cabinet.
I have a WiX installer project that I'm creating where I'd like the installer to check to see if another application is already installed on the user's machine. If it is, then I'd like to set the install level of one of the features to "1", otherwise it should remain hidden (i.e. install level = 0). To find out where the application is installed, I first do a registry search:
<Property Id="MYAPPINSTALLFOLDER">
<RegistrySearch Id='InstallPathRegistry'
Type='raw'
Root='HKLM'
Key='SOFTWARE\SomeLongAppPath' Name='FileName'
Win64='yes'/>
</Property>
You'll notice that the registry value that I end up getting is actually the directory of the installed application including the actual program name with extension (let's say myapp.exe). So, once I get the full path of the installed application, I check to see if the file exists:
<Property Id="MYAPPINSTALLED">
<DirectorySearch Id="CheckFileDir" Path="[MYAPPINSTALLFOLDER]" AssignToProperty="yes">
<FileSearch Id="CheckFile" Name="myapp.exe" />
</DirectorySearch>
</Property>
Now, what I would expect to see is that if the file actually exists in that location, then the Property called "MYAPPINSTALLED" would be set to 1, otherwise it would be 0. Then, when I setup my features I use something like this:
<Feature Id="ThirdPartyPlugins" Title="Third Party Plugins" Level="0">
<Condition Level="1">MYAPPINSTALLED</Condition>
<ComponentGroupRef Id="MyAppPlugin" />
</Feature>
However, when I run my installer the third party plugin feature is always hidden. I've enabled msi datalogging by setting the property like this:
<Property Id="MsiLogging" Value="voicewarmupx"/>
And when I check the log file I can definitely see that the MYAPPINSTALLFOLDER property gets changed to the correct file path when it does the registry search. However, if I search the log for the property MYAPPINSTALLED, then I can see the following:
AppSearch: Property: MYAPPINSTALLED, Signature: CheckFileDir
Action ended 15:55:06: AppSearch. Return value 1.
So, it looks like it worked, however it doesn't seem to ever set the Property to equal the search value. Am I doing something wrong? Can someone explain why my feature install level never gets set to 1 even though the application file exists?
Edit
Ok, after more debugging... I think the issue is that the directory search is trying to use a path that includes the file name and extension (i.e. C:/Program Files/MyApp/myapp.exe") instead of just the directory where the file comes from. This is because the registry search has the full path including the file name stored (but not just the install directory). If I do a directory search just using the correct absolute directory (not using the registry search) then the process works. So, my follow up question is... my Property MYAPPINSTALLFOLDER contains the full path with file name and extension. Is there a way to strip the file name and extension from this property so that I just have the proper directory name to search for?
You're checking to see if another application is installed but that's rather a long way around. Also, the file search returns a path, not zero or 1, but either way a full verbose log should tell you if the properties are being set. It might help if you could post the entire log somewhere rather than the parts you think are the only relevant ones. e.g. There's probably an AppSearch in the execute sequence for silent installs.
It's requently easier to do a single search for other applications that were installed with MSI packages in these ways:
If you know the other product's UpgradeCode (and version ranges if applicable) then add Upgrade/UpgradeVersion elements with onlydetect set to yes, and that search will set a property if the product is detected.
If you know (or can find out) the Component id of any of the relevant components from that other product, then you can use them in a WiX ComponentSearch. If you get the target property set then that component is installed. This post contains a couple of ways to find out component guids:
How to find out which application requires a certain assembly from GAC?
It's also puzzling that the AppSearch log extract you posted only refers to one property. The Directory/FileSearch is also an AppSearch, so if the MSI actually contains two searches in AppSearch there should be references to all the properties being set. Again, that's a reason to post the entire log and look in the MSI file for those searches. The RegLocator search is documnented to occur before the DRLocator, so why is there no MYAPPINSTALLFOLDER property in the AppSearch log entry? You're not on a 32-bit system are you? (noticing the win64 search).
Per the WiX documentation:
Use the AssignToProperty attribute to search for a file but set the outer property to the directory containing the file. When this attribute is set to yes, you may only nest a FileSearch element with a unique Id or define no child element [of the DirectorySearch].
I added the text in the brackets to make it more clear.
So, after reading this sentence a few times and cross referencing your WiX XML, I think I see what the problem is with your current WiX XML. You perform a separate registry search from the directory search. Instead, you should nest these. There are two ways to perform the search, depending on what you want to do. One way is to simply retrieve the registry value from the registry, and if the value exists, then you make the assumption that the feature's required application is installed, at which point you appropriately set a property that would enable hiding/showing the feature within your installer's feature selection tree. The other way is to actually find the file you're interested in, using the results of the registry search as the basis for the file search.
Below is the XML for just a registry search, which doesn't check that the file actually exists on disk. You're making the assumption that if this registry value exists, the file is installed and available.
<Property Id="MYAPPINSTALLFOLDER">
<RegistrySearch Id='InstallPathRegistry'
Type='raw'
Root='HKLM'
Key='SOFTWARE\SomeLongAppPath' Name='FileName'
Win64='yes'/>
</Property>
<Property Id="SHOW_APP_FEATURE" Value="hidden" />
<SetProperty Id="SHOW_APP_FEATURE" Value="collapse" Sequence="both" After="CostFinalize">
<!-- If MYAPPINSTALLFOLDER is defined and contains any non-empty value, this
evaluates to TRUE; otherwise, it evaluates to FALSE.
-->
MYAPPINSTALLFOLDER
</SetProperty>
<!-- You could also be more explicit:
<SetProperty Id="SHOW_APP_FEATURE" Value="collapse" Sequence="both" After="CostFinalize">
<![CDATA[MYAPPINSTALLFOLDER <> ""]]>
</SetProperty>
-->
<Feature Id="MyAwesomeFeature" Title="My Awesome App Feature"
Display="[SHOW_APP_FEATURE]">
... <!-- Component/ComponentRefs go here -->
</Feature>
If you want to ensure that, even if the registry value exists in the registry, that the file it points to is 1) actually a file path; and 2) that the file actually exists on disk, then you need to perform a nested file search within a directory search, which itself is nested within a registry search. You would again need to use a SetProperty custom action to set a property that would enable the hiding/showing of the feature within your installer's feature selection tree. Here's the XML for this search:
<!-- Performing a FileSearch nested within a DirectorySearch,
which is itself nested within a RegistrySearch
This search twill ensure that the file exists on disk, and
if so, assign the full filename and path to the
MYAPPINSTALLFOLDER property.
-->
<Property Id="MYAPPINSTALLFOLDER">
<RegistrySearch Id='InstallPathRegistry'
Type='raw'
Root='HKLM'
Key='SOFTWARE\SomeLongAppPath' Name='FileName'
Win64='yes'>
<DirectorySearch Id='InstallPathDirectory' AssignToProperty='yes'>
<FileSearch Id='InstallPathFile' Name='myapp.exe' />
</DirectorySearch>
</RegistrySearch>
</Property>
<Property Id="SHOW_APP_FEATURE" Value="hidden" />
<SetProperty Id="SHOW_APP_FEATURE" Value="collapse" Sequence="both" After="CostFinalize">
<!-- If MYAPPINSTALLFOLDER is defined and contains any non-empty value, this
evaluates to TRUE; otherwise, it evaluates to FALSE.
-->
MYAPPINSTALLFOLDER
</SetProperty>
<Feature Id="MyAwesomeFeature" Title="My Awesome App Feature"
Display="[SHOW_APP_FEATURE]">
... <!-- Component/ComponentRefs go here -->
</Feature>
This should allow you to accomplish what you're trying to achieve.
After following the struts 2 web pages and numerous examples, my application still will not pick up values from the struts.properties file.
I am trying this in order to give some values a money type format:
<s:property value="getText('struts.money.format',{value})" />
My struts.properties file which is under WEB-INF/classes and therefore visible has the following single line
struts.money.format= {0,number,\u00A4##0.00}
I get the string struts.money.format printed to the screen. If I change the first parameter of the getText call, the new string I put also will get printed instead of a true lookup happening.
If I do <s:property value="value" /> I will get back a proper number. If I drop the second argument on the getText call, I would expect to get back the right hand side of the assignment in the properties file, but i get the same struts.money.format back.
I am using Tomcat 6 with Struts 2.2.1.1. Is there an additional part of the puzzle I am possibly leaving out?
So in my struts.xml file, I put this line
<constant name="struts.custom.i18n.resources" value="struts" />
It needs this to know that I am trying to use a struts.properties file. I had assumed that by default a file named struts.properties was along the chain of places to look for a constant such as this. Only if you named it something else did you need to specify that. Even though it is in WEB-INF/classes which is recommended by the Struts 2 documentation, it just simply was not looking in this file.
EDIT
For what it is worth, I also had to modify my struts text tag like so
<s:property value="getText('struts.money.format',{#java.lang.Double#valueOf(value)})" />
Actually, value should have been a BigDecimal, but it was being treated at the view level here as java.lang.String. The problem is that some of the String objects had exponential formatting (like 1.642E07 or something to that effect) and the struts formatter could not handle the exponential formatting. The valueOf eliminates this exponential fomatting