たぷつきません

おなかがでてきた。もうたぷついてるやん。

作業領域変更で位置を自動補正する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);