0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

WinUI3Advent Calendar 2022

Day 9

[WinUI3] WinUI3でウインドウプロシージャをフックする(最大/最小ウインドウサイズの指定にも使える)

Last updated at Posted at 2022-08-29

もくじ
https://tera1707.com/entry/2022/02/06/144447#WinUI3

やりたいこと

以前、下記の記事で調べた、WPFでウインドウプロシージャをフックするということを、WinUI3でもやりたい。

前提

  • VisualStudio2022 Community 17.3.0
  • WinUI3 と パッケージプロジェクト
  • Windows App SDK 1.1.3
  • PInvoke.User32 v0.7.124のnugetパッケージを使用

メモ

別記事で、WinUI3で最小ウインドウサイズを指定するやり方を調べたときの副産物として、やり方が分かったのでメモしておく。

やりかた

ざっくり下記のような流れの処理を、メインウインドウの中で行う。

  • WinUI3ウインドウのウインドウハンドルを取ってくる
  • 自分の関心のあるWMメッセージの処理を書いた新しいウインドウプロシージャを用意する
  • SetWindowLong()に、下記をセットして、WMメッセージ到来時新しいプロシージャが呼ばれるようにする
    • ウインドウのハンドル
    • GWL_WNDPROC
    • 用意した新しいウインドウプロシージャ
  • SetWindowLong()の戻り値で、元のウインドウプロシージャが取得できるので、いったん保存する
  • 自分が関心のないメッセージを処理してもらうために、
    • 保存した元のプロシージャを、
    • 新しいプロシージャの最後でCallWindowProc()を使って呼んでやる

サンプルコード

下記のサンプルでは、WM_MOVEをフックして、ウインドウの位置をDebug出力している。

using Microsoft.UI.Xaml;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

// nuget PInvoke.User32 0.7.124

namespace MinWidthJikken
{
    public sealed partial class MainWindow : Window
    {
        public MainWindow()
        {
            this.InitializeComponent();
            // サブクラス化する
            SubClassing();
        }

        private NativeMethods.WinProc newWndProc = null;
        private IntPtr oldWndProc = IntPtr.Zero;

        private void SubClassing()
        {
            // ウインドウのハンドルを取ってくる
            var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
            newWndProc = new NativeMethods.WinProc(NewWindowProc);
            //oldWndProc = NativeMethods.SetWindowLong(hwnd, PInvoke.User32.WindowLongIndexFlags.GWL_WNDPROC, newWndProc);
            oldWndProc = NativeMethods.SetWindowLongPtr64(hwnd, PInvoke.User32.WindowLongIndexFlags.GWL_WNDPROC, newWndProc);
        }

        private IntPtr NewWindowProc(IntPtr hWnd, PInvoke.User32.WindowMessage Msg, IntPtr wParam, IntPtr lParam)
        {
            switch (Msg)
            {
                case PInvoke.User32.WindowMessage.WM_MOVE:
                {
                    // 移動した座標を取得
                    var position = lParam.ToInt32();
                    var x = position % 0x010000;
                    var y = position / 0x010000;
                    Debug.WriteLine($"WM_MOVE ({x}, {y})");
                }
                break;

            }
            // WM_GETMINMAXINFO以外の、自分で処理したくないMsgは、もとのWndProcに任せる
            return NativeMethods.CallWindowProc(oldWndProc, hWnd, Msg, wParam, lParam);
        }
    }

    public class NativeMethods
    {
        public delegate IntPtr WinProc(IntPtr hWnd, PInvoke.User32.WindowMessage Msg, IntPtr wParam, IntPtr lParam);

        //[DllImport("user32")]
        //public static extern IntPtr SetWindowLong(IntPtr hWnd, PInvoke.User32.WindowLongIndexFlags nIndex, WinProc newProc);
        [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
        public static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, PInvoke.User32.WindowLongIndexFlags nIndex, WinProc dwNewLong);
        [DllImport("user32.dll")]
        public static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, PInvoke.User32.WindowMessage Msg, IntPtr wParam, IntPtr lParam);
    }
}

注意

x86ビルドで試していたので、SetWindowLong()を使っていたが、それをx64にすると、WndProcを通らず、うまく動かなくなる。

x64では、SetWindowLongPtr64()を使わないといけない。

        [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
        public static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, PInvoke.User32.WindowLongIndexFlags nIndex, WinProc dwNewLong);

参照

WinUI3で最小ウインドウサイズを指定する方法を調べた記事

「サブクラス化」についてのわかりやすい記事

最大/最小ウインドウサイズの指定の仕方が紹介されたMSのgithubのissue

SetWindowLongA function (winuser.h)

CallWindowProcA function (winuser.h)

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?