スポンサーリンク

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

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

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をコピーしました