9

私はDllImport自分のソリューションで使用しています。
私の問題は、同じDLLの2つのバージョンがあり、1つは32ビット用に、もう1つは64ビット用にビルドされていることです。

これらは両方とも、同じ名前と同じ署名を持つ同じ関数を公開します。私の問題は、これらを公開する2つの静的メソッドを使用し、実行時にIntPtrサイズを使用して、呼び出す正しいメソッドを決定する必要があることです。

private static class Ccf_32
{
    [DllImport(myDllName32)]
    public static extern int func1();
}

private static class Ccf_64
{
    [DllImport(myDllName64)]
    public static extern int func1();
}

これを行う必要があるのは、一定myDllName32myDllName64なければならず、実行時に設定する方法が見つからないためです。

IntPtrコードの重複と定数サイズのチェックを取り除くことができるように、誰かがこれに対するエレガントなソリューションを持っていますか?

ファイル名を設定できれば、一度チェックするだけで、大量の繰り返しコードを取り除くことができます。

4

9 に答える 9

20

これを行うには、kernel32.dllからのLoadLibrary呼び出しを使用して、特定のDLLを特定のパスから強制的にロードします。

32ビットと64ビットのDLLに同じ名前を付け、それらを異なるパスに配置した場合、次のコードを使用して、実行しているWindowsのバージョンに基づいて正しいものをロードできます。ccfクラスを参照するコードを参照する前に、ExampleDllLoader.LoadDll()を呼び出すだけです。

private static class ccf
{
    [DllImport("myDllName")]
    public static extern int func1();
}

public static class ExampleDllLoader
{
    [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
    private extern static IntPtr LoadLibrary(string librayName);

    public static void LoadDll()
    {
        String path;

        //IntPtr.Size will be 4 in 32-bit processes, 8 in 64-bit processes 
        if (IntPtr.Size == 4)
            path = "c:/example32bitpath/myDllName.dll";
        else
            path = "c:/example64bitpath/myDllName.dll";

        LoadLibrary(path);
    }
}
于 2009-12-13T14:37:50.440 に答える
13

あなたはおそらく#ifキーワードでこれを達成することができます。と呼ばれる条件付きコンパイラシンボルを定義する場合win32、次のコードはwin32-blockを使用し、それを削除すると、他のブロックを使用します。

#if win32
    private static class ccf_32
    {
        [DllImport(myDllName32)]
        public static extern int func1();
    }
#else    
    private static class ccf_64
    {
        [DllImport(myDllName64)]
        public static extern int func1();
    }
#endif

これはおそらく、現在持っているクラスラッピングを削除できることを意味します。

    private static class ccf
    {
#if win32
        [DllImport(myDllName32)]
        public static extern int func1();
#else    
        [DllImport(myDllName64)]
        public static extern int func1();
#endif
    }

便宜上、コンパイルシンボルを制御するためのビルド構成を作成できると思います。

于 2009-08-23T23:28:17.327 に答える
10

私はこれが本当に古い質問であることを知っています(私は新しいです-古い質問に答えるのは悪いですか?)、しかし私はこれと同じ問題を解決しなければなりませんでした。.EXEが任意のCPU用にコンパイルされている間、OSに基づいて32ビットまたは64ビットのDLLを動的に参照する必要がありました。

DLLImportを使用でき、LoadLibrary()を使用する必要はありません。

SetDLLDirectoryを使用してこれを行いました。名前とは異なりSetDLLDirectory、DLL検索パスに追加され、パス全体が置き換えられることはありません。これにより、Win32およびWin64サブディレクトリに同じ名前のDLL(この説明では「TestDLL.dll」)を配置し、適切に呼び出すことができました。

public partial class frmTest : Form
{
    static bool Win32 = Marshal.SizeOf(typeof(IntPtr)) == 4;
    private string DLLPath = Win32 ? @"\Win32" : @"\Win64";

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool SetDllDirectory(string lpPathName);
    [DllImport("TestDLL.dll", SetLastError = true)]
    static extern IntPtr CreateTestWindow();

    private void btnTest_Click(object sender, EventArgs e)
    {
        string dllDir = String.Concat(Directory.GetCurrentDirectory(), DLLPath);
        SetDllDirectory(dllDir);

        IntPtr newWindow = CreateTestWindow();
    }
}
于 2012-05-01T13:28:27.357 に答える
2

それらをメソッドにラップしてみませんか?

private static class ccf_32_64
{
    private static class ccf_32
    {
        [DllImport(myDllName32)]
        private static extern int func1();
    }

    private static class ccf_64
    {
        [DllImport(myDllName64)]
        private static extern int func1();
    }

    public static int func1()
    {
        if (32bit)
        {
            return ccf_32.func1();
        }
        else
        {
            return ccf_64.func1();
        }
    }
}
于 2009-08-23T23:28:31.710 に答える
1

別のオプションの1つは、アンマネージDLLの32ビットバージョンと64ビットバージョンの両方に同じ名前を付けて、ビルド出力の別々のフォルダー(たとえば、x86\とx64\)に配置することです。

次に、インストーラーまたはこれを配布している他の方法で更新され、インストール先のプラットフォームに適切なDLLがインストールされていることがわかります。

于 2009-08-24T00:07:52.567 に答える
1

2つのメソッドを作成し、ランタイムで1つを選択できるため、Any CPU

public static class Ccf
{
    [DllImport(myDllName32)]
    private static extern int func32();

    [DllImport(myDllName64)]
    private static extern int func64();


    public static int func()
    {
        if(Environment.Is64BitProcess)
        {
            return func64();
        }
        return func32();
    }

}

于 2017-10-26T20:51:16.393 に答える
0

あなたが望むようにこれを行うことはできません。DllImport属性は、コンパイル時に使用されるメタデータと考える必要があります。その結果、動的にインポートしているDLLを変更することはできません。

管理対象コードを「任意のCPU」をターゲットにしたままにする場合は、ランタイム環境に応じて呼び出すことができる2つの異なる関数としてラップされた32ビットライブラリと64ビットライブラリの両方をインポートするか、追加のWin32APIを使用する必要があります。実行時にアンマネージアセンブリの正しいバージョンをレイトロードするための呼び出しと、必要なメソッドを実行するための追加のWin32呼び出し。そこにある欠点は、型の安全性などのために、そのタイプのコードのコンパイル時のサポートがないことです。

于 2009-08-24T00:37:09.117 に答える
0

うーん、32ビットと64ビットのdllに基づくメソッドを使用して、インターフェイスとクラスを作成できるかどうか疑問に思っています。

64ビットを実行しているかどうかを判断する明示的な方法があるかどうかはわかりませんが、次の方法で機能する可能性があります。安全でないコードを許可し、あるアドレスへのポインターを取得して、ポインターが4かどうかを判断する安全でない関数を使用するサイズは8バイト。結果に基づいて、作成するインターフェースの実装を決定します。

于 2009-11-17T05:26:08.953 に答える
0

IntPtrタイプ(とにかくネイティブintと呼ばれます)のサイズをチェックすることで、64ビットを実行しているかどうかを判断できます。次に、インポートされたLoadLibraryW呼び出しを使用して適切なDLLをロードし、GetProcAddressを使用して関数ポインターを取得してから、Marshal.GetDelegateForFunctionPointerをチェックアウトします。

これは、見た目ほど複雑ではありません。LoadLibraryWとGetProcAddressの両方をDllImportする必要があります。

于 2009-11-17T05:36:08.157 に答える