Removing attributes from elements - c#

I will start off with the code...
private static void File()
{
wixFile = XDocument.Load(filePath);
var fileElements = from file in wixFile.Descendants(GetWixNamespace() + "File")
select file;
foreach (var file in fileElements)
{
if(file.Attributes("Name").Any())
file.Attribute("Name").Remove();
}
wixFile.Save(filePath);
}
I actually check for quite a few attributes in the list and remove or correct them, but just to keep it short I took everything out except for one example of what I was doing. The problem that I am having is that this code is not removing the Name attribute from anything like I would expect it. When I put a break point in and watch it the "file" is getting updated as expected, but the changes are not going over to the wixFile when I save (or during the editing either). I am not entirely sure what I am missing here so any help would be greatly appreciated.
EDIT:
Here is a snippet of the code that is being changed:
<?define ApplicationPath=\\dwdata\develope\DocuWare\DW5\Mast_dsk\?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Directory Id="DATADIR" Name="data">
<Directory Id="mysql" Name="mysql">
<Component Id="procs_priv.MYI" Guid="{1148181A-4818-434F-B2D1-E4B417586168}">
<File Id="procs_priv.MYI" Name="procs_priv.MYI" KeyPath="yes" Source="$(var.ApplicationPath)Common\MySql\Server\data\mysql\procs_priv.MYI" />
</Component>
<Component Id="procs_priv.MYD" Guid="{A6688F48-71AF-4242-B6D0-CA69452A01B4}">
<File Id="procs_priv.MYD" Name="procs_priv.MYD" KeyPath="yes" Source="$(var.ApplicationPath)Common\MySql\Server\data\mysql\procs_priv.MYD" />
</Component>
<Component Id="procs_priv.frm" Guid="{3025C26C-8DFF-43D4-A62A-79E78D2D807D}">
<File Id="procs_priv.frm" Name="procs_priv.frm" KeyPath="yes" Source="$(var.ApplicationPath)Common\MySql\Server\data\mysql\procs_priv.frm" />
</Component>
<Component Id="proc.MYI" Guid="{FD4AA2E1-E059-4549-AE61-222878185A0A}">
<File Id="proc.MYI" Name="proc.MYI" KeyPath="yes" Source="$(var.ApplicationPath)Common\MySql\Server\data\mysql\proc.MYI" />
</Component>
<Component Id="proc.MYD" Guid="{12EE6EE8-AC44-4601-84C5-14B27CF9A3E6}">
<File Id="proc.MYD" Name="proc.MYD" KeyPath="yes" Source="$(var.ApplicationPath)Common\MySql\Server\data\mysql\proc.MYD" />
</Component>
<Component Id="proc.frm" Guid="{8A6F2928-5484-4B55-B75F-8475C684A091}">
<File Id="proc.frm" Name="proc.frm" KeyPath="yes" Source="$(var.ApplicationPath)Common\MySql\Server\data\mysql\proc.frm" />
</Component>
</Directory>
</Directory>
</Wix>
I am getting the exact same thing back as well. What I am trying to do is remove the "Name" attribute, but it just isn't working for whatever reason. The GetWiXNamespace() method returns the same namespace that is listed in the wix element as an XNamespace.

I'm going to read between the lines here and guess you are converting from an old version of WiX ( say 2.0 ) that required a File#Name attribute to a version ( say 3.0-3.6 ) that can infer this attribute and doesn't require it.
Here's some code that I just whipped up that I know works assuming the source xml is WiX 3.x. I'm not sure what is wrong with your code but perhaps the method that returns the namespace hasn't accounted for the url change.
XNamespace ns = "http://schemas.microsoft.com/wix/2006/wi";
var doc = XDocument.Load(#"C:\before.wxs");
var elements = from element in doc.Descendants( ns + "File")
where element.Attribute("Name") != null
select element;
foreach (var element in elements)
{
element.Attribute("Name").Remove();
}
doc.Save(#"C:\after.wxs");
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?define SourceDir="."?>
<Module Id="YourModuleHere" Language="1033" Version="1.0.0.0">
<Package Id="31c7722e-c2a0-4328-92a2-eacd443c10a9" Manufacturer="YourCompanyHere" InstallerVersion="200" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="MergeRedirectFolder">
<Component Id="owc7CEF9B0C53324FC23404D2AAAB5D12B8" Guid="6898e947-fa51-8efb-bda4-2e256dea8ed1">
<File Id="owf7CEF9B0C53324FC23404D2AAAB5D12B8" Name="bfsvc.exe" Source="$(var.SourceDir)\Windows\bfsvc.exe" KeyPath="yes" />
</Component>
<Component Id="owc8C6EA26177072C0006EEF8265FEF72A4" Guid="91737806-0b20-24ad-9653-cca05b5778fb">
<File Id="owf8C6EA26177072C0006EEF8265FEF72A4" Name="explorer.exe" Source="$(var.SourceDir)\Windows\explorer.exe" KeyPath="yes" />
</Component>
</Directory>
</Directory>
</Module>
</Wix>

Related

Ignore child order while comparing the xml nodes

I have two xml document such as:
aDoc.xml:
<update id="1293">
<abc>.....</abc>
<xyz>....</xyz>
<components>
<component name="C1">
<files source="s1" target="t1">
<file>**file2**</file>
<file>**file1**</file>
</files>
</component>
</components>
</update>
bDoc.xml:
<update id="1293">
<abc>.....</abc>
<xyz>....</xyz>
<components>
<component name="C1">
<files source="s1" target="t1">
<file>**file1**</file>
<file>**file2**</file>
</files>
</component>
</components>
</update>
what i have tried so far is:
var DiffInA = aDoc.Descendants("component").Cast<XNode>().Except(bDoc.Descendants("component").Cast<XNode>(), new XNodeEqualityComparer());
the above line returns the output as below:
<component name="C1">
<files source="s1" target="t1">
<file>file2</file>
<file>file1</file>
</files>
(the node which is present in aDoc but not part of bDoc)
But if you look at both the xml files the component C1 is same only the order in which the tag appears is different.
Is there any way to achieve this using linq to xml.(i dont want to use XmlDiff or other Api's.)

Wix Custom Action Implementation for Writing Installfolder in text

I have following Wix Code that is supposed to send the value of property to Custom Action Written in C#. Basically what I want is when the MSI is installed, I want to write the path of Folder where Wix installed the program in text file. I referred to this site and created code accordingly, but my Custom Action isn't working.
Following is my Wix File:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="SetupInstallFolder" Language="1033" Version="1.0.0.0" Manufacturer="LP" UpgradeCode="9e10a7d8-4ffb-493c-8318-c44ba4bc0c4c">
<Package InstallerVersion="200" Compressed="no" InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate />
<Feature Id="ProductFeature" Title="SetupInstallFolder" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="SetupInstallFolder" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component Id="SomeRandomEXE">
<File Source ="G:\SarVaGYa\myworkspace\LatestLpa\lpa\lpa_c\here\src\lpa\Release\lpa.exe" />
</Component>
</ComponentGroup>
<Binary Id="SetupCA2" src="G:\visual studio stuffs\SetupCAInstallFolder\SetupCAInstallFolder\bin\Release\SetupCAInstallFolder.CA.dll"/>
<CustomAction Id="INSTALLFOLDERFINDER" Execute="immediate" Property="INSTALLEDPATH" Value="[INSTALLFOLDER]" />
<InstallExecuteSequence>
<Custom Action="INSTALLFOLDERFINDER" Sequence="2"></Custom>
</InstallExecuteSequence>
</Fragment>
</Wix>
I have also given my C# code that is supposed to get the value and write it in file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Deployment.WindowsInstaller;
namespace SetupCAInstallFolder
{
public class CustomActions
{
[CustomAction]
public static ActionResult InstallFolderFinder(Session session)
{
session.Log("Here is the SetupCAInstallFolder");
string path = session["INSTALLEDPATH"];
session.Log("Installed Path is " + path);
System.IO.File.WriteAllText("F:\\pathgenerated.txt", path);
//System.IO.File.WriteAllText(path + "installed.txt", "sdkasdkasdlkasdk");
return ActionResult.Success;
}
}
}
The Wix file compiles and gives MSI that doesn't get the value of INSTALLEDPATH . If I add DllEntry="InstallFolderFinder" in CustomAction tag, it fails with error The CustomAction/#DllEntry attribute cannot coexist with a previously specified attribute on this element. The CustomAction element may only have one of the following target attributes specified at a time: DllEntry, Error, ExeCommand, JScriptCall, Script, Value, or VBScriptCall
How do I pass the value of INSTALLEDPATH to C# Custom Action?
I have fixed the issue after stumbling around some more site. I have added the code in gist. The Wix File Code is here and the C# Custom action code is here . Basically I added
two Custom tags in InstallExexuteSequeunce that first loads the dllentry and the second passes the parameter to Custom Action Written in C#.
MSI is determining the paths between the actions CostInitialize and CostFinalize.
Using hardcoded sequences is very rarely to recommend, and maybe you have chosen the wrong sequence number for this.
Try:
<InstallExecuteSequence>
<Custom Action='INSTALLFOLDERFINDER' After='CostFinalize'></Custom>
</InstallExecuteSequence>
I hope you are sure, INSTALLDEDPATH is your the correct property. The MSI base property for paths is `TARGETDIR.
If it still doesn't work, try a custom action type 51 with setting a property MYDUMMY on the value of [INSTALLEDPATH]. Now you can see, if at least the value is correctly written in a standard custom action not programmed.

How to install application in custom folder using wix installer,other than Program Files folder

I have created an installer using wix. By default the application gets installed under Program Files folder.I need to create a folder for my application under c: directory and install my application inside there.
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="WINDOWSVOLUME" >
<Directory Id="INSTALLLOCATION" Name="WIXDemoApp">
</Directory>
</Directory>
</Directory>
<SetDirectory Id="WINDOWSVOLUME" Value="c"/>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<!-- TODO: Remove the comments around this Component element and the ComponentRef below in order to add resources to this installer. -->
<Component Id="MyApplication.exe">
<File Source="$(var.MyApplication.TargetPath)" Name="MyApp.exe" Id="MYAPPEXE" KeyPath="yes" />
<!-- TODO: Insert files, registry keys, and other resources here. -->
</Component>
</ComponentGroup>
</Fragment>
I am getting the following error "error LGHT0094: Unresolved reference to symbol 'Directory:INSTALLFOLDER' in section 'Fragment:'".
Update:
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="WindowsVolume" >
<Directory Id="INSTALLLOCATION" Name="WIXDemoApp">
</Directory>
</Directory>
</Directory>
<SetDirectory Id="WindowsVolume" Value="c"/>
</Fragment>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLLOCATION">
<!-- TODO: Remove the comments around this Component element and the ComponentRef below in order to add resources to this installer. -->
<Component Id="MyApplication.exe">
<File Source="$(var.MyApplication.TargetPath)" Name="MyApp.exe" Id="MYAPPEXE" KeyPath="yes" />
<!-- TODO: Insert files, registry keys, and other resources here. -->
</Component>
</ComponentGroup>
</Fragment>
This is giving me another error "error LGHT0204: ICE99: The directory name: WindowsVolume is the same as one of the MSI Public Properties and can cause unforeseen side effects.".Googled and refereed this and this to fix this.But not working for me,still i am getting the same error as "error LGHT0204: ICE99: The directory name: WindowsVolume is the same as one of the MSI Public Properties and can cause unforeseen side effects.".Any idea what would be the problem.
Windows Installer is case-sensitive so WINDOWSVOLUME won't work. You can do something like this:
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLLOCATION" Name="SetupProject1" />
</Directory>
</Directory>
<SetDirectory Id="INSTALLLOCATION" Value="[WindowsVolume]SetupProject1" />
</Fragment>
For your second error, you're mixing two different ids: INSTALLFOLDER and INSTALLLOCATION. Pick one and use it in both places.
I found this tip on kentie.net - Wix Tips & Tricks. Tips said to use the WINDOWSVOLUME id.
TARGETDIR and the system partition
When trying to install to a subdirectory of the system drive root
(e.g. 'C:\application'), it might sense to assume that in something
like
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="INSTALLLOCATION" Name="SetupProject1">
</Directory>
</Directory>
TARGETDIR refers to the system partition, as ProgramFilesFolder is
always given as a child of TARGETDIR. This is not the case; TARGETDIR
is the partition with the most free disk space. It can even be a
partition on an external hard drive. To set it to the true system
partition, use the below approach:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="WINDOWSVOLUME" >
<Directory Id="INSTALLLOCATION" Name="SetupProject1">
</Directory>
</Directory>
</Directory>
<SetDirectory Id="WINDOWSVOLUME" Value="[WindowsVolume]"/>
The SetDirectory element
is required as trying to use WindowsVolume directly results in
error LGHT0204: ICE99: The directory name: WindowsVolume is the same as one of the MSI Public Properties and can cause unforeseen side effects.
Signing MSIs

How to make Wix call function inside a DLL without errors?

My issue is that clearly windows has issues accessing the dll InstallTools.dll since I'm calling a function inside OpenExeUrl
Here is my dll content
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Deployment.WindowsInstaller;
using System.Configuration;
using System.Diagnostics;
namespace InstallTools
{
public class InstallHelper
{
[CustomAction]
public static ActionResult OpenExeUrl(Session session)
{
try
{
var exeUrl = InstallTools.Properties.Settings.Default.EXEURL;
// Prepare the process to run
ProcessStartInfo start = new ProcessStartInfo();
int exitCode = 0;
// Enter in the command line arguments, everything you would enter after the executable name itself
//start.Arguments = arguments;
// Enter the executable to run, including the complete path
start.FileName = exeUrl;
// Do you want to show a console window?
start.WindowStyle = ProcessWindowStyle.Maximized;
start.CreateNoWindow = true;
// Run the external process & wait for it to finish
using (Process proc = Process.Start(start))
{
proc.WaitForExit();
// Retrieve the app's exit code
exitCode = proc.ExitCode;
}
}
catch (Exception e)
{
var errorMessage = "Cannot open exe file ! Error message: " + e.Message;
session.Log(errorMessage);
}
return ActionResult.Success;
}
}
}
The Wix file content :
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="MySetup" Language="1033" Version="1.0.0.0" Manufacturer="Sofar" UpgradeCode="c151e7ab-b83a-445f-93b2-2ab7122ea34b">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate />
<Feature Id="ProductFeature" Title="MySetup" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
<Binary Id="InstallTools" SourceFile="$(var.SolutionDir)InstallTools\bin\$(var.Configuration)\InstallTools.dll"/>
<Binary Id="NotepadPlus" SourceFile="C:\Program Files (x86)\Notepad++\notepad++.exe"/>
<CustomAction Id="OpenExe" BinaryKey="InstallTools" DllEntry="OpenExeUrl" Execute="deferred" Impersonate="yes" />
<!--<CustomAction Id="OpenExe" Return="ignore" Directory="ProgramFilesFolder" ExeCommand="C:\Program Files (x86)\Notepad++\notepad++.exe" Impersonate="yes" Execute="deferred"/>-->
<InstallExecuteSequence>
<Custom Action="OpenExe" Before='InstallFinalize'/>
</InstallExecuteSequence>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="MySetup" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<DirectoryRef Id="INSTALLFOLDER">
<Component Id="myAppFile">
<File Source="$(var.MyApplication.TargetPath)" />
</Component>
</DirectoryRef>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<!-- TODO: Remove the comments around this Component element and the ComponentRef below in order to add resources to this installer. -->
<!-- <Component Id="ProductComponent"> -->
<ComponentRef Id="myAppFile" />
<!-- </Component> -->
</ComponentGroup>
</Fragment>
</Wix>
You can notice that I'm calling the dll by building the custom action and add it to execution sequence.
<CustomAction Id="OpenExe" BinaryKey="InstallTools" DllEntry="OpenExeUrl" Execute="deferred" Impersonate="yes" />
<!--<CustomAction Id="OpenExe" Return="ignore" Directory="ProgramFilesFolder" ExeCommand="C:\Program Files (x86)\Notepad++\notepad++.exe" Impersonate="yes" Execute="deferred"/>-->
<InstallExecuteSequence>
<Custom Action="OpenExe" Before='InstallFinalize'/>
</InstallExecuteSequence>
My issue is that when I launch the installer I get this error shown in the following screenshot.
Could you please advice ?
1) Make your CustomAction class public: public class InstallHelper.
2) Make sure that you're referencing the correct DLL, it should end up with *.CA.dll extension. Your bin folder has supposedly two dll files: InstallTools.dll, InstallTools.CA.dll.

WiX to install EventSource

I created my event source based on this example. My event source looks like this:
[EventSource(Name = "Samples-EventSourceDemos-EventLog")]
public sealed class MinimalEventSource : EventSource
{
public static MinimalEventSource Log = new MinimalEventSource();
[Event(1, Message = "{0} -> {1}", Channel = EventChannel.Admin)]
public void Load(long baseAddress, string imageName)
{
WriteEvent(1, baseAddress, imageName);
}
}
The example use code to simulate install/uninstall process. From some other SO questions, I saw another example of installing event source using event message file.
But it is missing some good examples how to install/register the EventSource that defined by manifests. I am investigating using CustomAction to do something like:
wevtutil.exe im <EtwManifestManFile> /rf:"EtwManifestDllFile" /mf:"EtwManifestDllFile"
but wondering if you have any suggestions?
Figured out after some search. In the WixUtilExtension, there is build-in support. After referencing it and adding namespace, it is pretty easy.
Here are the changes in the Product.wxs:
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
...
<Component Id="etwManifest.dll">
<File Id="etwManifest.dll" KeyPath="yes"
Source="$(var.SampleClassLibrary.TargetDir)\SampleClassLibrary.Samples-EventSourceDemos-EventLog.etwManifest.dll" />
</Component>
<Component Id="etwManifest.man">
<File Id="etwManifest.man" KeyPath="yes"
Source="$(var.SampleClassLibrary.TargetDir)\SampleClassLibrary.Samples-EventSourceDemos-EventLog.etwManifest.man">
<util:EventManifest MessageFile="[etwManifest.dll]" ResourceFile="[etwManifest.dll]"></util:EventManifest>
</File>
</Component>
</Wix>
The only trick I had to do is reduce the length of component Id and File Id (used to match with the file names) to avoid the following error: Error 25540. There was a failure while configuring XML files.
For me, the sollutions was to add permission to both files, the .dll and the .man. (https://stackoverflow.com/a/32727624/5500092)
<util:PermissionEx User="Everyone" ReadPermission="yes" ReadAttributes="yes" ReadExtendedAttributes="yes" />
I also had to add the full path to correctly registrate the manifest file. When i didn't do that, the manifest file still refer to the bin/release folder of my visual studio solution.
<util:EventManifest MessageFile="[INSTALLFOLDER]SampleClassLibrary.Samples-EventSourceDemos-EventLog.etwManifest.dll" ResourceFile="[INSTALLFOLDER]SampleClassLibrary.Samples-EventSourceDemos-EventLog.etwManifest.dll"/>
Code:
<DirectoryRef Id="INSTALLFOLDER">
<Component Id="etwManifest.dll" Guid="PUT-GUID-HERE">
<File Id="etwManifest.dll" KeyPath="yes" Source="$(var.SampleClassLibrary.TargetDir)\SampleClassLibrary.Samples-EventSourceDemos-EventLog.etwManifest.dll" >
<util:PermissionEx User="Everyone" ReadPermission="yes" ReadAttributes="yes" ReadExtendedAttributes="yes" />
<util:PermissionEx User="Administrators" ReadPermission="yes" ReadAttributes="yes" ReadExtendedAttributes="yes"/>
</File>
</Component>
<Component Id="etwManifest.man" Guid="PUT-GUID-HERE">
<File Id="etwManifest.man" KeyPath="yes" Source="$(var.SampleClassLibrary.TargetDir)\SampleClassLibrary.Samples-EventSourceDemos-EventLog.etwManifest.man" >
<util:PermissionEx User="Everyone" ReadPermission="yes" ReadAttributes="yes" ReadExtendedAttributes="yes"/>
<util:PermissionEx User="Administrators" ReadPermission="yes" ReadAttributes="yes" ReadExtendedAttributes="yes"/>
<util:EventManifest MessageFile="[INSTALLFOLDER]SampleClassLibrary.Samples-EventSourceDemos-EventLog.etwManifest.dll" ResourceFile="[INSTALLFOLDER]SampleClassLibrary.Samples-EventSourceDemos-EventLog.etwManifest.dll"/>
</File>
</Component>
</DirectoryRef>
Using WiX Toolset v3.10
(Please do correct me if my English is bad)

Categories