もくじ
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)