71

これが状況です。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);

これまでのところ単純です。

ご想像のとおり、ソフトウェアは「AnyCPU」をオンにしてコンパイルされています。

また、システムで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つの実行ファイル(1つは32ビット用、もう1つは64ビット用)は必要ありませんか?DllImportステートメントで使用する前にDLL_FILE_NAMEを設定するにはどうすればよいですか?

4

9 に答える 9

70

これを行う最も簡単な方法は、異なる名前の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);
}

もちろん、インポートが多い場合は、手動で保守するのが非常に面倒になる可能性があります。

于 2012-06-01T15:08:28.610 に答える
67

これは、2つのDLLが同じ名前で、異なるフォルダーに配置されていることを必要とする別の方法です。例えば:

  • win32/MyDll.dll
  • win64/MyDll.dll

LoadLibrary秘訣は、CLRが実行する前にDLLを手動でロードすることです。次に、aMyDll.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);
}

編集2017/02/01シャドウコピーが有効になっAssembly.CodeBaseている場合でも機能するように使用します。

于 2015-06-04T14:07:31.040 に答える
17

この場合、私は次のようにする必要があります(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);
}
于 2015-06-29T12:24:33.870 に答える
8

DLLファイル名を保持する静的変数があります

静的変数ではありません。これは、コンパイル時に一定です。実行時にコンパイル時定数を変更することはできません。

この問題に対処する正しい方法は何でしょうか?

正直なところ、x86をターゲットにして、64ビットバージョンをすべて忘れて、アプリケーションをx64として実行する必要がない限り、アプリケーションをWOW64で実行することをお勧めします。

x64が必要な場合は、次のことができます。

  • DLLを同じ名前(など)に変更し、MyDll.dllインストール/デプロイ時に適切なものを配置します。(OSがx64の場合は、64ビットバージョンのDLLを展開し、そうでない場合はx86バージョンを展開します)。

  • x86用とx64用の2つの別々のビルドを一緒に用意します。

于 2012-06-01T15:02:04.260 に答える
2

あなたが説明するものは「サイドバイサイドアセンブリ」(同じアセンブリの2つのバージョン、1つは32ビット、もう1つは64ビット)として知られています...これらは役立つと思います。

ここでは、まさにシナリオのウォークスルーを見つけることができます(ネイティブDLLを参照するC ++ / CLIDLLをラップする.NETDLL)。

おすすめ:

それをx86としてビルドし、それで完了します...または2つのビルド(1つはx86と1つはx64)を持ちます...上記の手法はかなり複雑なので...

于 2012-06-01T15:03:56.420 に答える
2

別のアプローチは

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);
}
于 2015-07-01T14:20:55.730 に答える
0

私はvcsjonesが意味するアプローチの1つを使用しました:

「DLLをMyDll.dllなどの同じ名前に変更し、インストール/展開時に適切なものを配置します。」

このアプローチでは、2つのビルドプラットフォームを維持する必要がありますが、詳細については、次のリンクを参照してください:https ://stackoverflow.com/a/6446638/38368

于 2012-06-03T10:12:32.610 に答える
0

私がV8.Netに使用するトリックは、次のとおりです。

  1. 異なるアーキテクチャを切り替えるためのすべての定義を含む新しいC#「プロキシインターフェイス」プロジェクトを作成します。私の場合、プロジェクトの名前は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つを参照しないでください。

  1. さらに2つのプロジェクトを作成して、ライブラリのx64バージョンとx86バージョンを生成します。これは非常に簡単です。コピーアンドペーストし.csprojて同じフォルダ内のファイルを複製し、名前を変更するだけです。私の場合、プロジェクトファイルの名前がV8.Net-ProxyInterface-x64andに変更さV8.Net-ProxyInterface-x86れた後、プロジェクトをソリューションに追加しました。Visual Studioでそれぞれのプロジェクト設定を開き、Assembly Name名前にx64またはx86が含まれていることを確認します。この時点で、3つのプロジェクトがあります。最初の「プレースホルダー」プロジェクトと2つのアーキテクチャ固有のプロジェクトです。2つの新しいプロジェクトの場合:

    a)x64インターフェイスプロジェクトの設定を開き、Buildタブに移動し、上部でを選択All Platformsして、と入力します。Platformx64Conditional compilation symbols

    b)x86インターフェイスプロジェクトの設定を開き、Buildタブに移動し、上部でを選択All Platformsして、と入力します。Platformx86Conditional compilation symbols

  2. 開いて、それがx64プロジェクトのプラットフォームとして選択され、x86プロジェクトの両方と構成で選択されBuild->Configuration Manager...ていることを確認します。x64x86DebugRelease

  3. 2つの新しいインターフェイスプロジェクト(x64およびx86の場合)がホストプロジェクトの同じ場所に出力されることを確認します(プロジェクト設定を参照Build->Output path)。

  4. 最後の魔法:エンジンの静的コンストラクターで、アセンブリリゾルバーにすばやく接続します。

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 Localfalseます。これにより、リゾルバーを使用して実際にロードする関数を特定しながら、P/Invoke関数ごとに1つの名前で開発できます。

アセンブリローダーは必要な場合にのみ実行されることに注意してください。これは(私の場合)エンジンクラスへの最初のアクセス時にCLRシステムによって自動的にトリガーされるだけです。それがどのように変換されるかは、ホストプロジェクトがどのように設計されているかによって異なります。

于 2019-03-04T21:08:17.860 に答える
-1

これはDLLを動的にロードするのに役立つと思います。

   #if X64    
    [DllImport("MyDll64.dll", CallingConvention=CallingConvention.Cdecl, EntryPoint=Func1")]
    #else
    [DllImport("MyDll32.dll", CallingConvention=CallingConvention.Cdecl, EntryPoint=Func1")]
    #endif
    private static extern int is_Func1(int var1, int var2);
于 2020-06-18T05:57:43.437 に答える