Help us understand the problem. What is going on with this article?

T4 でカレントプロジェクトの参照アセンブリの情報を取得する

このドキュメントの内容

T4 でテキスト生成を行う際、テキストテンプレートを含むカレントプロジェクトの情報を参照したくなるケースがあります。プロジェクトが参照しているアセンブリの情報を取得する方法を紹介します。この例では Name と Path の値のみを対象としていますが、PublicToken や Version などの値を取得することもできます。

参考 VSLangProj.Reference インターフェース
https://docs.microsoft.com/en-us/dotnet/api/vslangproj.reference?view=visualstudiosdk-2017

VisualStudio 2017 以降ではパッケージの管理方法に PackageReference 形式が推奨されています。プロジェクトファイルから参照パスを取得することが難しくなっていますが、NuGet で参照したアセンブリの参照パスをこの方法で取得できることを確認しました。

私の目的はこの方法で取得した参照パスをテンプレートの assembly ディレクティブに指定することでしたが、それは実現できませんでした。

参照アセンブリを取得するインクルード用テンプレート

指定されたプロジェクトの参照アセンブリの名前とパスを取得する GetReferences メソッドを定義しています。
プロジェクト情報の取得には EnvDTE と VSLangProj を使っています。
ホストを参照するにはテンプレートの hostspecific プロパティに true を設定する必要があります。
このテンプレートは単独でテキスト生成に使用するわけではないため、拡張子を tt ではなく t4 にしています。

念のため、Marshal.FinalReleaseComObject メソッドで COM オブジェクトの解放を行っています。DTE と IServiceProvider に対して解放を行うと「基になる RCW から分割された COM オブジェクトを使うことはできません。」例外が発生するため、対象外としています。

DTETemplate.t4
<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ assembly name="EnvDTE" #>
<#@ assembly name="VSLangProj" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="VSLangProj" #>
<#@ import namespace="System.Runtime.InteropServices" #>
<# 

void ReleaseComObjects(params object[] objects) {
    foreach (object o in objects) {
    try {
        if (o == null) { continue; }
        if (!Marshal.IsComObject(o)) { continue; }
            Marshal.FinalReleaseComObject(o);
        } catch (Exception) {
        }
    }
}

IDictionary<string, string> GetReferences(string projectName)
{
    Dictionary<string, string> dic = new Dictionary<string, string>();

    IServiceProvider service = (IServiceProvider)this.Host;
    DTE dte = service.GetService(typeof(DTE)) as DTE;
    Solution solution = null;

    try
    {
        solution = dte.Solution;

        foreach (Project project in solution.Projects)
        {
            VSProject vsProject = null;
            References refs = null;

            try
            {
                if (project.Name != projectName) { continue; }

                vsProject = (VSProject)project.Object;
                if (vsProject == null) { continue; }

                refs = vsProject.References;
                if ( refs == null ) { continue; }

                foreach (Reference reference in refs)
                {
                    dic.Add(reference.Name, reference.Path);
                    ReleaseComObjects(reference);
                }
            }
            finally
            {
                ReleaseComObjects(refs, vsProject, project);
            }
        }
    }
    finally
    {
        ReleaseComObjects(solution);
    }

    return dic;
}
#>

テンプレート例

上記の GetReferences メソッドを呼び出す例です。
DTETemplate.t4 をインクルードしています。

Template1.tt
<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".txt" #>
<#@ include file="DTETemplate.t4" #>
<#

// CodeGenerationSample プロジェクトの参照アセンブリの名前とパスを取得
IDictionary<string, string> refs = GetReferences("CodeGenerationSample");

foreach (string name in refs.Keys)
{
    WriteLine(string.Format("{0} = {1}", name, refs[name]));
}

#>

出力結果

mxProject.CodeGenerations は NuGet を使って参照しています。NuGet のリポジトリのパスが取得できています。

Template1.txt
NETStandard.Library = 
System.Security.Cryptography.Algorithms = C:\Program Files\dotnet\sdk\NuGetFallbackFolder\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Cryptography.Algorithms.dll
System.Net.Requests = C:\Program Files\dotnet\sdk\NuGetFallbackFolder\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.Requests.dll
System.Windows = C:\Program Files\dotnet\sdk\NuGetFallbackFolder\netstandard.library\2.0.3\build
\netstandard2.0\ref\System.Windows.dll
(省略)
mxProject.CodeGenerations = E:\Data\Program\Nuget\global\mxproject.codegenerations\0.5.0\lib\netstandard2.0\mxProject.CodeGenerations.dll
mxProject
SIerでエンジニアをしています。最近は gRPC を調べています。 これまではブログサイトを利用していましたが、Qiitaに移ってきました。
https://mxproj.blogspot.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした