Category Archives: .NET

.NET 5プロジェクトでのCOM参照の追加

Visual Studio 2019 v16.6より.NET CoreプロジェクトでのCOM参照の仕組みが追加されており、Visual Studio 2019 v16.8/.NET 5でもその機能を使うことが出来ます。こういうめんどくさいことはツールサポートが無いとやっていられません。

ここではコンソールアプリからExcelのCOM Automationを使う方法をサンプルとして提示したいと思います。

まず、Visual Studio 2019で.NET 5のコンソールアプリケーションのプロジェクトを作成し、以下のように依存関係のコンテキストメニューから「COM参照の追加」を選択します。

そうすると、見慣れたCOM参照の追加ダイアログが表示されます。

Excelを参照します。そうすると以下のようにinteropアセンブリが追加されます。

ここで大事なのは、このinteropアセンブリを選択して以下のように「ローカルコピー」「相互運用性の埋め込み」の両方を「はい」に設定しておきます。こうしておかないと実行時にSystem.IO.FileNotFoundExceptionのエラーになります。

必要な準備はこれだけです。これでcsprojファイルに以下のようにCOM参照の定義が追加され、必要なinteropアセンブリがプロジェクトに追加されます。

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
    <PlatformTarget>x86</PlatformTarget>
  </PropertyGroup>

  <ItemGroup>
    <COMReference Include="Microsoft.Office.Excel.dll">
      <WrapperTool>tlbimp</WrapperTool>
      <VersionMinor>9</VersionMinor>
      <VersionMajor>1</VersionMajor>
      <Guid>00020813-0000-0000-c000-000000000046</Guid>
      <Lcid>0</Lcid>
      <Isolated>false</Isolated>
      <Private>true</Private>
      <EmbedInteropTypes>true</EmbedInteropTypes>
    </COMReference>
  </ItemGroup>

</Project>

実際にExcelを使うコードは以下のようになりますが、これは.NET Frameworkの時と違いはありません。

using System;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;

namespace HelloWorld
{
    class Program
    {
        static void Main(string[] args)
        {
            var excelName = @"C:\temp\sample.xls";
            Console.WriteLine("Hello World!");
            var oXls = new Excel.Application();
            oXls.Visible = true;
            var oWorkbook = oXls.Workbooks.Open(
                excelName,  // オープンするExcelファイル名
                Type.Missing, // (省略可能)UpdateLinks (0 / 1 / 2 / 3)
                Type.Missing, // (省略可能)ReadOnly (True / False )
                Type.Missing, // (省略可能)Format
                              // 1:タブ / 2:カンマ (,) / 3:スペース / 4:セミコロン (;)
                              // 5:なし / 6:引数 Delimiterで指定された文字
                Type.Missing, // (省略可能)Password
                Type.Missing, // (省略可能)WriteResPassword
                Type.Missing, // (省略可能)IgnoreReadOnlyRecommended
                Type.Missing, // (省略可能)Origin
                Type.Missing, // (省略可能)Delimiter
                Type.Missing, // (省略可能)Editable
                Type.Missing, // (省略可能)Notify
                Type.Missing, // (省略可能)Converter
                Type.Missing, // (省略可能)AddToMru
                Type.Missing, // (省略可能)Local
                Type.Missing  // (省略可能)CorruptLoad
            );
            Console.Write("エンターキーでExcelを閉じる: ");
            Console.ReadLine();
            oWorkbook.Close();
            oXls.Quit();

            // これを忘れてはダメ
#pragma warning disable CA1416 // プラットフォームの互換性の検証
            _ = Marshal.ReleaseComObject(oWorkbook);
            _ = Marshal.ReleaseComObject(oXls);
#pragma warning restore CA1416 // プラットフォームの互換性の検証

            Console.ReadLine();
            
        }
    }
}

参考:

.NET 5 / .NET CoreでCCWしたいときには以下を参照。

.NET 5でシングルバイナリを作る

.NET Coreから.NETのアプリケーションを1本の実行ファイル(シングルバイナリ)にする方法が出来て、.NET 5でも当然引き継がれています。

ちなみに、シングルバイナリファイルを正式には単一ファイルアプリケーション(Single-file Publish)と呼びます。

dotnetコマンドでシングルバイナリを作成する。

.NET 5でシングルバイナリを作るにはコマンドラインでは以下の方法があります。

Linux: dotnet publish -r linux-x64 /p:PublishSingleFile=true

このコマンドラインではHelloWorld, HelloWorld.pdbの二つのファイルが作成されます

Windows : dotnet publish -r win-x64 /p:PublishSingleFile=true

このコマンドラインでは次のファイルが作成されます。

HelloWorld.exe, HelloWorld.pdb, coreclr.dll, clrjit.dll, clrcompression.dll, mscordaccore.dll

Windowsでは一つのファイルとは行かずにいくつかの最低限のライブラリDLLをいっしょに配置する必要があります。これでは「シングルバイナリー」とは呼びづらいですね。

Linuxと同様に1つのEXEファイルにランタイムライブラリもまとめたい場合には以下のようにします。

Windows: dotnet publish -r win-x64 /p:PublishSingleFile=true /p:IncludeNativeLibrariesForSelfExtract=true

これで生成されるファイルは以下の二つになります。

HelloWorld.exe, HelloWorld.pdb

先ほどまとめたい場合と書きましたが、それには理由があり、この1本に見えるEXEファイルは実際には自己展開される圧縮ファイルであり、先ほど/p:IncludeNativeLibrariesForSelfExtract=trueが指定されなかった場合に配置されたランタイムのDLLが実行ファイルといっしょに自己解凍方式の圧縮ファイルに圧縮されている形式になります。ただし、この方法でWindowsでも1本のEXEファイルにする事はできました。

Visual Studio 2019でシングルバイナリを作成する

プロジェクトファイルのコンテキストメニューから発行を選択します。

フォルダを選択します。

ここでもフォルダを選択します。

とりあえずこのままでもかまいません。

公開の設定画面になるので、上の図の矢印の先にあるペン型のアイコンクリックします。

プロファイル設定画面で詳細を設定します。大事なのは配置モードを自己完結にすること、ファイル公開オプションで単一ファイルの作成を選択します。この設定内容で、コマンドラインで次のように設定したのと同じになります。

dotnet publish -r win-x64 /p:PublishSingleFile=true

上のコマンドと同じであるため、発行ボタンをクリックすると以下のファイルが先ほど指定した公開先のフォルダに生成されます。

HelloWorld.exe, HelloWorld.pdb, coreclr.dll, clrjit.dll, clrcompression.dll, mscordaccore.dll

現状、VSのGUIからは/p:IncludeNativeLibrariesForSelfExtract=trueオプションを指定する方法がないようなので、WindowsでEXE1本にどうしてもまとめたい場合にはコマンドラインから操作する以外に方法はないようです。

参考:

Visual Studio 2019 v16.8 / Visual Studio 2019 for Mac version 8.8

VS2019 v16.8ではGitの生産性が向上。C++生産性の改善、C++ 20のサポート、C++17のサポート強化。.NETの生産性の改善として.NET analyzersのサポート、インラインでの引数目のヒント、リファクタリング機能の強化、XAMLエディタの改善、.NET 5でのWindows Form Designerのサポートなどが行われています。

.NET 5, C# 9, F# 5 GA リンクまとめ

という事で、予告通り.NET Conf 2020でC# 9/F#5/.NET 5が一般公開となりました。

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 コード内容の一部変更、コメントの追加。誰得?オレ特。

.NET Conf 2020

Join the .NET Conf 2020 free virtual event November 10-12 to learn about the newest developments across the .NET platform, open source, and dev tools. Mark your calendar!

情報源: .NET Conf 2020

11/10~12に開催。今回もオンラインイベントです。

.NET 5リリースって書かれているけど確定かな?

C#コーディングルール_#14_StyleCopAnalyzersのインストール|ピーコックアンダーソン

前回はStyleCopAnalyzersのお話をしました。StyleCopAnalyzersを使うと,間違った…

情報源: C#コーディングルール_#14_StyleCopAnalyzersのインストール|ピーコックアンダーソン

ナイスなStylecopAnalyzersの記事。

こちらも参照。

StyleCop.Analyzersを導入して整ったコードを書く – Qiita

NET Core Releases and Support | .NET Blog

While we’ve covered .NET Core releases, cadence and support policies in previous blog posts, the information has been distributed across a couple of individual posts. With the .NET 5 release just around the corner, we thought this is a good time to bring all the information together into a single post as a refresher on these topics.

情報源: NET Core Releases and Support | .NET Blog

.NET Coreのサポートライフサイクルについて。

Debug Your .NET Core Apps in WSL 2 with Visual Studio

The .NET Core Debugging with WSL 2 – Preview extension lets run and debug your .NET Core apps in WSL 2 from Visual Studio.

情報源: Debug Your .NET Core Apps in WSL 2 with Visual Studio | .NET Blog

MEMO。WSL2でのデバッグ方法。