UIntPtrのZero値

以前の日記で紹介したWindowsXPによるゴーストウインドウを抑制する方法だが、Windows2000上でコードを実行した際に例外が投げられていることに気が付いた。

public static void DisableProcessWindowsGhostingImpl()
{
    int hmod = LoadLibrary("User32");
    UIntPtr funcaddr = GetProcAddress(hmod, "DisableProcessWindowsGhosting");
    if (!funcaddr.Equals(0))
    {
        DisableProcessWindowsGhosting();
    }
    FreeLibrary(hmod);
}
[DllImport("User32")]
public extern static void DisableProcessWindowsGhosting();

内容としてはGetProcAddressで"DisableProcessWindowsGhosting"という名前のWinAPIの関数へのポインタを取得できたならば、同名で定義されている関数をコールするだけのものだが、Windows2000で実行すると、同O/Sには実装されていないはずの関数を呼ぼうとしてSystem.EntryPointNotFoundExceptionがスローされるのだ。

実はこのコード、最初に日記を書いた時から変更されている。これまた過去の日記になるが、64ビット時代で、P/Invokeコードを64ビットプラットホームでも正しく動作するように、GetProcAddressの定義を変えているのだ。

  • 対策前
[DllImport("kernel32", CharSet = CharSet.Ansi)]
public extern static int GetProcAddress(int hModule, string lpProcName);
  • 対策後
[DllImport("kernel32", CharSet = CharSet.Ansi)]
public extern static UIntPtr GetProcAddress(IntPtr hModule, string lpProcName);

64ビットプラットホームへの移行の指針としてはintの型をint32に限定しないことが必要なため、対策後のようにUIntPtrで関数ポインタの戻り値を受けているのだが、そうすると

UIntPtr funcaddr = GetProcAddress(hmod, "DisableProcessWindowsGhosting");

の戻り値は0に相当する値が戻ってきているはずなのだが

(!funcaddr.Equals(0))

この式が真になってしまうのである。なんでやねん、と突っ込みを入れたくなったが、こんなときはMSDNということで調べてみるとUIntPtr構造体にはこんなフィールドが定義されていた。

public static readonly UIntPtr Zero

... 最初からこれ使え、ということなのね....

public static void DisableProcessWindowsGhostingImpl()
{
    int hmod = LoadLibrary("User32");
    UIntPtr funcaddr = GetProcAddress(hmod, "DisableProcessWindowsGhosting");
    if ( funcaddr!= null && funcaddr != UIntPtr.Zero )
    {
        DisableProcessWindowsGhosting();
    }
    FreeLibrary(hmod);
}

どうやらもう一度P/Invokeコードの見直しを行う必要があるようだ。orz ....