ここに状況があります。私はdot.netアプリケーションでCベースのdllを使用しています。 2つのDLLがあり、1つはMyDll32.dllと呼ばれる32ビットで、もう1つはMyDll64.dllと呼ばれる64ビットバージョンです。
DLLファイル名:文字列DLL_FILE_NAMEを保持する静的変数があります。
そして、次の方法で使用されます。
[DllImport(DLL_FILE_NAME, CallingConvention=CallingConvention.Cdecl, EntryPoint=Func1")]
private static extern int is_Func1(int var1, int var2);
これまでのところシンプル。
ご想像のとおり、ソフトウェアは「Any CPU」をオンにしてコンパイルされています。
また、システムで64ビットファイルを使用するか32ビットファイルを使用するかを決定する次のコードもあります。
#if WIN64
public const string DLL_FILE_NAME = "MyDll64.dll";
#else
public const string DLL_FILE_NAME = "MyDll32.dll";
#endif
これで問題が表示されるはずです。DLL_FILE_NAMEは実行時ではなくコンパイル時に定義されているため、実行コンテキストに従って正しいdllがロードされません。
この問題に対処する正しい方法は何でしょうか? 2つの実行ファイル(32ビット用と64ビット用)が必要ないのですか? DLL_FILE_NAMEを設定するにはどうすればよいですか?before DllImportステートメントで使用されていますか?
これを行う最も簡単な方法は、異なる名前の2つのメソッドをインポートし、正しいメソッドを呼び出すことです。 DLLは呼び出しが行われるまでロードされないので問題ありません:
[DllImport("MyDll32.dll", EntryPoint = "Func1", CallingConvention = CallingConvention.Cdecl)]
private static extern int Func1_32(int var1, int var2);
[DllImport("MyDll64.dll", EntryPoint = "Func1", CallingConvention = CallingConvention.Cdecl)]
private static extern int Func1_64(int var1, int var2);
public static int Func1(int var1, int var2) {
return IntPtr.Size == 8 /* 64bit */ ? Func1_64(var1, var2) : Func1_32(var1, var2);
}
もちろん、多くのインポートがある場合、これを手動で維持するのは非常に面倒になります。
2つのDLLが同じ名前を持ち、異なるフォルダーに配置されることを必要とする別の代替方法を次に示します。例えば:
win32/MyDll.dll
win64/MyDll.dll
トリックは、CLRが実行する前に手動でDLLをLoadLibrary
でロードします。その後、MyDll.dll
が既にロードされていることを確認して使用します。
これは、親クラスの静的コンストラクターで簡単に実行できます。
static class MyDll
{
static MyDll()
{
var myPath = new Uri(typeof(MyDll).Assembly.CodeBase).LocalPath;
var myFolder = Path.GetDirectoryName(myPath);
var is64 = IntPtr.Size == 8;
var subfolder = is64 ? "\\win64\\" : "\\win32\\";
LoadLibrary(myFolder + subfolder + "MyDll.dll");
}
[DllImport("kernel32.dll")]
private static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("MyDll.dll")]
public static extern int MyFunction(int var1, int var2);
}
EDIT 2017/02/01:Assembly.CodeBase
を使用して、 Shadow Copying が有効になっていても動作するようにします。
この場合、私はこのようにする必要があります(x64およびx86の2つのフォルダを作成し、両方のフォルダに同じ名前の対応するdllを配置します):
using System;
using System.Runtime.InteropServices;
using System.Reflection;
using System.IO;
class Program {
static void Main(string[] args) {
var path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
path = Path.Combine(path, IntPtr.Size == 8 ? "x64" : "x86");
bool ok = SetDllDirectory(path);
if (!ok) throw new System.ComponentModel.Win32Exception();
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool SetDllDirectory(string path);
}
DLLファイル名を保持する静的変数があります
静的変数ではありません。コンパイル時の定数です。実行時にコンパイル時定数を変更することはできません。
この問題に対処する正しい方法は何でしょうか?
正直なところ、x86をターゲットにし、64ビットバージョンをすべて忘れて、アプリケーションをWOW64で実行することをお勧めします。ただし、アプリケーションがx64として実行する必要がある場合を除きます。
X64が必要な場合は、次のことができます。
DLLをMyDll.dll
などの同じ名前に変更し、インストール/デプロイ時に正しいものを配置します。 (OSがx64の場合、64ビットバージョンのDLLをデプロイします。それ以外の場合はx86バージョンをデプロイします)。
X86用とx64用の2つの別々のビルドがあります。
あなたが説明するものは「サイドバイサイドアセンブリ」(同じアセンブリの2つのバージョン、1つは32ビット、もう1つは64ビット)として知られています...これらは役に立つと思います:
こちら シナリオにぴったりのチュートリアルを見つけることができます(.NET DLL C++/CLIのラッピングDLLネイティブDLLの参照)。
勧告:
X86としてビルドし、それで完了します...または、2つのビルド(x86とx64)があります...上記の手法はかなり複雑です...
別のアプローチがあります
public static class Sample
{
public Sample()
{
string StartupDirEndingWithSlash = System.IO.Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName) + "\\";
string ResolvedDomainTimeFileName = StartupDirEndingWithSlash + "ABCLib_Resolved.dll";
if (!File.Exists(ResolvedDomainTimeFileName))
{
if (Environment.Is64BitProcess)
{
if (File.Exists(StartupDirEndingWithSlash + "ABCLib_64.dll"))
File.Copy(StartupDirEndingWithSlash + "ABCLib_64.dll", ResolvedDomainTimeFileName);
}
else
{
if (File.Exists(StartupDirEndingWithSlash + "ABCLib_32.dll"))
File.Copy(StartupDirEndingWithSlash + "ABCLib_32.dll", ResolvedDomainTimeFileName);
}
}
}
[DllImport("ABCLib__Resolved.dll")]
private static extern bool SomeFunctionName(ref int FT);
}
私はvcsjonesが意味するアプローチの1つを使用しました。
「DLLをMyDll.dllなどの同じ名前に変更し、インストール/デプロイ時に適切なDLLを配置します。」
このアプローチでは、2つのビルドプラットフォームを維持する必要がありますが、詳細については次のリンクを参照してください。 https://stackoverflow.com/a/6446638/38368
V8.Net に使用するトリックは次のとおりです。
V8.Net-ProxyInterface
という名前が付けられました。例: public unsafe static class V8NetProxy
{
#if x86
[DllImport("V8_Net_Proxy_x86")]
#Elif x64
[DllImport("V8_Net_Proxy_x64")]
#else
[DllImport("V8_Net_Proxy")] // (dummy - NOT USED!)
#endif
public static extern NativeV8EngineProxy* CreateV8EngineProxy(bool enableDebugging, void* debugMessageDispatcher, int debugPort);
これは、参照するプロジェクトです。次の2つを参照しないでください。
ライブラリのx64およびx86バージョンを生成するプロジェクトをさらに2つ作成します。これは非常に簡単です。同じフォルダ内の.csproj
ファイルをコピーして貼り付け、名前を変更するだけです。私の場合、プロジェクトファイルの名前はV8.Net-ProxyInterface-x64
およびV8.Net-ProxyInterface-x86
に変更され、プロジェクトをソリューションに追加しました。 Visual Studioでそれぞれのプロジェクト設定を開き、Assembly Name
の名前にx64またはx86が含まれていることを確認します。この時点で、3つのプロジェクトがあります。最初の「プレースホルダー」プロジェクトと、2つのアーキテクチャ固有のプロジェクトです。 2つの新しいプロジェクトの場合:
a)x64インターフェイスプロジェクト設定を開き、Build
タブに移動して、上部のPlatform
でAll Platforms
を選択し、x64
にConditional compilation symbols
と入力します。
b)x86インターフェースプロジェクト設定を開き、Build
タブに移動して、上部のPlatform
のAll Platforms
を選択し、x86
にConditional compilation symbols
と入力します。
Build->Configuration Manager...
を開き、x64
がx64プロジェクトのプラットフォームとして選択され、x86
がx86プロジェクトで選択されていることを確認します。両方ともDebug
AND Release
構成。
2つの新しいインターフェイスプロジェクト(x64およびx86用)がホストプロジェクトの同じ場所に出力されることを確認します(プロジェクト設定Build->Output path
を参照)。
最後の魔法:エンジンの静的コンストラクターで、アセンブリリゾルバーにすばやくアタッチします。
static V8Engine()
{
AppDomain.CurrentDomain.AssemblyResolve += Resolver;
}
Resolver
メソッドでは、現在のプロセスによって示された現在のプラットフォームに基づいてファイルをロードします(注:このコードは簡略化されたバージョンであり、テストされていません)。
var currentExecPath = Assembly.GetExecutingAssembly().Location;
var platform = Environment.Is64BitProcess ? "x64" : "x86";
var filename = "V8.Net.Proxy.Interface." + platform + ".dll"
return Assembly.LoadFrom(Path.Combine(currentExecPath , fileName));
最後に、ソリューションエクスプローラーでホストプロジェクトに移動し、References
を展開し、手順1で作成した最初のダミープロジェクトを選択して右クリックしてプロパティを開き、Copy Local
をfalse
。これにより、P/Invoke関数ごとに1つの名前で開発し、リゾルバーを使用して実際にロードする関数を決定できます。
アセンブリローダーは、必要な場合にのみ実行されることに注意してください。エンジンクラスへの最初のアクセス時に、CLRシステムによって(私の場合は)自動的にトリガーされます。それがあなたにどのように翻訳されるかは、ホストプロジェクトがどのように設計されているかによります。