アプリの機能を拡張する方法として、プラグインという方法があります。
Windowsの場合DLLファイルでプラグインを実現することが出来るので、
試してみたいと思います。
プロジェクトの作成手順
作業ディレクトリの作成
複数のプロジェクトをまとめたソリューションを保存するディレクトリを作成します。
mkdir PluginSample
cd PluginSample
ソリューションの作成
コマンド
dotnet new sln -n PluginSample
生成されるファイル
PluginSample.sln
以降このソリューションファイルにプロジェクトなどを追加することになります。
PluginContractsプロジェクト作成
このプロジェクトは、プラグインのインターフェイスになります。
コマンド
dotnet new classlib -n PluginContracts
結果(参考)
ls ./PluginContracts
Directory: C:\Users\karet\src\csharp\PluginSample\PluginContracts
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 2026/01/01 12:47 obj
-a--- 2026/01/01 12:47 62 Class1.cs
-a--- 2026/01/01 12:47 218 PluginContracts.csproj
4.Hostプロジェクト作成
このプロジェクトはプラグインを使う側で、
今回は通常のコンソールアプリで作成します。
コマンド
dotnet new console -n Host.Console
結果(参考)
ls ./Host.Console
Directory: C:\Users\karet\src\csharp\PluginSample\Host.Console
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 2026/01/01 12:50 obj
-a--- 2026/01/01 12:50 252 Host.Console.csproj
-a--- 2026/01/01 12:50 105 Program.cs
SamplePluginプロジェクトの作成
このプロジェクトは呼び出される側のプラグイン本体で、
classlibとして作成します。
dotnet new classlib -n SamplePlugin
結果(参考)
ls ./SamplePlugin
Directory: C:\Users\karet\src\csharp\PluginSample\SamplePlugin
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 2026/01/01 12:54 obj
-a--- 2026/01/01 12:54 59 Class1.cs
-a--- 2026/01/01 12:54 218 SamplePlugin.csproj
ソリューションにプロジェクトを追加
dotnet sln add PluginContracts/PluginContracts.csproj
dotnet sln add Host.Console/Host.Console.csproj
dotnet sln add SamplePlugin/SamplePlugin.csproj
出来上がった、PluginSample.slnの内容は次のようになりました。
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PluginContracts", "PluginContracts\PluginContracts.csproj", "{960D87F1-98CA-42BD-95C1-CD46E37CFD84}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Host.Console", "Host.Console\Host.Console.csproj", "{14F4246A-E581-43AA-8757-2258FEF86BC8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SamplePlugin", "SamplePlugin\SamplePlugin.csproj", "{07A39DB2-8662-4354-B40A-02F4662291F0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{960D87F1-98CA-42BD-95C1-CD46E37CFD84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{960D87F1-98CA-42BD-95C1-CD46E37CFD84}.Debug|Any CPU.Build.0 = Debug|Any CPU
{960D87F1-98CA-42BD-95C1-CD46E37CFD84}.Release|Any CPU.ActiveCfg = Release|Any CPU
{960D87F1-98CA-42BD-95C1-CD46E37CFD84}.Release|Any CPU.Build.0 = Release|Any CPU
{14F4246A-E581-43AA-8757-2258FEF86BC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{14F4246A-E581-43AA-8757-2258FEF86BC8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{14F4246A-E581-43AA-8757-2258FEF86BC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{14F4246A-E581-43AA-8757-2258FEF86BC8}.Release|Any CPU.Build.0 = Release|Any CPU
{07A39DB2-8662-4354-B40A-02F4662291F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{07A39DB2-8662-4354-B40A-02F4662291F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{07A39DB2-8662-4354-B40A-02F4662291F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{07A39DB2-8662-4354-B40A-02F4662291F0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
複数のプロジェクトが、一つのソリューションとして扱えるようになりました。
次はプロジェクト同士の関係性を設定の工程になります。
HostプロジェクトからPluginContractsを参照する設定
dotnet add Host.Console reference PluginContracts
SamplePluginプロジェクトからPluginContractsを参照する設定
dotnet add SamplePlugin reference PluginContracts
ビルド確認
正しく、ソリューションやプロジェクトが作成されたことを確認するため、
ビルドコマンドを実行してみます。
dotnet build
ソリューションに含まれる3つのプロジェクトがビルドされます。
プロジェクトごとに個別にビルドする場合は、以下のコマンドを実行します。
dotnet build Host.Console
これで、ソリューションとプロジェクトが出来上がりましたので、
コーディングすることが出来るようになりました。
構成図(暫定)
PluginSample(ディレクトリ)
│ PluginSample.sln
│
├─Host.Console
│ Host.Console.csproj
│ Program.cs
│
│
├─PluginContracts
│ Class1.cs
│ PluginContracts.csproj
│
│
└─SamplePlugin
Class1.cs
SamplePlugin.csproj
ソースコード
PluginContracts\PluginContracts.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
PluginContracts\IPlugin.cs
namespace PluginContracts;
/// <summary>
/// プラグインのインターフェイス
/// </summary>
public interface IPlugin
{
/// <summary>
/// プラグイン名
/// </summary>
string Name { get; }
/// <summary>
/// ファイルを引数に処理を行う
/// </summary>
void Execute(string[] files);
}
SamplePlugin\SamplePlugin.csproj
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\PluginContracts\PluginContracts.csproj" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<!-- PluginContracts.dll を Host 側に 1 つだけ置くため -->
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
</PropertyGroup>
</Project>
SamplePlugin\HelloPlugin.cs
using PluginContracts;
namespace SamplePlugin;
/// <summary>
/// サンプルプラグイン
/// </summary>
public class HelloPlugin : IPlugin
{
public string Name => "Hello Plugin";
public void Execute(string[] files)
{
Console.WriteLine($"[{Name}]");
if (files.Length == 0)
{
Console.WriteLine("No files.");
return;
}
foreach (var file in files)
{
Console.WriteLine($"- {file}");
}
}
}
Host.Console\Host.Console.csproj
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\PluginContracts\PluginContracts.csproj" />
</ItemGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
Host.Console\Program.cs
using PluginContracts;
using System.Reflection;
class Program
{
static void Main(string[] args)
{
var baseDir = AppContext.BaseDirectory;
var pluginDir = Path.Combine(baseDir, "Plugins");
Directory.CreateDirectory(pluginDir);
var plugins = LoadPlugins(pluginDir);
Console.WriteLine($"Plugins loaded: {plugins.Count}");
foreach (var plugin in plugins)
{
Console.WriteLine($"Execute: {plugin.Name}");
ExecuteSafe(plugin, args);
}
}
static List<IPlugin> LoadPlugins(string folder)
{
var result = new List<IPlugin>();
foreach (var dll in Directory.GetFiles(folder, "*.dll"))
{
try
{
var asm = Assembly.LoadFrom(dll);
foreach (var type in asm.GetTypes())
{
if (!typeof(IPlugin).IsAssignableFrom(type)) continue;
if (type.IsAbstract) continue;
var instance = (IPlugin?)Activator.CreateInstance(type);
if (instance != null)
result.Add(instance);
}
}
catch (Exception ex)
{
Console.WriteLine($"Failed to load: {dll}");
Console.WriteLine(ex.Message);
}
}
return result;
}
static void ExecuteSafe(IPlugin plugin, string[] files)
{
try
{
plugin.Execute(files);
}
catch (Exception ex)
{
Console.WriteLine($"Plugin crashed: {plugin.Name}");
Console.WriteLine(ex);
}
}
}
※Class1.csは不要なので削除
ソリューションのビルド
dotnet build -c Release
実行してみます。
Host.Console\bin\Release\net8.0\Host.Console.exe
Plugins loaded: 0
プラグインがロードされた数が0というメッセージが表示されました。
SamplePlugin.dllをPluginsディレクトリへコピーします。
cp SamplePlugin\bin\Release\net8.0\SamplePlugin.dll Host.Console\bin\Release\net8.0\Plugins
実行してみます。
Host.Console\bin\Release\net8.0\Host.Console.exe
Plugins loaded: 1
Execute: Hello Plugin
[Hello Plugin]
No files.
今度は1件プラグインがロードされ、
Hello Pluginというメッセージが表示されてました。
コマンドライン引数をセットしてます。
Host.Console\bin\Release\net8.0\Host.Console.exe a.txt b.txt
Plugins loaded: 1
Execute: Hello Plugin
[Hello Plugin]
- a.txt
- b.txt
「Hello Plugin」が表示された後、
コマンドライン引数にセットした文字列が表示されました。
どうやら、SamplePlugin.dllが正しくロードされ実行された模様です。
感想
DLLファイルを使った、プラグインの作り方を確認しました。

コメント
こちらは独自性があるネタみたいですね。
こんなの作ったことないからなかなか面白い試みだと思います。
コメントありがとうございます。
本記事は、YMM4プラグイン作成の学習の一環として
DLLプラグインの最小構成を整理したものになります。