Search code examples
xmlmsbuildeventlog-sourcexmlpoke

How to change the value of an attribute in an instrumentation manifest with msbuild?


The Microsoft EventRegister Tool creates an instrumentation manifest file along with a resource file during project compilation. I would like to move those files to another path after compilation and change two attributes in the instrumentation manifest file with msbuild. The values of the attributes are the same, each one represents the path of the accompanying resource file. It seems I cannot get the syntax for modifying the attributes with msbuild right, and I figure it has something to do with two things.

First, the instrumentation manifest file does not contain a classic xml file declaration. Second, the instrumentation manifest includes namespaces.

What I have come up with so far, thanks to the blog post "Updating XML files with MSBuild" by Sayed Ibrahim Hashimi, is this:

<PropertyGroup>
    <SourceManifestAssembly>$(OutputPath)Name.etwManifest.dll</SourceManifestAssembly>
    <DestinationManifestAssembly>$(Programdata)\MyCompany\MyProduct\1.0.0.0\Name.etwManifest.dll</DestinationManifestAssembly>
    <SourceManifest>$(OutputPath)Name.etwManifest.man</SourceManifest>
    <DestinationManifest>$(Programdata)\MyCompany\MyProduct\1.0.0.0\Name.etwManifest.man</DestinationManifest>
</PropertyGroup>
<ItemGroup>
    <UpdateManifest Include="UpdatemessageFileName">
        <NewValue>$(DestinationManifestAssembly)</NewValue>
        <Namespaces>&lt;Namespace Prefix='x'  Uri='http://schemas.microsoft.com/win/2004/08/events' /&gt;</Namespaces>
        <XPath>//x:events/provider/@messageFileName</XPath>
    </UpdateManifest>
    <UpdateManifest Include="UpdateresourceFileName">
        <NewValue>$(DestinationManifestAssembly)</NewValue>
        <Namespaces>&lt;Namespace Prefix='x'  Uri='http://schemas.microsoft.com/win/2004/08/events' /&gt;</Namespaces>
        <XPath>//x:events/provider/@resourceFileName</XPath>
    </UpdateManifest>
</ItemGroup>
<Target Name="AfterBuild">
    <Copy SourceFiles="$(SourceManifestAssembly)" DestinationFiles="$(DestinationManifestAssembly)" />
    <Copy SourceFiles="$(SourceManifest)" DestinationFiles="$(DestinationManifest)" />
    <XmlPoke XmlInputPath="$(DestinationManifest)" Query="%(UpdateManifest.XPath)" Value="%(UpdateManifest.NewValue)" Namespaces="%(UpdateManifest.Namespaces)" />
</Target>

This takes care of the copying but it does not change the attribute values.

The instrumentation manifest file looks like this:

<instrumentationManifest xmlns="http://schemas.microsoft.com/win/2004/08/events">
<instrumentation xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:win="http://manifests.microsoft.com/win/2004/08/windows/events">
    <events xmlns="http://schemas.microsoft.com/win/2004/08/events">
        <provider name="MyCompany-MyProduct-MyLog" guid="{658FE45E-C2D4-4E73-82BB-6441A0348D9B}" resourceFileName="C:\Documents\Visual Studio\Projects\Name\bin\Debug\Name.etwManifest.dll" messageFileName="C:\Documents\Visual Studio\Projects\Name\bin\Debug\Name.etwManifest.dll" symbol="MyCompanyMyProductMyLog">
        </provider>
    </events>
</instrumentation>

The attributes that need to be changed are //provider/@resourceFileName and //provider/@messageFileName.


Solution

  • In the Windows Installer XML Util extension, the element EventManifest is used to install an event manifest. This elemnent schedules a configuration file change during installation which does exactly what is described above. After having run the installation with the installation log enabled, I simply looked through the log and examined the SchedXmlFile entries. There I found the following XPath expression:

    /*/*/*/*[@messageFileName]
    

    I tried out this code fragment, and it seems you can omit XPath namespaces, when you use the following notation:

    <PropertyGroup>
        <SourceManifestAssembly>$(OutputPath)Name.etwManifest.dll</SourceManifestAssembly>
        <DestinationManifestAssembly>$(Programdata)\MyCompany\MyProduct\1.0.0.0\Name.etwManifest.dll</DestinationManifestAssembly>
        <SourceManifest>$(OutputPath)Name.etwManifest.man</SourceManifest>
        <DestinationManifest>$(Programdata)\MyCompany\MyProduct\1.0.0.0\Name.etwManifest.man</DestinationManifest>
    </PropertyGroup>
    <ItemGroup>
        <UpdateManifest Include="UpdatemessageFileName">
            <NewValue>$(DestinationManifestAssembly)</NewValue>
            <XPath>/*/*/*/*/@messageFileName</XPath>
        </UpdateManifest>
        <UpdateManifest Include="UpdateresourceFileName">
            <NewValue>$(DestinationManifestAssembly)</NewValue>
            <XPath>/*/*/*/*/@resourceFileName</XPath>
        </UpdateManifest>
    </ItemGroup>
    <Target Name="AfterBuild">
        <Copy SourceFiles="$(SourceManifestAssembly)" DestinationFiles="$(DestinationManifestAssembly)" />
        <Copy SourceFiles="$(SourceManifest)" DestinationFiles="$(DestinationManifest)" />
        <XmlPoke XmlInputPath="$(DestinationManifest)" Query="%(UpdateManifest.XPath)" Value="%(UpdateManifest.NewValue)" />
    </Target>