スポンサーリンク

Memo: Roslynを使ってC#のコードでC#のコードをコンパイルする その2

Memo: Roslynを使ってC#のコードでC#のコードをコンパイルするの続きで、コンパイル結果をインメモリで取得して、そこからリフレクションを使ってインスタンスを作成する方法と、コンパイルエラー処理を追加。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<span class="hljs-keyword">using</span> System;
<span class="hljs-keyword">using</span> System.Collections.Generic;
<span class="hljs-keyword">using</span> System.Text;
<span class="hljs-keyword">using</span> System.IO;
<span class="hljs-keyword">using</span> Microsoft.CodeAnalysis.CSharp;
<span class="hljs-keyword">using</span> Microsoft.CodeAnalysis;
<span class="hljs-keyword">using</span> Microsoft.CodeAnalysis.Text;
<span class="hljs-keyword">using</span> System.Reflection;
<span class="hljs-keyword">namespace</span> <span class="hljs-title">RoslynComple</span>
{
<span class="hljs-keyword">class</span> <span class="hljs-title">Program</span>
{
<span class="hljs-comment">// デフォルトして使用するネームスペースを定義</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">readonly</span> List<<span class="hljs-keyword">string</span>> DefaultNamepaces =
<span class="hljs-keyword">new</span> List<<span class="hljs-keyword">string</span>>
{
<span class="hljs-string">"System"</span>,
<span class="hljs-string">"System.IO"</span>,
<span class="hljs-string">"System.Net"</span>,
<span class="hljs-string">"System.Linq"</span>,
<span class="hljs-string">"System.Text"</span>,
<span class="hljs-string">"System.Text.RegularExpressions"</span>,
<span class="hljs-string">"System.Collections.Generic"</span>
};
<span class="hljs-comment">// デフォルトで使用する参照アセンブリの設定</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span> runtimePath
= <span class="hljs-string">@"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\{0}.dll"</span>;
<span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">readonly</span> List<MetadataReference> DefaultRefrences =
<span class="hljs-keyword">new</span> List<MetadataReference>
{
MetadataReference.CreateFromFile(<span class="hljs-keyword">string</span>.Format(runtimePath, <span class="hljs-string">"mscorlib"</span>)),
MetadataReference.CreateFromFile(<span class="hljs-keyword">string</span>.Format(runtimePath, <span class="hljs-string">"System"</span>)),
MetadataReference.CreateFromFile(<span class="hljs-keyword">string</span>.Format(runtimePath, <span class="hljs-string">"System.Core"</span>))
};
<span class="hljs-comment">// DLLを出力、オーバーフローチェックはする、最適化オプションはRelease、</span>
<span class="hljs-comment">// 先に定義したネームスペースをデフォルトとのネームスペースとして使用、</span>
<span class="hljs-comment">// 決定論的コンパイラの出力をON</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">readonly</span> CSharpCompilationOptions DefaultCompletionOpsions =
<span class="hljs-keyword">new</span> CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
.WithOverflowChecks(<span class="hljs-literal">true</span>).WithOptimizationLevel(OptimizationLevel.Release)
.WithUsings(DefaultNamepaces).WithDeterministic(<span class="hljs-literal">true</span>);
<span class="hljs-comment">// パース処理の定義</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> SyntaxTree <span class="hljs-title">Parse</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> text, <span class="hljs-keyword">string</span> filename = <span class="hljs-string">""</span>, CSharpParseOptions options = <span class="hljs-literal">null</span></span>)</span>
{
<span class="hljs-keyword">var</span> stringText = SourceText.From(text, Encoding.UTF8);
<span class="hljs-keyword">return</span> SyntaxFactory.ParseSyntaxTree(stringText, options, filename);
}
<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Main</span>(<span class="hljs-params"><span class="hljs-keyword">string</span>[] args</span>)</span>
{
<span class="hljs-comment">// DLL Source Code</span>
<span class="hljs-keyword">var</span> fileToCompile = <span class="hljs-string">@"C:\temp\Test.cs"</span>;
<span class="hljs-keyword">var</span> source = File.ReadAllText(fileToCompile);
<span class="hljs-comment">// パースのオプションとしてC# 7.3を使用するように指定</span>
<span class="hljs-keyword">var</span> parseSyntaxTree
= Parse(source, fileToCompile, CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp7_3));
<span class="hljs-keyword">var</span> compliation
= CSharpCompilation.Create(<span class="hljs-string">"Test"</span>,
<span class="hljs-keyword">new</span> SyntaxTree[] { parseSyntaxTree },
DefaultRefrences, DefaultCompletionOpsions);
<span class="hljs-comment">//コンパイル結果をファイルに出力する。</span>
<span class="hljs-keyword">var</span> dllFileName = <span class="hljs-string">@"c:\temp\Test.dll"</span>;
<span class="hljs-keyword">try</span>
{
<span class="hljs-keyword">var</span> result = compliation.Emit(dllFileName);
Console.WriteLine(result.Success ? <span class="hljs-string">"Sucess!!"</span> : <span class="hljs-string">"Failed"</span>);
}
<span class="hljs-keyword">catch</span> (Exception ex)
{
Console.WriteLine(ex);
<span class="hljs-keyword">throw</span>;
}
<span class="hljs-comment">// コンパイル結果をメモリーストリームに出力し、そのコンパイル結果からインスタンスを作成する。</span>
<span class="hljs-keyword">object</span> instance;
<span class="hljs-keyword">using</span> (<span class="hljs-keyword">var</span> ms = <span class="hljs-keyword">new</span> MemoryStream())
{
<span class="hljs-keyword">try</span>
{
<span class="hljs-keyword">var</span> result = compliation.Emit(ms);
Console.WriteLine(result.Success ? <span class="hljs-string">"Sucess!!"</span> : <span class="hljs-string">"Failed"</span>);
<span class="hljs-keyword">if</span> (!result.Success)
{
<span class="hljs-keyword">foreach</span> (<span class="hljs-keyword">var</span> diag <span class="hljs-keyword">in</span> result.Diagnostics)
{
<span class="hljs-comment">// コンパイルエラーメッセージを表示</span>
Console.WriteLine(diag.ToString());
<span class="hljs-comment">// 以下でコンパイルエラーメッセージだけ取れそうだが、返ってくるのは空文字</span>
Console.WriteLine(<span class="hljs-string">"Description :"</span> + diag.Descriptor.Description.ToString());
}
Console.Read();
<span class="hljs-keyword">return</span>;
}
ms.Seek(<span class="hljs-number">0</span>, SeekOrigin.Begin);
<span class="hljs-keyword">var</span> assembly = Assembly.Load(ms.ToArray());
<span class="hljs-comment">//instance = assembly.CreateInstance("TEST.Test", true);</span>
<span class="hljs-keyword">var</span> types = assembly.GetExportedTypes();
instance = assembly.CreateInstance(types[<span class="hljs-number">0</span>].FullName);
}
<span class="hljs-keyword">catch</span> (Exception ex)
{
Console.WriteLine(ex);
<span class="hljs-keyword">throw</span>;
}
}
Console.WriteLine(instance.GetType());
Console.Read();
}
}
}
<span class="hljs-keyword">using</span> System; <span class="hljs-keyword">using</span> System.Collections.Generic; <span class="hljs-keyword">using</span> System.Text; <span class="hljs-keyword">using</span> System.IO; <span class="hljs-keyword">using</span> Microsoft.CodeAnalysis.CSharp; <span class="hljs-keyword">using</span> Microsoft.CodeAnalysis; <span class="hljs-keyword">using</span> Microsoft.CodeAnalysis.Text; <span class="hljs-keyword">using</span> System.Reflection; <span class="hljs-keyword">namespace</span> <span class="hljs-title">RoslynComple</span> { <span class="hljs-keyword">class</span> <span class="hljs-title">Program</span> { <span class="hljs-comment">// デフォルトして使用するネームスペースを定義</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">readonly</span> List<<span class="hljs-keyword">string</span>> DefaultNamepaces = <span class="hljs-keyword">new</span> List<<span class="hljs-keyword">string</span>> { <span class="hljs-string">"System"</span>, <span class="hljs-string">"System.IO"</span>, <span class="hljs-string">"System.Net"</span>, <span class="hljs-string">"System.Linq"</span>, <span class="hljs-string">"System.Text"</span>, <span class="hljs-string">"System.Text.RegularExpressions"</span>, <span class="hljs-string">"System.Collections.Generic"</span> }; <span class="hljs-comment">// デフォルトで使用する参照アセンブリの設定</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span> runtimePath = <span class="hljs-string">@"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\{0}.dll"</span>; <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">readonly</span> List<MetadataReference> DefaultRefrences = <span class="hljs-keyword">new</span> List<MetadataReference> { MetadataReference.CreateFromFile(<span class="hljs-keyword">string</span>.Format(runtimePath, <span class="hljs-string">"mscorlib"</span>)), MetadataReference.CreateFromFile(<span class="hljs-keyword">string</span>.Format(runtimePath, <span class="hljs-string">"System"</span>)), MetadataReference.CreateFromFile(<span class="hljs-keyword">string</span>.Format(runtimePath, <span class="hljs-string">"System.Core"</span>)) }; <span class="hljs-comment">// DLLを出力、オーバーフローチェックはする、最適化オプションはRelease、</span> <span class="hljs-comment">// 先に定義したネームスペースをデフォルトとのネームスペースとして使用、</span> <span class="hljs-comment">// 決定論的コンパイラの出力をON</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">readonly</span> CSharpCompilationOptions DefaultCompletionOpsions = <span class="hljs-keyword">new</span> CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary) .WithOverflowChecks(<span class="hljs-literal">true</span>).WithOptimizationLevel(OptimizationLevel.Release) .WithUsings(DefaultNamepaces).WithDeterministic(<span class="hljs-literal">true</span>); <span class="hljs-comment">// パース処理の定義</span> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> SyntaxTree <span class="hljs-title">Parse</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> text, <span class="hljs-keyword">string</span> filename = <span class="hljs-string">""</span>, CSharpParseOptions options = <span class="hljs-literal">null</span></span>)</span> { <span class="hljs-keyword">var</span> stringText = SourceText.From(text, Encoding.UTF8); <span class="hljs-keyword">return</span> SyntaxFactory.ParseSyntaxTree(stringText, options, filename); } <span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Main</span>(<span class="hljs-params"><span class="hljs-keyword">string</span>[] args</span>)</span> { <span class="hljs-comment">// DLL Source Code</span> <span class="hljs-keyword">var</span> fileToCompile = <span class="hljs-string">@"C:\temp\Test.cs"</span>; <span class="hljs-keyword">var</span> source = File.ReadAllText(fileToCompile); <span class="hljs-comment">// パースのオプションとしてC# 7.3を使用するように指定</span> <span class="hljs-keyword">var</span> parseSyntaxTree = Parse(source, fileToCompile, CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp7_3)); <span class="hljs-keyword">var</span> compliation = CSharpCompilation.Create(<span class="hljs-string">"Test"</span>, <span class="hljs-keyword">new</span> SyntaxTree[] { parseSyntaxTree }, DefaultRefrences, DefaultCompletionOpsions); <span class="hljs-comment">//コンパイル結果をファイルに出力する。</span> <span class="hljs-keyword">var</span> dllFileName = <span class="hljs-string">@"c:\temp\Test.dll"</span>; <span class="hljs-keyword">try</span> { <span class="hljs-keyword">var</span> result = compliation.Emit(dllFileName); Console.WriteLine(result.Success ? <span class="hljs-string">"Sucess!!"</span> : <span class="hljs-string">"Failed"</span>); } <span class="hljs-keyword">catch</span> (Exception ex) { Console.WriteLine(ex); <span class="hljs-keyword">throw</span>; } <span class="hljs-comment">// コンパイル結果をメモリーストリームに出力し、そのコンパイル結果からインスタンスを作成する。</span> <span class="hljs-keyword">object</span> instance; <span class="hljs-keyword">using</span> (<span class="hljs-keyword">var</span> ms = <span class="hljs-keyword">new</span> MemoryStream()) { <span class="hljs-keyword">try</span> { <span class="hljs-keyword">var</span> result = compliation.Emit(ms); Console.WriteLine(result.Success ? <span class="hljs-string">"Sucess!!"</span> : <span class="hljs-string">"Failed"</span>); <span class="hljs-keyword">if</span> (!result.Success) { <span class="hljs-keyword">foreach</span> (<span class="hljs-keyword">var</span> diag <span class="hljs-keyword">in</span> result.Diagnostics) { <span class="hljs-comment">// コンパイルエラーメッセージを表示</span> Console.WriteLine(diag.ToString()); <span class="hljs-comment">// 以下でコンパイルエラーメッセージだけ取れそうだが、返ってくるのは空文字</span> Console.WriteLine(<span class="hljs-string">"Description :"</span> + diag.Descriptor.Description.ToString()); } Console.Read(); <span class="hljs-keyword">return</span>; } ms.Seek(<span class="hljs-number">0</span>, SeekOrigin.Begin); <span class="hljs-keyword">var</span> assembly = Assembly.Load(ms.ToArray()); <span class="hljs-comment">//instance = assembly.CreateInstance("TEST.Test", true);</span> <span class="hljs-keyword">var</span> types = assembly.GetExportedTypes(); instance = assembly.CreateInstance(types[<span class="hljs-number">0</span>].FullName); } <span class="hljs-keyword">catch</span> (Exception ex) { Console.WriteLine(ex); <span class="hljs-keyword">throw</span>; } } Console.WriteLine(instance.GetType()); Console.Read(); } } }
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using System.Reflection;

namespace RoslynComple
{
    class Program
    {
        // デフォルトして使用するネームスペースを定義
        private static readonly List<string> DefaultNamepaces = 
            new List<string>
            {
                "System",
                "System.IO",
                "System.Net",
                "System.Linq",
                "System.Text",
                "System.Text.RegularExpressions",
                "System.Collections.Generic"
            };

        // デフォルトで使用する参照アセンブリの設定
        private static string runtimePath 
            = @"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\{0}.dll";
        private static readonly List<MetadataReference> DefaultRefrences =
            new List<MetadataReference>
            {
                MetadataReference.CreateFromFile(string.Format(runtimePath, "mscorlib")),
                MetadataReference.CreateFromFile(string.Format(runtimePath, "System")),
                MetadataReference.CreateFromFile(string.Format(runtimePath, "System.Core"))
            };

        // DLLを出力、オーバーフローチェックはする、最適化オプションはRelease、
        // 先に定義したネームスペースをデフォルトとのネームスペースとして使用、
        // 決定論的コンパイラの出力をON
        private static readonly CSharpCompilationOptions DefaultCompletionOpsions =
            new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
                    .WithOverflowChecks(true).WithOptimizationLevel(OptimizationLevel.Release)
                    .WithUsings(DefaultNamepaces).WithDeterministic(true);

        // パース処理の定義
        public static SyntaxTree Parse(string text, string filename = "", CSharpParseOptions options = null)
        {
            var stringText = SourceText.From(text, Encoding.UTF8);
            return SyntaxFactory.ParseSyntaxTree(stringText, options, filename);
        }

        static void Main(string[] args)
        {
            // DLL Source Code
            var fileToCompile = @"C:\temp\Test.cs";
            var source = File.ReadAllText(fileToCompile);
            // パースのオプションとしてC# 7.3を使用するように指定
            var parseSyntaxTree 
                = Parse(source, fileToCompile, CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp7_3));
            var compliation 
                = CSharpCompilation.Create("Test", 
                                            new SyntaxTree[] { parseSyntaxTree }, 
                                            DefaultRefrences, DefaultCompletionOpsions);

            //コンパイル結果をファイルに出力する。
            var dllFileName = @"c:\temp\Test.dll";
            try
            {
                var result = compliation.Emit(dllFileName);
                Console.WriteLine(result.Success ? "Sucess!!" : "Failed"); 
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                throw;
            }

            // コンパイル結果をメモリーストリームに出力し、そのコンパイル結果からインスタンスを作成する。
            object instance;
            using (var ms = new MemoryStream())
            {
                try
                {
                    var result = compliation.Emit(ms);
                    Console.WriteLine(result.Success ? "Sucess!!" : "Failed");
                    if (!result.Success)
                    {
                        foreach (var diag in result.Diagnostics)
                        {
                            // コンパイルエラーメッセージを表示
                            Console.WriteLine(diag.ToString());
                            // 以下でコンパイルエラーメッセージだけ取れそうだが、返ってくるのは空文字
                            Console.WriteLine("Description :" + diag.Descriptor.Description.ToString());
                        }

                        Console.Read();
                        return;
                    }
                    
                    ms.Seek(0, SeekOrigin.Begin);
                    var assembly = Assembly.Load(ms.ToArray());

                    //instance = assembly.CreateInstance("TEST.Test", true);
                    var types = assembly.GetExportedTypes();
                    instance = assembly.CreateInstance(types[0].FullName);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex); 
                    throw;
                }
            }
            Console.WriteLine(instance.GetType());

            Console.Read();
        }
    }
}

Descriptorオブジェクトの中身が空なのが謎。

2020/11/24 コード内容の一部変更、コメントの追加。誰得?オレ特。

コメント

タイトルとURLをコピーしました