C#::X64

従来型DLLをC#から使うときの話はid:lord_hollow:20090606:p1に書いてあるんだけども、32bit環境でずーーっと開発してきたDLLとC#アプリを64Bitマシンで動かしてみたらLoadLibraryで失敗した。
そのときのエラーメッセージは"有効なWin32アプリケーションではありません"。x64環境なのにエラーメッセージはWin32のままですね。怠慢ではないのか!(笑

で、だ。いろいろ調べてみると・・・作ったままの状態のプロジェクトはターゲットプラットフォームが"AnyCPU"になっていて、x86だろうがx64だろうが動く中間コードを吐く。それをx64で動かすとx64モードで動くわけだけど、C++で作ったDLLは当然x86のままなのでまー普通に動かない、というのが原因のようです。

x64環境はどちらかというとイレギュラーな環境なので、DLLをx64でビルドしなおすという解決方法は使わないとすると、対策としては、C#プロジェクトの[プラットフォーム ターゲット] プロパティを「X86」に設定するだけ、らしい。これでx64環境でもx86として実行されるようになる(遅くなったりするのかも)。
ところがどっこいこのプロパティはExpressEditionでは表示されないので、その場合はcsprojファイルを直接編集する必要があるようです。
http://www.microsoft.com/japan/msdn/vstudio/express/support/knownIssues/

最初の セクションを探し、次の行を追加します。
x86

ところで、Win32APIを呼んで失敗したときのGetLastError/FormatMessageまでの一連の流れはC#では以下のように簡単に書けるようです。

class Library : IDisposable{
  [DllImport("kernel32.dll", SetLastError=true)]
  private static extern IntPtr LoadLibrary(String lpFileName);
  [DllImport("kernel32.dll")]
  private static extern Boolean FreeLibrary(IntPtr hLibModule);

  public Library(string dllName){
    hModule = LoadLibrary(dllName);
    if (hModule == IntPtr.Zero){
      throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
    }
  }
  public void Dispose(){
    FreeLibrary(hModule);
  }
}

インポートするときの属性に"SetLastError=true"を追加して、コール後に戻り値が異常だったらWin32Exception(Marshal.GetLastWin32Error())をスローするだけ。便利だね。マーシャラ賢すぎる。きっとこの部分の開発チームは超苦労したことでしょう。
FormatMessageの可変長部分は処理されないので、必要に応じて別途置換する必要があるようですが。