64bitWindows + C# でDLLが読み込めない。

結構はまったのでメモ。

64biWindowsを利用している場合、32bit設定でビルドされたDLLは読み込むことができません。

// hoge32.dll内にある、void HogeFunc()をインポートして、delegateに格納する例。
[DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("kernel32", SetLastError = true)]
internal static extern bool FreeLibrary(IntPtr hModule);
[DllImport("kernel32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = false)]
internal static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
//
private delegate void HogeFunc();
//
private void button1_Click(object sender, EventArgs e)
{
    // DLLを読み込む。
    IntPtr handle = LoadLibrary("hoge32.dll");
    if (handle == null) {
        // 64bitOSで32bitビルドのDLLを読み込んだ場合、ここでresult == 193が返ってくる。
        int result = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
        System.Diagnostics.Debug.WriteLine(result);
    }
    IntPtr funcPtr = GetProcAddress(handle, "HogeFunc");
    //
    //
    HogeFunc hogefunc = (hogefunc)System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer(funcPtr, typeof(hogefunc));
    hogefunc();
    FreeLibrary(handle);
}

しかし、C#のデフォルト設定では「Any CPU」に設定されており、動作するWindowsのバージョンにあわせて64/32bitのアプリケーション側で切り替えるようにビルドします。ある意味賢い。

しかし、そうなると64bit環境で動かす場合、DLLが読み込めなくなってしまいます。

これを何とかするには、以下の二つの方法があります。

  1. DLLを64bit環境でビルドしなおす。
  2. ビルド設定で切り替え機能を無効にし、32bitアプリケーションとして実行するように変更する。
  3. ビルド後のアプリケーションの設定を変更して、32bitアプリケーションとして実行するように変更する。

DLLをリビルドできる環境であれば、1.を試すべきですが、あいにく今回は取れない(Susieプラグインを読み込みたい)。
というわけで、2.を試してみたのですが、メニューのどこを見渡しても32/64bitのビルド切り替えが見つからない。

これは、デフォルト設定では隠しメニューなので出てこないのです。

やり方は(VC#2008で説明します)、

  1. Visual C# のメニューからツール->オプションを選択。
  2. 左下の「すべての設定を表示」にチェックを入れる。
  3. オプションダイアログの左のツリーから「プロジェクトおよびソリューション」の「全般」を選択。
  4. 「ビルド構成の詳細を表示」にチェックを入れる。
  5. これでCPUが選択できます。VisualC#のメニューから「ビルド->構成マネージャ」
  6. 「アクティブソリューションプラットフォーム」のコンボボックスから「新規作成」を選択。
  7. 「新しいプラットフォームを入力または選択してください」のドロップボックスからx86を選択。設定のコピー元は「Any CPU」を選択してOKを押す。
  8. あとはそのままビルド。32bit DLLの関数も実行できるはずです。