C#でDLLを作成・参照・呼び出す方法|エラー原因まで初心者向けに解説

はじめに

C#で開発をしていると、「DLLを作成して別プロジェクトから使いたい」「既存のDLLを参照したい」「DllImportで外部DLLを呼び出したい」といった場面が出てきます。

DLLは、C#の処理を再利用しやすくしたり、複数のアプリで共通機能を管理したりするために使われる重要な仕組みです。一方で、初心者にとっては「参照を追加したのに使えない」「名前空間が見つからない」「ファイルまたはアセンブリを読み込めない」などのエラーでつまずきやすい分野でもあります。

この記事では、C#におけるDLLの基本から、DLLの作成、参照、呼び出し方法、外部DLLの利用、よくあるエラーの原因と対処法まで、初心者向けに順番に解説します。

1. C#のDLLとは?作成・参照・呼び出しの全体像

1-1. DLLの意味とC#における役割

DLLとは「Dynamic Link Library」の略で、日本語では動的リンクライブラリと呼ばれます。簡単に言うと、プログラム本体とは別に用意された部品のようなファイルです。

C#では、共通処理をまとめたクラスやメソッドをDLLとして作成し、別のアプリケーションから参照して利用できます。

たとえば、次のような処理をDLLにまとめることがあります。

C#
public class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
}

このようなクラスをDLLにしておくと、コンソールアプリ、Windowsフォームアプリ、WPFアプリ、Webアプリなど、複数のプロジェクトから同じ処理を呼び出せます。

C#で作成するDLLは、多くの場合「クラスライブラリプロジェクト」として作成します。ビルドすると、拡張子が.dllのファイルが出力されます。

1-2. DLLを使うメリット:処理の共通化・再利用・保守性向上

DLLを使う最大のメリットは、処理を共通化できることです。

たとえば、複数のアプリケーションで同じ計算処理、文字列変換処理、ログ出力処理、データ変換処理を使っている場合、それぞれのアプリに同じコードを書くと修正が大変になります。

DLLに共通処理をまとめておけば、修正箇所を1か所に集約できます。

DLLを使う主なメリットは次のとおりです。

・同じ処理を複数プロジェクトで再利用できる
・コードの重複を減らせる
・機能ごとにプロジェクトを分けられる
・保守しやすくなる
・テストしやすくなる
・アプリ本体と共通ライブラリを分離できる

特に業務システムでは、共通処理をDLL化しておくことで、長期的な保守性が大きく向上します。

1-3. DLL作成から呼び出しまでの基本フロー

C#でDLLを作成して呼び出す流れは、基本的に次のようになります。

1. DLL側のクラスライブラリプロジェクトを作成する
2. DLLに含めたいクラスやメソッドを作成する
3. 外部から使えるようにpublicを付ける
4. DLLプロジェクトをビルドする
5. 呼び出し側プロジェクトでDLLを参照する
6. usingで名前空間を読み込む
7. DLL内のクラスやメソッドを呼び出す

C#で作成したDLLを別プロジェクトから使う場合、基本的には「参照の追加」を行い、通常のクラスと同じように呼び出します。

1-4. 初心者が混同しやすい「DLL」「クラスライブラリ」「アセンブリ」の違い

C#のDLLを理解するときに混同しやすい言葉として、「DLL」「クラスライブラリ」「アセンブリ」があります。

まず、DLLはファイル形式の名前です。拡張子が.dllのファイルを指します。

クラスライブラリは、DLLを作成するためのプロジェクトの種類です。Visual Studioで「クラスライブラリ」を選んでプロジェクトを作成すると、ビルド時にDLLファイルが出力されます。

アセンブリは、.NETにおける実行単位や配布単位を表す言葉です。C#で作成されたDLLやEXEは、.NETアセンブリとして扱われます。

整理すると、次のようになります。

DLL:拡張子.dllのファイル
クラスライブラリ:DLLを作るためのプロジェクト形式
アセンブリ:.NETが認識する実行・参照の単位

初心者のうちは、「C#でDLLを作る=クラスライブラリプロジェクトを作成してビルドする」と考えると理解しやすいです。

2. C#でDLLを作成する方法

2-1. Visual Studioでクラスライブラリプロジェクトを作成する

C#でDLLを作成するには、Visual Studioでクラスライブラリプロジェクトを作成します。

手順は次のとおりです。

1. Visual Studioを起動する
2. 「新しいプロジェクトの作成」を選択する
3. 「クラス ライブラリ」を検索する
4. 使用するターゲットフレームワークを選択する
5. プロジェクト名を入力する
6. 作成をクリックする

たとえば、プロジェクト名をMyLibraryにすると、ビルド後にMyLibrary.dllというDLLファイルが作成されます。

.NETのバージョンによって、テンプレート名は次のように表示されることがあります。

クラス ライブラリ
クラス ライブラリ (.NET Framework)
クラス ライブラリ (.NET Standard)

新しく開発する場合は、基本的には.NET用のクラスライブラリを選ぶことが多いです。古い.NET Frameworkアプリから参照する必要がある場合は、.NET Framework用のクラスライブラリを選びます。

2-2. DLLに含めるクラス・メソッドを作成する

クラスライブラリプロジェクトを作成すると、初期状態でClass1.csのようなファイルが作成されます。このファイルに、DLLとして提供したいクラスやメソッドを記述します。

たとえば、簡単な計算処理を行うDLLを作る場合は、次のように書きます。

C#
namespace MyLibrary
{
public class Calculator
{
public int Add(int a, int b)
{
return a + b;
}

public int Subtract(int a, int b)
{
return a - b;
}
}
}

この例では、MyLibraryという名前空間の中にCalculatorクラスを作成しています。Addメソッドは足し算、Subtractメソッドは引き算を行います。

2-3. 外部から呼び出せるようにpublicを設定する

DLLを別プロジェクトから呼び出すには、クラスやメソッドをpublicにする必要があります。

C#では、アクセス修飾子を指定しない場合、クラスやメンバーは外部から見えない状態になることがあります。そのため、DLLとして公開したいクラスやメソッドには明示的にpublicを付けましょう。

外部から呼び出せる例です。

C#
public class MessageService
{
public string GetMessage()
{
return "Hello from DLL";
}
}

一方、次のようにpublicが付いていないメソッドは、別プロジェクトから呼び出せません。

C#
class MessageService
{
string GetMessage()
{
return "Hello from DLL";
}
}

DLLを参照しているのにメソッドが表示されない場合は、まずpublicが付いているかを確認しましょう。

2-4. ビルドしてDLLファイルを出力する

クラスやメソッドを作成したら、プロジェクトをビルドします。

Visual Studioでは、メニューから次の操作を行います。

ビルド → ソリューションのビルド

ビルドに成功すると、プロジェクトフォルダ内のbinフォルダにDLLファイルが出力されます。

Debugビルドの場合は、一般的に次のような場所に出力されます。

MyLibrary\bin\Debug\net8.0\MyLibrary.dll

Releaseビルドの場合は、次のような場所です。

MyLibrary\bin\Release\net8.0\MyLibrary.dll

ターゲットフレームワークによって、net8.0net472などのフォルダ名は変わります。

2-5. Debug版とRelease版の違い

DLLをビルドするときには、Debug版とRelease版を選択できます。

Debug版は、開発中のデバッグに向いています。デバッグ情報が含まれているため、ブレークポイントを設定したり、ステップ実行したりしやすいのが特徴です。

Release版は、配布や本番環境での利用に向いています。最適化されるため、実行時のパフォーマンスや配布に適しています。

違いを簡単に整理すると、次のようになります。

Debug版:開発・検証向け
Release版:本番・配布向け

開発中はDebug版を使い、利用者に配布する場合や本番環境で使う場合はRelease版を使うのが基本です。

2-6. .NET Framework・.NET・.NET Standardの選び方

DLLを作成するときは、ターゲットフレームワークの選び方が重要です。呼び出し側のプロジェクトと互換性がないDLLは参照できない、または実行時にエラーになることがあります。

主な選び方は次のとおりです。

.NET Framework:
古いWindowsアプリや既存の.NET Frameworkプロジェクト向け

.NET:
新しいC#アプリやクロスプラットフォーム対応アプリ向け

.NET Standard:
複数の.NET実装から使えるライブラリを作りたい場合向け

初心者が新規でC#アプリを作る場合は、呼び出し側とDLL側のターゲットを同じ.NETバージョンにそろえるのが最も簡単です。

たとえば、呼び出し側が.NET 8なら、DLL側も.NET 8のクラスライブラリにするとトラブルが少なくなります。

3. 作成したDLLをC#プロジェクトから参照する方法

3-1. Visual Studioの「参照の追加」からDLLを追加する

作成したDLLをC#プロジェクトから使うには、呼び出し側プロジェクトに参照を追加します。

Visual Studioでの基本手順は次のとおりです。

1. 呼び出し側プロジェクトを右クリックする
2. 「追加」または「参照の追加」を選択する
3. 「プロジェクト」または「参照」からDLLを選ぶ
4. OKをクリックする

同じソリューション内にDLLプロジェクトがある場合は、「プロジェクト」から参照を追加するのがおすすめです。

すでにビルド済みのDLLファイルだけを使う場合は、「参照」や「参照」ボタンから.dllファイルを選択します。

3-2. プロジェクト参照とファイル参照の違い

C#でDLLを参照する方法には、大きく分けて「プロジェクト参照」と「ファイル参照」があります。

プロジェクト参照は、同じソリューション内のクラスライブラリプロジェクトを参照する方法です。DLL側を修正してビルドすると、呼び出し側にも変更が反映されやすく、開発中に便利です。

ファイル参照は、すでに存在するDLLファイルを直接参照する方法です。外部から提供されたDLLや、別チームから受け取ったDLLを使う場合に利用します。

プロジェクト参照:
同じソリューション内のDLL開発に向いている

ファイル参照:
完成済みDLLや外部DLLを利用する場合に向いている

初心者が自分でDLLを作成して呼び出す練習をする場合は、まずプロジェクト参照を使うと分かりやすいです。

3-3. usingで名前空間を読み込む方法

DLLを参照に追加しただけでは、必ずしもそのままクラス名を使えるとは限りません。DLL内のクラスが属している名前空間をusingで読み込む必要があります。

たとえば、DLL側のコードが次のようになっているとします。

C#
namespace MyLibrary
{
public class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
}
}

呼び出し側では、次のようにusing MyLibrary;を追加します。

C#
using MyLibrary;

class Program
{
static void Main()
{
Calculator calculator = new Calculator();
int result = calculator.Add(10, 20);

Console.WriteLine(result);
}
}

usingを追加しない場合は、完全修飾名で呼び出すこともできます。

C#
MyLibrary.Calculator calculator = new MyLibrary.Calculator();

ただし、通常はusingを使った方がコードが読みやすくなります。

3-4. 参照したDLLがコピーされる出力先を確認する

C#プロジェクトからDLLを参照すると、ビルド時に呼び出し側の出力フォルダへDLLがコピーされることがあります。

たとえば、コンソールアプリの出力先が次の場所だった場合、

MyApp\bin\Debug\net8.0\

このフォルダに、実行ファイルと一緒に参照DLLが配置されます。

MyApp.exe
MyLibrary.dll

実行時には、アプリケーションが必要なDLLを読み込める場所に配置されている必要があります。参照は追加できていても、実行時の出力フォルダにDLLが存在しないと、読み込みエラーになることがあります。

3-5. NuGetパッケージとしてDLLを管理する場合

DLLは、ファイルとして直接参照するだけでなく、NuGetパッケージとして管理することもできます。

NuGetパッケージにすると、DLL本体だけでなく、バージョン情報、依存関係、説明情報などもまとめて管理できます。

社内共通ライブラリを複数プロジェクトで使う場合、単純にDLLファイルをコピーして配布するよりも、NuGetパッケージ化した方が管理しやすいケースがあります。

NuGetを使うメリットは次のとおりです。

・バージョン管理しやすい
・依存関係を管理しやすい
・複数プロジェクトへ配布しやすい
・更新履歴を追いやすい

小規模な学習用途ならDLL参照で十分ですが、実務で複数プロジェクトに展開する場合はNuGet化も検討するとよいでしょう。

4. C#からDLL内のクラス・メソッドを呼び出す方法

4-1. インスタンスメソッドを呼び出すサンプルコード

DLL内のクラスにインスタンスメソッドが定義されている場合、呼び出し側ではクラスのインスタンスを作成してからメソッドを呼び出します。

DLL側のコード例です。

C#
namespace MyLibrary
{
public class GreetingService
{
public string CreateMessage(string name)
{
return $"こんにちは、{name}さん";
}
}
}

呼び出し側のコード例です。

C#
using MyLibrary;

class Program
{
static void Main()
{
GreetingService service = new GreetingService();

string message = service.CreateMessage("山田");

Console.WriteLine(message);
}
}

実行結果は次のようになります。

こんにちは、山田さん

インスタンスメソッドは、オブジェクトごとに状態を持たせたい場合に向いています。

4-2. staticメソッドを呼び出すサンプルコード

DLL内のメソッドがstaticとして定義されている場合、インスタンスを作成せずにクラス名から直接呼び出せます。

DLL側のコード例です。

C#
namespace MyLibrary
{
public class MathUtil
{
public static int Multiply(int a, int b)
{
return a * b;
}
}
}

呼び出し側のコード例です。

C#
using MyLibrary;

class Program
{
static void Main()
{
int result = MathUtil.Multiply(5, 3);

Console.WriteLine(result);
}
}

実行結果は次のようになります。

15

staticメソッドは、状態を持たない共通処理やユーティリティ処理に向いています。

4-3. 戻り値や引数があるメソッドの呼び出し方

DLL内のメソッドには、引数や戻り値を定義できます。呼び出し側では、通常のC#メソッドと同じように値を渡し、戻り値を受け取ります。

DLL側のコード例です。

C#
namespace MyLibrary
{
public class TaxCalculator
{
public decimal CalculateTaxIncludedPrice(decimal price, decimal taxRate)
{
return price * (1 + taxRate);
}
}
}

呼び出し側のコード例です。

C#
using MyLibrary;

class Program
{
static void Main()
{
TaxCalculator calculator = new TaxCalculator();

decimal price = 1000m;
decimal taxRate = 0.10m;

decimal result = calculator.CalculateTaxIncludedPrice(price, taxRate);

Console.WriteLine(result);
}
}

実行結果は次のようになります。

1100.00

DLL内のメソッドだからといって、特別な呼び出し方が必要になるわけではありません。参照が正しく追加されていれば、通常のクラスと同じように扱えます。

4-4. 別プロジェクトのDLLを呼び出す実践例

同じソリューション内に、次の2つのプロジェクトがあるとします。

MyLibrary:DLL側のクラスライブラリ
MyApp:呼び出し側のコンソールアプリ

DLL側のMyLibraryに次のクラスを作成します。

C#
namespace MyLibrary
{
public class UserService
{
public string GetUserName(int userId)
{
return $"ユーザーID:{userId}";
}
}
}

次に、MyAppからMyLibraryをプロジェクト参照として追加します。

呼び出し側では、次のようにコードを書きます。

C#
using MyLibrary;

class Program
{
static void Main()
{
UserService userService = new UserService();

string userName = userService.GetUserName(100);

Console.WriteLine(userName);
}
}

実行結果は次のようになります。

ユーザーID:100

このように、別プロジェクトのDLLでも、参照を追加すれば通常のクラスと同じ感覚で呼び出せます。

4-5. DLL更新後に呼び出し側へ反映する方法

DLL側のコードを修正した場合、呼び出し側に変更を反映するには再ビルドが必要です。

プロジェクト参照の場合は、ソリューション全体をビルドすればDLL側の変更が呼び出し側にも反映されます。

ファイル参照の場合は、修正後にビルドされた新しいDLLファイルを、呼び出し側が参照している場所へコピーする必要があります。

反映されない場合は、次の点を確認しましょう。

・DLL側をビルドしたか
・呼び出し側を再ビルドしたか
・古いDLLを参照していないか
・出力フォルダに新しいDLLがコピーされているか
・Debug版とRelease版を間違えていないか

「修正したはずなのに動作が変わらない」という場合は、古いDLLが実行フォルダに残っていることがよくあります。

5. 外部DLL・既存DLLをC#から呼び出す方法

5-1. .NET製DLLとネイティブDLLの違い

C#からDLLを呼び出す場合、そのDLLが「.NET製DLL」なのか「ネイティブDLL」なのかによって呼び出し方法が変わります。

.NET製DLLは、C#やVB.NETなどで作成された.NETアセンブリです。通常はVisual Studioで参照を追加し、名前空間をusingして呼び出します。

ネイティブDLLは、CやC++などで作成されたDLLです。C#から呼び出す場合は、DllImportを使うことが一般的です。

.NET製DLL:
参照を追加して通常のC#クラスとして呼び出す

ネイティブDLL:
DllImportを使って関数を呼び出す

この違いを理解していないと、C++製DLLを通常の参照追加で読み込もうとして失敗することがあります。

5-2. .NET製DLLを参照して呼び出す方法

.NET製DLLの場合、基本的な呼び出し方法は自作DLLと同じです。

手順は次のとおりです。

1. 呼び出し側プロジェクトで参照を追加する
2. DLLの名前空間をusingで読み込む
3. DLL内のクラスを生成する
4. メソッドを呼び出す

コード例です。

C#
using ExternalLibrary;

class Program
{
static void Main()
{
SampleService service = new SampleService();

string result = service.Execute();

Console.WriteLine(result);
}
}

.NET製DLLは、ターゲットフレームワークの互換性が重要です。呼び出し側が古い.NET Frameworkなのに、DLL側が新しい.NET用に作られている場合は参照できないことがあります。

5-3. C/C++製DLLをDllImportで呼び出す方法

CやC++で作られたネイティブDLLをC#から呼び出す場合は、DllImport属性を使います。

たとえば、Windows APIのMessageBoxを呼び出す例です。

C#
using System;
using System.Runtime.InteropServices;

class Program
{
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern int MessageBox(
IntPtr hWnd,
string text,
string caption,
uint type
);

static void Main()
{
MessageBox(IntPtr.Zero, "こんにちは", "確認", 0);
}
}

DllImportでは、DLL名、関数名、引数の型、戻り値の型を正しく定義する必要があります。

C#側の型定義が実際のC/C++側の関数定義と違っていると、実行時エラーや予期しない動作の原因になります。

5-4. 32bit・64bitの違いに注意する

外部DLLを呼び出すときに特に注意したいのが、32bitと64bitの違いです。

C#アプリが64bitで動作しているのに、読み込もうとしているDLLが32bit用の場合、BadImageFormatExceptionが発生することがあります。

逆に、C#アプリが32bitで動作しているのに、DLLが64bit用の場合も同じように失敗します。

対処法としては、呼び出し側プロジェクトのプラットフォームターゲットを確認します。

Visual Studioでは、プロジェクトのプロパティから次の設定を確認できます。

ビルド → プラットフォームターゲット

代表的な設定は次のとおりです。

Any CPU
x86
x64

ネイティブDLLを使う場合は、DLLのビット数に合わせてx86またはx64を明示するのが安全です。

5-5. 動的にDLLを読み込む場合の基本

通常のC#開発では、参照を追加してコンパイル時にDLLを指定します。しかし、プラグイン機能のように、実行時にDLLを読み込みたい場合もあります。

.NETでは、リフレクションを使ってDLLを動的に読み込むことができます。

簡単な例です。

C#
using System.Reflection;

class Program
{
static void Main()
{
Assembly assembly = Assembly.LoadFrom("MyLibrary.dll");

Type? type = assembly.GetType("MyLibrary.Calculator");

object? instance = Activator.CreateInstance(type!);

MethodInfo? method = type!.GetMethod("Add");

object? result = method!.Invoke(instance, new object[] { 10, 20 });

Console.WriteLine(result);
}
}

この方法では、コンパイル時にDLLを参照していなくても、実行時にDLLを読み込んでメソッドを呼び出せます。

ただし、型名やメソッド名を文字列で指定するため、通常の参照よりもエラーに気づきにくくなります。初心者はまず通常の参照追加による呼び出しを理解してから、動的読み込みを学ぶとよいでしょう。

6. C#のDLL参照・呼び出しでよくあるエラーと原因

6-1. 「型または名前空間の名前が見つかりません」の原因と対処法

C#でDLLを参照したときによく出るエラーが、次のようなメッセージです。

型または名前空間の名前が見つかりません

主な原因は次のとおりです。

・DLL参照が追加されていない
・usingの名前空間が間違っている
・クラスがpublicになっていない
・メソッドやクラス名を間違えている
・ターゲットフレームワークに互換性がない

まず確認すべきなのは、参照が正しく追加されているかです。次に、DLL側の名前空間と呼び出し側のusingが一致しているかを確認します。

DLL側が次のような名前空間の場合、

C#
namespace MyLibrary.Services
{
public class UserService
{
}
}

呼び出し側では次のように書く必要があります。

C#
using MyLibrary.Services;

また、クラスがpublicになっていない場合、参照できても呼び出し側から見えません。

6-2. 「参照を追加できません」の原因と対処法

Visual StudioでDLLを追加しようとしたときに、「参照を追加できません」と表示されることがあります。

主な原因は次のとおりです。

・.NET製DLLではなくネイティブDLLを参照しようとしている
・DLLが破損している
・ターゲットフレームワークが対応していない
・古い形式または互換性のないDLLである
・DLLではないファイルを選択している

CやC++で作成されたネイティブDLLは、通常の「参照の追加」では使えないことがあります。その場合は、DllImportを使って呼び出します。

.NET製DLLかどうか分からない場合は、提供元のドキュメントを確認するか、DLLの作成方法を確認しましょう。

6-3. 「ファイルまたはアセンブリを読み込めませんでした」の原因と対処法

実行時に次のようなエラーが出ることがあります。

ファイルまたはアセンブリを読み込めませんでした

このエラーは、ビルド時には問題がなくても、実行時に必要なDLLを読み込めない場合に発生します。

主な原因は次のとおりです。

・実行フォルダにDLLが存在しない
・依存DLLが不足している
・DLLのバージョンが違う
・ターゲットフレームワークが合っていない
・参照先のパスが間違っている

対処法としては、まず実行ファイルと同じフォルダに必要なDLLがあるか確認します。

MyApp.exe
MyLibrary.dll
DependentLibrary.dll

DLL本体だけでなく、そのDLLが依存している別のDLLも必要になる場合があります。

6-4. 「BadImageFormatException」が発生する原因と対処法

BadImageFormatExceptionは、DLLの形式が実行環境と合わない場合によく発生します。

特に多い原因は、32bitと64bitの不一致です。

・C#アプリがx64なのにDLLがx86
・C#アプリがx86なのにDLLがx64
・.NET製DLLではないものを.NETアセンブリとして参照しようとしている

対処法としては、Visual Studioのプロジェクト設定でプラットフォームターゲットを確認します。

プロジェクトのプロパティ → ビルド → プラットフォームターゲット

ネイティブDLLを利用する場合は、DLLのビット数に合わせてx86またはx64を設定しましょう。

6-5. 「DllNotFoundException」が発生する原因と対処法

DllNotFoundExceptionは、DllImportで指定したDLLが見つからない場合に発生します。

主な原因は次のとおりです。

・DLLファイルが実行フォルダにない
・DLL名の指定が間違っている
・DLLがPATHの通った場所にない
・依存しているネイティブDLLが不足している

たとえば、次のように指定している場合、

C#
[DllImport("SampleNative.dll")]
private static extern int Execute();

SampleNative.dllが実行ファイルと同じフォルダに存在するか確認します。

また、指定したDLL自体は存在していても、そのDLLが内部で別のDLLに依存している場合、依存DLLが不足していると読み込みに失敗します。

6-6. 「EntryPointNotFoundException」が発生する原因と対処法

EntryPointNotFoundExceptionは、DllImportで指定した関数名がDLL内に見つからない場合に発生します。

主な原因は次のとおりです。

・関数名を間違えている
・C++側で名前修飾されている
・DLLに対象関数がエクスポートされていない
・CallingConventionの指定が合っていない

たとえば、C#側で次のように指定しているとします。

C#
[DllImport("Sample.dll")]
private static extern int Add(int a, int b);

しかし、実際のDLL側にAddという名前で関数が公開されていなければ、EntryPointNotFoundExceptionが発生します。

関数名が違う場合は、EntryPointを指定できます。

C#
[DllImport("Sample.dll", EntryPoint = "AddNumbers")]
private static extern int Add(int a, int b);

C/C++製DLLを呼び出す場合は、DLL側でどの関数がどの名前でエクスポートされているかを確認することが重要です。

6-7. DLLのバージョン違いによるエラーの対処法

DLLのバージョン違いも、C#開発でよくあるトラブルです。

たとえば、開発環境では新しいDLLを参照しているのに、実行環境には古いDLLが配置されている場合、次のような問題が起きます。

・存在するはずのメソッドが見つからない
・引数の数や型が合わない
・実行時にアセンブリ読み込みエラーが出る
・想定と違う処理が実行される

対処法としては、次の点を確認しましょう。

・参照しているDLLのバージョン
・出力フォルダに配置されているDLLのバージョン
・本番環境に配置したDLLのバージョン
・依存DLLのバージョン

DLLを差し替える場合は、ファイル名だけでなく、更新日時やアセンブリバージョンも確認することが大切です。

7. DLLを配布・配置するときの注意点

7-1. exeファイルとDLLファイルの配置場所

C#アプリを配布するときは、実行ファイルだけでなく、必要なDLLも一緒に配置する必要があります。

基本的には、EXEファイルと同じフォルダにDLLを置きます。

MyApp.exe
MyLibrary.dll

DLLが別のフォルダにある場合、アプリがそのDLLを見つけられず、実行時エラーになることがあります。

Visual Studioでビルドした場合は、出力フォルダに必要なDLLがコピーされているか確認しましょう。

7-2. 依存DLLも一緒に配布する必要があるケース

作成したDLLが別のDLLに依存している場合、その依存DLLも一緒に配布する必要があります。

たとえば、次のような構成です。

MyApp.exe
MyLibrary.dll
CommonUtility.dll
ExternalLibrary.dll

MyApp.exeが直接参照しているのはMyLibrary.dllだけでも、MyLibrary.dllCommonUtility.dllExternalLibrary.dllを使っている場合、それらも実行時に必要になります。

「DLLは置いてあるのに読み込めない」という場合は、依存DLLが不足していないか確認しましょう。

7-3. 参照設定の「ローカルコピー」を確認する

Visual Studioの参照設定には、「ローカルコピー」という設定があります。

ローカルコピーが有効になっていると、ビルド時に参照DLLが出力フォルダへコピーされます。無効になっていると、出力フォルダにコピーされない場合があります。

実行時にDLLが見つからない場合は、参照のプロパティで次の項目を確認しましょう。

ローカルコピー
Copy Local

通常は、アプリケーションと一緒に配布する必要があるDLLはローカルコピーを有効にしておくと扱いやすいです。

7-4. DLLの差し替え時に注意すべき互換性

DLLを更新して差し替える場合は、呼び出し側との互換性に注意が必要です。

たとえば、次のような変更は呼び出し側に影響します。

・publicメソッドを削除する
・メソッド名を変更する
・引数の数を変更する
・引数の型を変更する
・戻り値の型を変更する
・名前空間を変更する
・クラス名を変更する

このような変更を行うと、呼び出し側のコードがコンパイルエラーになったり、実行時エラーになったりします。

DLLを更新する場合は、既存の呼び出し側が使っている公開メンバーを不用意に変更しないことが重要です。

7-5. セキュリティ面で注意したいDLLの取り扱い

DLLはアプリケーションの一部として実行されるため、信頼できないDLLを読み込むのは危険です。

特に外部から入手したDLLを使う場合は、提供元や改ざんの有無を確認する必要があります。

注意すべき点は次のとおりです。

・出所不明のDLLを使わない
・配布元が信頼できるか確認する
・DLLを不用意に差し替えられないようにする
・本番環境では不要なDLLを配置しない
・DLLの更新履歴やバージョンを管理する

アプリケーションのフォルダに悪意あるDLLが置かれると、意図しないコードが実行される可能性があります。DLLを配布・配置するときは、機能面だけでなくセキュリティ面にも注意しましょう。

8. 初心者向け:C# DLL作成・参照・呼び出しの実践手順

8-1. DLL側プロジェクトを作成する

ここでは、初心者向けにC#でDLLを作成し、別のコンソールアプリから呼び出す手順を紹介します。

まず、Visual Studioでクラスライブラリプロジェクトを作成します。

プロジェクト名:SampleLibrary
種類:クラスライブラリ

このプロジェクトがDLL側になります。

ビルドすると、次のようなDLLが作成されます。

SampleLibrary.dll

8-2. 呼び出されるメソッドを実装する

DLL側に、呼び出し用のクラスとメソッドを作成します。

C#
namespace SampleLibrary
{
public class MessageService
{
public string GetMessage(string name)
{
return $"こんにちは、{name}さん。DLLからのメッセージです。";
}
}
}

ポイントは、クラスとメソッドにpublicを付けることです。

C#
public class MessageService
public string GetMessage(string name)

publicを付けないと、呼び出し側プロジェクトから利用できません。

8-3. 呼び出し側プロジェクトを作成する

次に、同じソリューション内にコンソールアプリを作成します。

プロジェクト名:SampleApp
種類:コンソールアプリ

このSampleAppから、先ほど作成したSampleLibrary.dllを呼び出します。

8-4. DLLを参照に追加する

SampleAppからSampleLibraryを参照します。

同じソリューション内にある場合は、プロジェクト参照を追加します。

SampleAppを右クリック
→ 追加
→ プロジェクト参照
→ SampleLibraryにチェック
→ OK

これで、SampleAppからSampleLibrary内のクラスを使えるようになります。

8-5. コードからDLL内のメソッドを実行する

呼び出し側のProgram.csに次のコードを書きます。

C#
using SampleLibrary;

class Program
{
static void Main()
{
MessageService service = new MessageService();

string message = service.GetMessage("佐藤");

Console.WriteLine(message);
}
}

using SampleLibrary;を追加することで、DLL側の名前空間を読み込んでいます。

その後、MessageServiceのインスタンスを作成し、GetMessageメソッドを呼び出しています。

8-6. 実行結果を確認する

実行すると、コンソールに次のような結果が表示されます。

こんにちは、佐藤さん。DLLからのメッセージです。

これで、C#で作成したDLLを別プロジェクトから参照し、メソッドを呼び出す基本手順は完了です。

うまく動かない場合は、次の点を確認してください。

・DLL側のクラスがpublicになっているか
・メソッドがpublicになっているか
・呼び出し側に参照を追加したか
・usingの名前空間が正しいか
・ソリューション全体をビルドしたか

9. C# DLLに関するよくある質問

9-1. DLLファイルだけで中身のコードは見られる?

C#で作成されたDLLは、元の.csファイルがそのまま入っているわけではありません。しかし、.NETのDLLは中間言語として保存されているため、専用ツールを使うとある程度コードの内容を解析できる場合があります。

つまり、DLLファイルだけを配布しても、完全に中身を隠せるわけではありません。

重要なロジックや秘匿情報をDLL内に含める場合は注意が必要です。特に、パスワード、APIキー、秘密鍵などをDLL内に直接書くのは避けましょう。

9-2. DLLを修正したら呼び出し側も再ビルドが必要?

DLL側の修正内容によります。

メソッド内部の処理だけを変更し、クラス名やメソッド名、引数、戻り値が変わらない場合は、DLLを差し替えるだけで動作することがあります。

一方で、次のような変更をした場合は、呼び出し側の再ビルドが必要になることがあります。

・クラス名を変更した
・メソッド名を変更した
・引数を変更した
・戻り値の型を変更した
・名前空間を変更した
・公開メンバーを削除した

開発中は、DLL側と呼び出し側の両方を再ビルドするのが安全です。

9-3. 他の言語で作ったDLLをC#から使える?

使える場合があります。ただし、DLLの種類によって呼び出し方法が異なります。

C#やVB.NETなどで作られた.NET製DLLであれば、通常は参照を追加して呼び出せます。

CやC++で作られたネイティブDLLの場合は、DllImportを使って呼び出します。

C#
[DllImport("Sample.dll")]
private static extern int Execute();

ただし、ネイティブDLLを呼び出す場合は、関数名、引数の型、戻り値の型、文字コード、呼び出し規約、32bit・64bitの違いに注意が必要です。

9-4. DLLが参照できないときは何を確認すべき?

DLLが参照できないときは、次の順番で確認すると原因を見つけやすくなります。

1. DLLが.NET製DLLかネイティブDLLか
2. 呼び出し側のターゲットフレームワークと互換性があるか
3. DLLファイルが破損していないか
4. 参照設定が正しく追加されているか
5. DLL側のクラスやメソッドがpublicか
6. usingの名前空間が正しいか
7. 32bit・64bitが一致しているか
8. 依存DLLが不足していないか

特に初心者の場合、.NET製DLLではないDLLを参照に追加しようとしているケースや、publicを付け忘れているケースがよくあります。

9-5. DLLとNuGetパッケージは何が違う?

DLLは、コンパイル済みのライブラリファイルそのものです。一方、NuGetパッケージは、DLLや依存関係、バージョン情報、メタデータなどをまとめて管理する仕組みです。

簡単に言うと、DLLは「部品そのもの」、NuGetパッケージは「部品を配布・管理するための箱」のようなものです。

DLL:
ライブラリ本体のファイル

NuGetパッケージ:
DLL、依存関係、バージョン情報などをまとめた配布形式

個人の学習や小規模なプロジェクトではDLLを直接参照する方法でも問題ありません。

一方、複数のプロジェクトやチームで同じライブラリを使う場合は、NuGetパッケージとして管理した方がバージョン管理や配布がしやすくなります。

まとめ

C#でDLLを使うと、共通処理をライブラリ化して複数のプロジェクトから再利用できます。DLLを作成するには、Visual Studioでクラスライブラリプロジェクトを作成し、外部に公開したいクラスやメソッドにpublicを付けてビルドします。

作成したDLLを呼び出すには、呼び出し側プロジェクトで参照を追加し、必要に応じてusingで名前空間を読み込みます。その後は、通常のC#クラスと同じようにインスタンスを作成したり、staticメソッドを呼び出したりできます。

C#のDLLでつまずきやすいポイントは、参照設定、名前空間、public指定、ターゲットフレームワーク、32bit・64bit、依存DLL、バージョン違いです。

特に初心者は、次の流れを押さえておくと理解しやすくなります。

1. クラスライブラリでDLLを作る
2. publicなクラス・メソッドを用意する
3. ビルドしてDLLを出力する
4. 呼び出し側で参照を追加する
5. usingで名前空間を読み込む
6. コードからメソッドを呼び出す
7. 実行フォルダにDLLが配置されているか確認する

C#のDLLは、処理の共通化や再利用、保守性向上に役立つ基本的な仕組みです。最初は小さなクラスライブラリを作成し、別プロジェクトから呼び出すところから練習すると、DLLの作成・参照・呼び出しの流れを実践的に理解できます。