最終的な感想としては、Microsoftの中でもDevチームとWindowsチームが同床異夢であるという認識は変わらなかったかな。選択肢が増えることは良いのかもしれないけれど、こちら側としてはそれでは今後どうすれば良いのかと言う方向性が決定できない感じ。もう少し別の言い方をすると、どれに賭けを張れば(ベッドすれば)良いのですかねという疑問に対する回答が、同じMicrosoftの中でもDevチームとWindowsチームで違うものを押している感じです。このようなメッセージの発信の仕方は、昔のMicrosoftでは無かったことだし、ある意味最終的にその統一されたメッセージのものが、ものになったのかならなかったのかは別として、ある種の将来に対する計算可能性がMicrosoftのアプリケーションプラットフォームとしての良さだったのですが、今はそこが無いって所ですかね。それ故にWindowsのデスクトップアプリケーションを開発するに当たって、安全にベッドしようと思うと、ElectronやReact Nativeが最適解みたいな事になりかねません。現にOfficeチームはReact Nativeを使ってしまっているわけですし。ここ数年は何だかなー、アプリケーションプラットフォームとしてもWindowsは終わりなのではないのかと思う次第です。
Category Archives: .NET
.NET 6 Preview 1
.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(); } } }
参考:
- Excelファイルにアクセスするには?[C#、VB]:.NET TIPS – @IT
- .NET core 3.0 and MS Office Interop – Stack Overflow
- .Net Core でCOMコンポーネントを利用する – Qiita
.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
- Visual Studio 2019 v16.8 and v16.9 Preview 1 Release Today
- Visual Studio 2019 for Mac version 8.8 is now available
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 リンクまとめ
- C# 9.0 on the record
- Announcing F# 5
- Announcing .NET 5.0
- Announcing ASP.NET Core in .NET 5
- Announcing the Release of EF Core 5.0
- Announcing C#/WinRT Version 1.0 with the .NET 5 GA Release
という事で、予告通り.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 5 RC2 リンクまとめ
.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の記事。
こちらも参照。