TL;DR;
Windows10組み込みの文章読み上げエンジンをCOM経由で操作してみます。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<COMReference Include="SpeechLib.dll"> <!-- 👈これでCOMInteropをリンク -->
<Guid>c866ca3a-32f7-11d2-9602-00c04f8ee628</Guid>
<VersionMajor>5</VersionMajor>
<VersionMinor>4</VersionMinor>
</COMReference>
</ItemGroup>
</Project>
class Program
{
// COMのクラスが使える
static void Main(string[] args) => new SpeechLib.SpVoice().Speak("Hello world");
}
# VSについてくるやつ(dotnet msbuildでは駄目)
msbuild /t:restore
msbuild /t:rebuild
StaticComTest.exe
# 🔊 はろぅわぁるど(ねっとり片言ボイス)
COMの呼び出し
私自身、まだCOMへの理解が甘いのですが、何となくこんな感じ…というのをメモします。
.NETにおけるCOMの静的リンク(VSでいう参照の追加>COM)というのは、実際にはCOMをリンクしているわけではなく、COMIntropのDLLをリンクすることを指しています。そもそも、COMというのは原則GUIDをKeyとしてOS登録して、使うときにCoInitialize
でOSにCOMオブジェクトをCOMサーバーにリクエストするインターフェイスです。(Registration-Free COMというのもあるらしい)COMIntropはCOMをラッピングした.NETのDLLでありCOMではありません。実際にDirectX12のようなIntropが存在しないCOMは、VSの参照の追加ダイアログの中にもありません。
まとめると、以下のようになります。
- コンパイル時に、ユーザーコードに
Introp.Speech.dll
(COMIntropのDLL)をリンク -
new SpVoice()
でSpVoiceインターフェイスを取得。(インターフェイスだが、CoClass
属性付きなのでnewできる) - SpVoice(の基底のISpeechVoice)の
Speak
メソッドを呼び出し - SpVoiceの実体となるSpVoiceClassの
ISpeechVoice.Speak
メソッドが呼び出される -
ISpeechVoice.Speak
から、SpVoice.Speak
が呼び出される。 -
SpVoice.Speak
はexternメソッドでCOMのメソッドのコール(DispID:12)に置き換えられる。
一度、SpVoiceインターフェイスを経由するのは、SpVoiceClassの実装を隠蔽するためです。SpVoiceClassはCOMと直接つながっているダーティな存在のため、COMの仕様が変わった場合、ユーザーCOMIntropライブラリの利用側に変更が必要にならないようにインターフェイスをかませています。まぁ、アクセス修飾子と名前衝突をかわしてexternメソッドを外から呼べないようにすれば隠蔽できますが…なんだかそういうルールらしいです。
COM 呼び出し可能ラッパー クラス インターフェイスの概要 (MSDN)
.NETCoreのコンパイラではコンパイルできない
COMを静的にリンクしたとしても、.NETCoreをターゲットにコンパイルすることはできます。そのため、CsProjには.NETCore3.1を指定しています。
しかし、COMReferenceは通常のMSBuildのみの機能のため、.NET Coreのdotnet msbuild
では利用できません。
$ dotnet build
C:\Program Files\dotnet\sdk\3.1.101\Microsoft.Common.CurrentVersion.targets(2726,5) : error : MSB4803:
タスク "ResolveComReference" は .NET Core バージョンの MSBuild ではサポートされていません。
.NET Framework バージョンの MSBuild をご使用ください。
詳細については、https://aka.ms/msbuild/MSB4803 をご覧ください。
ちなみに、dotnet msbuild
はmsbuild.exe
と同じ機能を提供するものではなく、msbuild.exe
と同じオプションで.NETCoreのコンパイラを使うものらしいです。
以下の一文を読むと、互換なコンパイラに切り替える(内蔵のmsbuild.exe
相当のコンパイラに切り替えると思っていた)ように聞こえますが…
The command has the exact same capabilities as the existing MSBuild command-line client for SDK-style projects only.
https://docs.microsoft.com/ja-jp/dotnet/core/tools/dotnet-msbuild#description
そこで、VisualStudioのコンパイラであるところのmsbuild.exe
を利用します。
(VisualStudioでコンパイルすればいいのですが…VSCodeとCLIでやりたい)
参考
- COM への .NET Core コンポーネントの公開 (MSDN)
- dotnet/samples COM Server Demo(Github)
- dotnet msbuild (MSDN)
- MSBuild プロジェクトの共通項目 (MSDN)
- COM Interop (Wikipedia)
- COM Interop in .NET (MSDN)
- COM Interop (Visual Basic) (MSDN)
- COM 呼び出し可能ラッパー クラス インターフェイスの概要 (MSDN)
- COMの基礎 (HSPからのCOM操作)
- Registration-Free COM Interop (MSDN)
- COM クラスの例 (C# プログラミング ガイド) (MSDN)
PS
「COMはWindowsの機能である」旨の誤りと記述ミスをご指摘いただきました、@Midoliy さん、編集リクエストありがとうございました。
COMはWindows固有の機能と位置付けられたものではありません。プロトコルであり、それを実装して利用できる状態で提供されているのが主にWindwosとなります。