📢 Webサイト閉鎖と移転のお知らせ
このWebサイトは2026年9月に閉鎖いたします。
新しい記事は移転先で追加しております。(旧サイトでは記事を追加しておりません)
細 文字列「</source>」を「</syntaxhighlight>」に置換 |
編集の要約なし |
||
| (同じ利用者による、間の2版が非表示) | |||
| 1行目: | 1行目: | ||
== 概要 == | == 概要 == | ||
C# | C#では、実行環境のアーキテクチャがx86 / AMD64に関わらず、適切なモードで実行できるAnyCPUという仕組みが存在する。<br> | ||
<br> | |||
しかし、AnyCPUは実行時までx86 / AMD64のどちらで動作するかは不明である。<br> | |||
これは、C/C++言語やC++/CLI言語で開発されたネイティブライブラリを使用する場合に問題となる。<br> | |||
ネイティブライブラリはコンパイル時に特定のアーキテクチャ (x86またはAMD64) 向けにビルドされるため、実行環境のアーキテクチャと一致しなければ動作しない。<br> | |||
このような場合、どのような解決策を使うにせよ、何らかの制約を強いられることになる。 | つまり、x86向けにビルドされたDLLは64ビットプロセスからは呼び出すことができず、逆にAMD64向けにビルドされたDLLは32ビットプロセスからは呼び出すことができない。<br> | ||
<br> | |||
一方、C#の <code>DllImport</code> 属性は、コンパイル時にネイティブライブラリのパスと関数名を静的に指定する仕組みである。<br> | |||
この指定は実行ファイルに組み込まれるため、実行時に動的に変更することができない。<br> | |||
したがって、AnyCPUでビルドされたC#アプリケーションが実際に32ビットプロセスとして起動するか64ビットプロセスとして起動するかは実行時に決定されるにもかかわらず、<br> | |||
<code>DllImport</code> で指定するDLLのパスはコンパイル時に固定されてしまうという矛盾が生じる。<br> | |||
<br> | |||
例えば、<code>[DllImport("MyNativeLib.dll")]</code> と記述した場合、この指定では単一のDLLパスしか指定できないため、<br> | |||
実行時のアーキテクチャに応じて異なるDLL (例:x86版とAMD64版) を自動的に選択することができない。<br> | |||
この制約により、AnyCPUとネイティブライブラリの組み合わせは相性が悪いと言える。<br> | |||
<br> | |||
このような場合、どのような解決策を使うにせよ、何らかの制約を強いられることになる。<br> | |||
<br> | |||
なお、.NET FrameworkにおけるAnyCPUには、<u>AnyCPU</u> と <u>AnyCPU 32bitPreferred</u> の2種類が存在する。<br> | |||
<u>32bitPreferred</u> は64ビットOS上でも32ビットプロセスとして実行される設定であり、ARM環境等での互換性を考慮した設定である。<br> | |||
<br><br> | |||
== | == 全てのモジュールをx86に固定する方法 == | ||
C# | C#側の実行ファイルをx86に指定することにより、常にx86のライブラリを使用する方法である。<br> | ||
<br> | |||
* | * メリット | ||
* | *: 最も簡単であり、最低限の変更で済む。 | ||
* デメリット | |||
*: 64ビットOSのメリット (多くのメモリが使用でき、一部の処理が高速化する) を活かせない。 | |||
<br><br> | <br><br> | ||
== C# | == C#側のモジュールもx86 / AMD64を別々に用意する方法 == | ||
全てのモジュールに対して、x86 / AMD64を別々に開発する方法である。<br> | |||
<br> | |||
最初に起動する実行ファイルがx86またはAMD64でのビルドになっている場合、それ以降に読み込まれるAnyCPUのライブラリもそれに応じて動作する。<br> | |||
そのため、AnyCPUのライブラリが多く存在する場合もそれらは全てコピーで問題ない。<br> | |||
* | <br> | ||
* | * メリット | ||
*: ユーザに選択を任せることにより、プログラム側で判断をする必要から解放される。 | |||
* デメリット | |||
*: Webサイトに公開する場合等、ユーザは自分の環境に合わせたバージョンをダウンロードする必要がある。 | |||
<br><br> | <br><br> | ||
== DLLをSystemディレクトリに配置する方法 == | == DLLをSystemディレクトリに配置する方法 == | ||
AMD64向けWindowsにはWOW64機能があり、AMD64のSystem32ディレクトリとは別に、x86のプログラムを動作させるためにSysWOW64ディレクトリが存在する。<br> | |||
これを利用して、該当するディレクトリに対して、別々にライブラリを配置する方法である。<br> | |||
<br> | |||
* | * メリット | ||
* | *: ソースコードの修正が不要である。 | ||
* デメリット | |||
*: Systemディレクトリが汚れる。 | |||
*: セキュリティ関連の警告が表示される。 | |||
*: また、アンインストールも煩雑になる。 | |||
<br><br> | <br><br> | ||
== インストーラを使用する方法 == | == インストーラを使用する方法 == | ||
インストーラを使用する場合、インストーラが実行時に実行環境を判別して適切なライブラリを配置するように設定可能なため、不要なライブラリは残らない。<br> | |||
<br> | |||
* | * メリット | ||
* | *: 不要なライブラリが残らない。 | ||
* デメリット | |||
*: インストーラを作成する必要がある。 | |||
*: レジストリにインストール情報が残る。 | |||
*: x86向けライブラリおよびAMD64向けライブラリの両方を開発しなければならない。 | |||
<br><br> | <br><br> | ||
== SetDllDirectory関数を使用する方法 == | == SetDllDirectory関数を使用する方法 == | ||
x86 / AMD64のライブラリを別のディレクトリに配置して呼び分ける方法である。<br> | |||
<br> | |||
ライブラリの配置場所を変更するAPIである<code>SetDllDirectory</code>関数を使用して、実行時にそれぞれのライブラリのパスを指定する。<br> | |||
<br> | |||
<syntaxhighlight lang=" | <syntaxhighlight lang="c#"> | ||
// <summary> | /// <summary> | ||
// | /// DllImport向けに、x86 / AMD64向けのライブラリを別々に設定するためのクラス | ||
// </summary> | /// </summary> | ||
public static class NativeDllDir | public static class NativeDllDir | ||
{ | { | ||
// <summary> | /// <summary> | ||
// | /// DllImport向けに、x86 / AMD64のライブラリのあるディレクトリを設定する | ||
// </summary> | /// </summary> | ||
// <param name="x86DllDir"> | /// <param name="x86DllDir">x86 DLLを配置したディレクトリを指定する | ||
// | /// 指定しなければカレントディレクトリとなる | ||
// </param> | /// </param> | ||
// <param name="x64DllDir"> | /// <param name="x64DllDir">AMD64 DLLを配置したディレクトリを指定する | ||
// | /// 指定しなければカレントディレクトリとなる | ||
// </param> | /// </param> | ||
// <returns> | /// <returns>設定に成功した場合は true</returns> | ||
// <exception cref="PlatformNotSupportedException"> | /// <exception cref="PlatformNotSupportedException">x86でもAMD64でもない場合の例外</exception> | ||
public static bool Set( string x86DllDir = null, string x64DllDir = null ) | public static bool Set( string x86DllDir = null, string x64DllDir = null ) | ||
{ | { | ||
| 68行目: | 94行目: | ||
if ( IntPtr.Size == 8 ) | if ( IntPtr.Size == 8 ) | ||
{ // | { // AMD64 | ||
return SetDllDirectory( string.IsNullOrEmpty( x64DllDir ) ? "." : x64DllDir ); | return SetDllDirectory( string.IsNullOrEmpty( x64DllDir ) ? "." : x64DllDir ); | ||
} | } | ||
else if ( IntPtr.Size == 4 ) | else if ( IntPtr.Size == 4 ) | ||
{ // | { // x86 | ||
return SetDllDirectory( string.IsNullOrEmpty( x86DllDir ) ? "." : x86DllDir ); | return SetDllDirectory( string.IsNullOrEmpty( x86DllDir ) ? "." : x86DllDir ); | ||
} | } | ||
else | else | ||
{ // その他 | { // その他 | ||
throw new PlatformNotSupportedException(); | throw new PlatformNotSupportedException(); | ||
} | } | ||
| 87行目: | 113行目: | ||
static class Program | static class Program | ||
{ | { | ||
[STAThread] | [STAThread] | ||
static void Main( string[] args ) | static void Main( string[] args ) | ||
| 97行目: | 120行目: | ||
System.Reflection.Assembly.GetEntryAssembly().Location ); | System.Reflection.Assembly.GetEntryAssembly().Location ); | ||
NativeDllDir.Set( MyPath + @"\x86", MyPath + @"\x64" ); | NativeDllDir.Set( MyPath + @"\x86", MyPath + @"\x64" ); | ||
// | |||
// DllImportを定義しているクラスを使用 | |||
// ...略 | |||
} | } | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br> | |||
* | * メリット | ||
* | *: <code>SetDllDirectory</code> 関数の呼び出しコードを追加するだけのため、実装が比較的容易である。 | ||
* デメリット | |||
*: Windows XP with Service Pack 1 (SP1) 以降、または、Windows Server 2003以降でのみ動作する。 | |||
*: それ以前のOSの場合は、<code>SetDllDirectory</code> メソッドは使用できない。 | |||
*: x86向け および AMD64向けのライブラリの両方を開発しなければならない。 | |||
*: プロセス全体のDLL検索パスに影響を与えるため、他のライブラリの読み込みにも影響する可能性がある。 | |||
<br><br> | <br><br> | ||
== | == x86向けDLLとAMD64向けDLLの両方をDllImportする方法 == | ||
<code>SetDllDirectory</code> メソッドを使用する方法より簡単に記述可能であるが、中途半端で使用しにくい。<br> | |||
<br> | |||
<syntaxhighlight lang=" | * メリット | ||
*: Windows XP SP1より以前でも使用できる。 | |||
* デメリット | |||
*: x86向けDLLとAMD64向けライブラリの両方を開発する必要がある。 | |||
*: また、呼び出す関数を全て記述する必要がある。 | |||
<br> | |||
<syntaxhighlight lang="c#"> | |||
public class DLL | public class DLL | ||
{ | { | ||
| 121行目: | 156行目: | ||
public delegate IntPtr HogeFunc(); | public delegate IntPtr HogeFunc(); | ||
public static readonly HogeFunc Hoge; | public static readonly HogeFunc Hoge; | ||
static Dll() | static Dll() | ||
{ | { | ||
| 134行目: | 169行目: | ||
} | } | ||
} | } | ||
</syntaxhighlight> | |||
<br><br> | |||
== LoadLibrary / GetProcAddress 関数を使用した動的ロード方法 == | |||
<code>LoadLibrary</code> 関数 と <code>GetProcAddress</code> 関数 を使用して、実行時にライブラリと関数を動的にロードする方法である。<br> | |||
<br> | |||
<syntaxhighlight lang="c#"> | |||
public static class NativeLibraryLoader | |||
{ | |||
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)] | |||
private static extern IntPtr LoadLibrary(string lpFileName); | |||
[DllImport("kernel32", SetLastError = true)] | |||
private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName); | |||
[DllImport("kernel32", SetLastError = true)] | |||
private static extern bool FreeLibrary(IntPtr hModule); | |||
public static IntPtr LoadNativeLibrary(string basePath, string libraryName) | |||
{ | |||
string path; | |||
if (IntPtr.Size == 8) | |||
{ | |||
path = Path.Combine(basePath, "x64", libraryName); | |||
} | |||
else | |||
{ | |||
path = Path.Combine(basePath, "x86", libraryName); | |||
} | |||
return LoadLibrary(path); | |||
} | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
* メリット | |||
*: 細かい制御が可能である。 | |||
*: 必要なタイミングでライブラリをロード / アンロードできる。 | |||
* デメリット | |||
*: 実装が複雑になる。 | |||
*: デリゲートを使用して関数ポインタを取得する必要がある。 | |||
*: エラーハンドリングを適切に実装する必要がある。 | |||
<br><br> | |||
* | == .NET 5以降での改善 == | ||
* | .NET 5以降 (.NET Core 3.0以降) では、<code>NativeLibrary</code> クラスが導入され、ネイティブライブラリの読み込みがより容易になった。<br> | ||
<br> | |||
* メリット | |||
*: クロスプラットフォーム対応である。 | |||
*: <code>RuntimeInformation</code> により、明示的なアーキテクチャ判定が可能である。 | |||
*: <code>NativeLibrary.TryLoad</code> メソッド や <code>NativeLibrary.Free</code> メソッド等の便利なメソッドが提供されている。 | |||
* デメリット | |||
*: .NET Frameworkでは使用できない。 | |||
<br> | |||
<syntaxhighlight lang="c#"> | |||
using System.Runtime.InteropServices; | |||
public static class NativeLibraryHelper | |||
{ | |||
public static IntPtr LoadNativeLibrary(string basePath, string libraryName) | |||
{ | |||
string path; | |||
if (RuntimeInformation.ProcessArchitecture == Architecture.X64) | |||
{ | |||
path = Path.Combine(basePath, "x64", libraryName); | |||
} | |||
else if (RuntimeInformation.ProcessArchitecture == Architecture.X86) | |||
{ | |||
path = Path.Combine(basePath, "x86", libraryName); | |||
} | |||
else | |||
{ | |||
throw new PlatformNotSupportedException(); | |||
} | |||
return NativeLibrary.Load(path); | |||
} | |||
} | |||
</syntaxhighlight> | |||
<br><br> | <br><br> | ||
{{#seo: | |||
|title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki | |||
|keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,電気回路,電子回路,基板,プリント基板 | |||
|description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux | |||
|image=/resources/assets/MochiuLogo_Single_Blue.png | |||
}} | |||
__FORCETOC__ | __FORCETOC__ | ||
[[カテゴリ:C_Sharp]] | [[カテゴリ:C_Sharp]] | ||