作業領域変更で位置を自動補正するWindowの挙動を抑制する手立てアリ!
先日、WPFの位置制御とAPPBAR制御との相性が悪い件で.NET的アプローチでの解決を模索していた中、YOUネイチブでイっちゃいなYO!と、まめしばやんさんからの助言をいただきイっちゃいました。
@ITフォーラムでのデリゲートをGCの対象から外す方法でもフィールドで固定化すりゃいいじゃんという結論なので、それに準じる感じです。
private static Dictionary<Window, Delegate> saveProcs = new Dictionary<Window, Delegate>(); private Window window; private IntPtr origianlWndProc; public AppBarAdapter(Window window) { this.window = window; } /// <summary> /// メッセージフックの登録/解除 /// </summary> public bool IsHooked { get { return saveProcs.ContainsKey(window); } set { if (IsHooked != value) { IntPtr hWnd = WinUtil.HWnd(window); if (hWnd != null) { if (value) { origianlWndProc = GetWindowLong(hWnd, GWL.WNDPROC); Delegate d = new WindowProc(WindowProc); saveProcs.Add(window, d); // 保存しないとCallbackOnCollectedDelegateエラーになる。 IntPtr pointer = Marshal.GetFunctionPointerForDelegate(d); SetWindowLong(hWnd, GWL.WNDPROC, pointer); } else { SetWindowLong(hWnd, GWL.WNDPROC, origianlWndProc); saveProcs.Remove(window); } } } } } public IntPtr WindowProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam) { bool handled = false; IntPtr result = Hook(hWnd, msg, wParam, lParam, ref handled); if (!handled) { result = CallWindowProc(origianlWndProc, hWnd, msg, wParam, lParam); } return result; } private IntPtr Hook(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { // HwndSourceHookデリゲートと同じI/Fです。実装はご随意に…。 // 例)APPBARにしたい場合は、WM_SETTINGCHANGEとWM_WINDOWPOSCHANGINGで位置を改修してhandledをtrueにしてしまえばWPFウィンドウの横槍を抑制できます。 return IntPtr.Zero; }
↑で使っているAPI関連の定義はゴチャゴチャするので以下に分けときます。
enum GWL : int { WNDPROC = (-4), HINSTANCE = (-6), HWNDPARENT = (-8), STYLE = (-16), EXSTYLE = (-20), USERDATA = (-21), ID = (-12), } [DllImport("user32")] static extern IntPtr GetWindowLong(IntPtr hWnd, GWL nIndex); [DllImport("user32")] static extern IntPtr SetWindowLong(IntPtr hWnd, GWL nIndex, IntPtr dwNewLong); [DllImport("user32")] static extern IntPtr CallWindowProc(IntPtr prevFunc, IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); delegate IntPtr WindowProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);