C# – Passing array to custom MSBuild task

cmsbuildmsbuild-task

I thought this would be quite simple but then realised that I couldnt find any information on it anywhere.

I have a custom task like so:

public class MyCustomTask : Task
{
    [Required]
    public string[] SomeStrings {get;set;}

    public override bool Execute()
    {
        // Do something with strings...
    }
}

The matching MSBuild stuff is basically like so:

<UsingTask TaskName="MyCustomTask" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildBinPath)\Microsoft.Build.Tasks.v4.0.dll">
    <ParameterGroup>
      <SomeStrings ParameterType="System.String[]" Required="true" />
    </ParameterGroup>
    <Task>
    ... 
    </Task>
</UsingTask>

<Target Name="DoSomething">
    <MyCustomTask SomeStrings="????" />
</Target>

Dont have any idea of what to put in the SomeStrings parameter, thought maybe it would understand if I did "xxx,xxx,xxx" so can anyone shed any light on this. The basic scenario is alot like tokenizing so I require a list of strings then some comparison strings so I need to pass in 2 lists/arrays, but just stumped.

Best Answer

@BrianKretzler is exactly dead on in using ITaskItem, since it's what MSBuild uses when you declare an <ItemGroup>.

I just wanted to flush out the answer with a full working example, since I found this post while I was trying to accomplish the same thing and it helped me out. (It's very hard to search for these problems, because the keywords are used in different contexts, so hopefully this will help someone else).

<UsingTask TaskName="MyCustomTask" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
    <ParameterGroup>
        <SomeStrings ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
    </ParameterGroup>
    <Task>
        <Code Type="Class" Language="cs"><![CDATA[
            using System;
            using Microsoft.Build.Framework;
            using Microsoft.Build.Utilities;

            public class MyCustomTask : Task
            {  
                public ITaskItem[] SomeStrings { get; set; }

                public override bool Execute()
                {
                    foreach (var item in SomeStrings)
                    {
                        Log.LogMessage(MessageImportance.High, 
                                       "Got item {0}",
                                       item.ItemSpec);
                        Log.LogMessage(" -> {0} -> {1}", 
                                       item.GetMetadata("Comparison"),
                                       item.GetMetadata("MoreDetail"));
                    }
                    return true;
                }
            }
        ]]></Code>
    </Task>
</UsingTask>

Now you can call this task with:

<Target Name="DoSomething">
    <ItemGroup>
       <SomeStrings Include="first string">
          <Comparison>first</Comparison>
       </SomeStrings>
       <SomeStrings Include="second string">
          <Comparison>2nd</Comparison>
          <MoreDetail>this is optional</MoreDetail>
       </SomeStrings>
    </ItemGroup>
    <MyCustomTask SomeStrings="@(SomeStrings)" />
</Target>

and the output is:

Microsoft (R) Build Engine Version 4.0.30319.1
[Microsoft .NET Framework, Version 4.0.30319.269]
Copyright (C) Microsoft Corporation 2007. All rights reserved.

Build started 2012-10-19 5:41:22 PM.
Got first string
 -> first -> 
Got second string
 -> 2nd -> this is optional

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:00.12

You can of course also use something like <ItemGroup><SomeStrings Include="**\*.txt" /></ItemGroup> and you'll get the list of filenames that are matched, and of course you can use GetMetadata() to access the well-known file metadata