MSBuild inline task reference is escaping parenthe

2019-04-29 03:48发布

问题:

I am writing an inline task for MSBuild. It requires a reference to System.ServiceProcess.dll.

The task works great if I hard-code the path to the System.ServiceProcess.dll file, like this:

<UsingTask
    TaskName="MyTask"
    TaskFactory="CodeTaskFactory"
    AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
    <Task>
        <Reference Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.ServiceProcess.dll" />
        <Code Type="Fragment" Language="cs">...working fine...</Code>
    </Task>
</UsingTask>

However, I would rather not hard-code that path.

If I just use <Reference Include="System.ServiceProcess.dll" />, I get an error: MSB3755: Could not find reference "System.ServiceProcess.dll", so I think I have to use the full path here.

The $(FrameworkPathOverride) property contains the correct path already, so I tried to use that:

<Reference Include="$(FrameworkPathOverride)\System.ServiceProcess.dll" />

But that gives me an error:

C:\path\to\project.csproj(93,3): error MSB3754: The reference assembly "C:\Program Files %28x86%29\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.ServiceProcess.dll" is invalid. "The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047)"[C:\path\to\project.csproj]

Notice how it escaped (x86) into %28x86%29.

Notably, it seems to do this only for $(FrameworkPathOverride). If I define my own property and use it instead, it works just fine, unless that property also references $(FrameworkPathOverride). In other words, this works (but still has me hard-coding the path):

<PropertyGroup>
    <MyPath>C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5</MyPath>
</PropertyGroup>

// (later, inside <Task />)
<References Include="$(MyPath)\System.ServiceProcess.dll" />

However, this fails with the same error that reports searching a path for %28x86%29:

<PropertyGroup>
    <MyPath>$(FrameworkPathOverride)</MyPath>
</PropertyGroup>

Just for kicks, I also tried this variation, which also fails with the same error:

<PropertyGroup>
    <MyPath>$([System.Convert]::ToString("$(FrameworkPathOverride)"))</MyPath>
</PropertyGroup>

Also, in all cases, the output of <Message Text="$(FrameworkPathOverride)" /> and <Message Test="$(MyPath)" /> are identical. The <Message /> task is not escaping the parenthesis inside $(FrameworkPathOverride), but the <Reference Include="..." /> is. Hmm.

Why does (x86) become %28x86%29 inside <Reference /> but not inside <Message />?

Why does it happen for $(FrameworkPathOverride) and not for $(MyPath)?

Why does it start happening to $(MyPath) if it references $(FrameworkPathOverride)?

How can I avoid hard-coding this path?

回答1:

Similar to your last attempt, have you tried the following using the "Unescape" MSBuild Property Function?

<PropertyGroup>
    <MyPath>$([MSBuild]::Unescape("$(FrameworkPathOverride)"))</MyPath>
</PropertyGroup>

It seems to be a known issue titled "MSBuild 4.0 UsingTask cannot have a path with parentheses": http://connect.microsoft.com/VisualStudio/feedback/details/532677/msbuild-4-0-usingtask-cannot-have-a-path-with-parentheses

Unfortunately, I have not had the opportunity to test this.