SendInputをAnyCpuビルド(C#)で使おうとするとハマる件
構造体/共用体のアライメントの問題で動かない。
元(Win32API)の定義:
良く見かける(C#の)サンプル定義:
これをAnyCpuビルドで構築すると、x64環境では正常動作しない。なぜならば、「struct tagINPUT」はpackedではないので、x64環境ではunion部分のオフセットは「8」になる。FieldOffset(4)→FieldOffset(8)にすれば、x64環境で動作するがx86では動作しなくなる。(x86では試してはいないけど)
x86ビルドにするのが現実的な解決方法だと思う。AnyCpuはリスクの割にメリットがない。ただし「x64環境では大容量メモリ(2G超)を利用したいし、x86/x64を別々にビルドしたくない」という需要もある。(だからClickShotはAnyCpuに戻した)
色々と調べた結果、以下の定義で使うのが良さそうだった。
「xxx.mi.dwFlags = ~」としていた所を「xxx.u.mi.dwFlags = ~」にする必要があるが、悪くないと思う。
他の方法としては、x86/x64で別々の定義(INPUT_x64とか)を作成し、実行時に使い分ける方法もあった。この場合は、そのまま使うと「if(IntPtr.Size==4)~」が多発して可読性が低下するので、もう一段ラップクラスを用意しないと使えないと思う。
あと、基本的な事としてint(Int32)とIntPtrの区別ができている等の基本はちゃんと押える事、この辺り適当なサンプルは多い。(自分も人の事はいえないが・・・)
元(Win32API)の定義:
typedef struct tagINPUT {
DWORD type;
union
{
MOUSEINPUT mi;
KEYBDINPUT ki;
HARDWAREINPUT hi;
};
} INPUT, *PINPUT, FAR* LPINPUT;
良く見かける(C#の)サンプル定義:
[StructLayout(LayoutKind.Explicit)]
private struct INPUT
{
[FieldOffset(0)] public int type;
[FieldOffset(4)] public MOUSEINPUT mi;
[FieldOffset(4)] public KEYBDINPUT ki;
[FieldOffset(4)] public HARDWAREINPUT hi;
};
これをAnyCpuビルドで構築すると、x64環境では正常動作しない。なぜならば、「struct tagINPUT」はpackedではないので、x64環境ではunion部分のオフセットは「8」になる。FieldOffset(4)→FieldOffset(8)にすれば、x64環境で動作するがx86では動作しなくなる。(x86では試してはいないけど)
x86ビルドにするのが現実的な解決方法だと思う。AnyCpuはリスクの割にメリットがない。ただし「x64環境では大容量メモリ(2G超)を利用したいし、x86/x64を別々にビルドしたくない」という需要もある。(だからClickShotはAnyCpuに戻した)
色々と調べた結果、以下の定義で使うのが良さそうだった。
[StructLayout(LayoutKind.Sequential)]
private struct INPUT
{
public int type;
public INPUT_UNION u;
};
[StructLayout(LayoutKind.Explicit)]
private struct INPUT_UNION
{
[FieldOffset(0)] public MOUSEINPUT mi;
[FieldOffset(0)] public KEYBDINPUT ki;
[FieldOffset(0)] public HARDWAREINPUT hi;
};
「xxx.mi.dwFlags = ~」としていた所を「xxx.u.mi.dwFlags = ~」にする必要があるが、悪くないと思う。
他の方法としては、x86/x64で別々の定義(INPUT_x64とか)を作成し、実行時に使い分ける方法もあった。この場合は、そのまま使うと「if(IntPtr.Size==4)~」が多発して可読性が低下するので、もう一段ラップクラスを用意しないと使えないと思う。
あと、基本的な事としてint(Int32)とIntPtrの区別ができている等の基本はちゃんと押える事、この辺り適当なサンプルは多い。(自分も人の事はいえないが・・・)
この記事へのコメント