Use hInstance of Dalamud for RegisterClassExW

This commit is contained in:
Soreepeong 2025-08-09 08:30:27 +09:00
parent 0ce4f6d598
commit 7705aa800b

View file

@ -1,19 +1,18 @@
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using Dalamud.Memory;
using Serilog; using Serilog;
using TerraFX.Interop.Windows; using TerraFX.Interop.Windows;
using static Dalamud.Interface.ImGuiBackend.Helpers.ImGuiViewportHelpers;
using static TerraFX.Interop.Windows.Windows; using static TerraFX.Interop.Windows.Windows;
using ERROR = TerraFX.Interop.Windows.ERROR; using ERROR = TerraFX.Interop.Windows.ERROR;
@ -585,51 +584,50 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
private struct ViewportHandler : IDisposable private struct ViewportHandler : IDisposable
{ {
[SuppressMessage("ReSharper", "CollectionNeverQueried.Local", Justification = "Keeping references alive")] private static readonly string WindowClassName = typeof(ViewportHandler).FullName!;
private readonly List<object> delegateReferences = new();
private Win32InputHandler input; private Win32InputHandler input;
private nint classNamePtr;
private bool wantUpdateMonitors = true; private bool wantUpdateMonitors = true;
public ViewportHandler(Win32InputHandler input) public ViewportHandler(Win32InputHandler input)
{ {
this.input = input; this.input = input;
this.classNamePtr = Marshal.StringToHGlobalUni("ImGui Platform");
var pio = ImGui.GetPlatformIO(); var pio = ImGui.GetPlatformIO();
pio.PlatformCreateWindow = this.RegisterFunctionPointer<CreateWindowDelegate>(this.OnCreateWindow); pio.PlatformCreateWindow = (delegate* unmanaged[Cdecl]<ImGuiViewportPtr, void>)&OnCreateWindow;
pio.PlatformDestroyWindow = this.RegisterFunctionPointer<DestroyWindowDelegate>(this.OnDestroyWindow); pio.PlatformDestroyWindow = (delegate* unmanaged[Cdecl]<ImGuiViewportPtr, void>)&OnDestroyWindow;
pio.PlatformShowWindow = this.RegisterFunctionPointer<ShowWindowDelegate>(this.OnShowWindow); pio.PlatformShowWindow = (delegate* unmanaged[Cdecl]<ImGuiViewportPtr, void>)&OnShowWindow;
pio.PlatformSetWindowPos = this.RegisterFunctionPointer<SetWindowPosDelegate>(this.OnSetWindowPos); pio.PlatformSetWindowPos = (delegate* unmanaged[Cdecl]<ImGuiViewportPtr, Vector2, void>)&OnSetWindowPos;
pio.PlatformGetWindowPos = this.RegisterFunctionPointer<GetWindowPosDelegate>(this.OnGetWindowPos); pio.PlatformGetWindowPos = (delegate* unmanaged[Cdecl]<Vector2*, ImGuiViewportPtr, Vector2*>)&OnGetWindowPos;
pio.PlatformSetWindowSize = this.RegisterFunctionPointer<SetWindowSizeDelegate>(this.OnSetWindowSize); pio.PlatformSetWindowSize = (delegate* unmanaged[Cdecl]<ImGuiViewportPtr, Vector2, void>)&OnSetWindowSize;
pio.PlatformGetWindowSize = this.RegisterFunctionPointer<GetWindowSizeDelegate>(this.OnGetWindowSize); pio.PlatformGetWindowSize = (delegate* unmanaged[Cdecl]<Vector2*, ImGuiViewportPtr, Vector2*>)&OnGetWindowSize;
pio.PlatformSetWindowFocus = this.RegisterFunctionPointer<SetWindowFocusDelegate>(this.OnSetWindowFocus); pio.PlatformSetWindowFocus = (delegate* unmanaged[Cdecl]<ImGuiViewportPtr, void>)&OnSetWindowFocus;
pio.PlatformGetWindowFocus = this.RegisterFunctionPointer<GetWindowFocusDelegate>(this.OnGetWindowFocus); pio.PlatformGetWindowFocus = (delegate* unmanaged[Cdecl]<ImGuiViewportPtr, byte>)&OnGetWindowFocus;
pio.PlatformGetWindowMinimized = pio.PlatformGetWindowMinimized = (delegate* unmanaged[Cdecl]<ImGuiViewportPtr, byte>)&OnGetWindowMinimized;
this.RegisterFunctionPointer<GetWindowMinimizedDelegate>(this.OnGetWindowMinimized); pio.PlatformSetWindowTitle = (delegate* unmanaged[Cdecl]<ImGuiViewportPtr, byte*, void>)&OnSetWindowTitle;
pio.PlatformSetWindowTitle = this.RegisterFunctionPointer<SetWindowTitleDelegate>(this.OnSetWindowTitle); pio.PlatformSetWindowAlpha = (delegate* unmanaged[Cdecl]<ImGuiViewportPtr, float, void>)&OnSetWindowAlpha;
pio.PlatformSetWindowAlpha = this.RegisterFunctionPointer<SetWindowAlphaDelegate>(this.OnSetWindowAlpha); pio.PlatformUpdateWindow = (delegate* unmanaged[Cdecl]<ImGuiViewportPtr, void>)&OnUpdateWindow;
pio.PlatformUpdateWindow = this.RegisterFunctionPointer<UpdateWindowDelegate>(this.OnUpdateWindow);
// pio.Platform_SetImeInputPos = this.RegisterFunctionPointer<SetImeInputPosDelegate>(this.OnSetImeInputPos); // pio.Platform_SetImeInputPos = this.RegisterFunctionPointer<SetImeInputPosDelegate>(this.OnSetImeInputPos);
// pio.Platform_GetWindowDpiScale = this.RegisterFunctionPointer<GetWindowDpiScaleDelegate>(this.OnGetWindowDpiScale); // pio.Platform_GetWindowDpiScale = this.RegisterFunctionPointer<GetWindowDpiScaleDelegate>(this.OnGetWindowDpiScale);
// pio.Platform_ChangedViewport = this.RegisterFunctionPointer<ChangedViewportDelegate>(this.OnChangedViewport); // pio.Platform_ChangedViewport = this.RegisterFunctionPointer<ChangedViewportDelegate>(this.OnChangedViewport);
var wcex = new WNDCLASSEXW fixed (char* windowClassNamePtr = WindowClassName)
{ {
cbSize = (uint)sizeof(WNDCLASSEXW), var wcex = new WNDCLASSEXW
style = CS.CS_HREDRAW | CS.CS_VREDRAW, {
hInstance = GetModuleHandleW(null), cbSize = (uint)sizeof(WNDCLASSEXW),
hbrBackground = (HBRUSH)(1 + COLOR.COLOR_BACKGROUND), style = CS.CS_HREDRAW | CS.CS_VREDRAW,
lpfnWndProc = (delegate* unmanaged<HWND, uint, WPARAM, LPARAM, LRESULT>)Marshal hInstance = (HINSTANCE)Marshal.GetHINSTANCE(typeof(ViewportHandler).Module),
.GetFunctionPointerForDelegate(this.input.wndProcDelegate), hbrBackground = (HBRUSH)(1 + COLOR.COLOR_BACKGROUND),
lpszClassName = (ushort*)this.classNamePtr, lpfnWndProc = (delegate* unmanaged<HWND, uint, WPARAM, LPARAM, LRESULT>)Marshal
}; .GetFunctionPointerForDelegate(this.input.wndProcDelegate),
lpszClassName = (ushort*)windowClassNamePtr,
};
if (RegisterClassExW(&wcex) == 0) if (RegisterClassExW(&wcex) == 0)
throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()) ?? new("RegisterClassEx Fail"); throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()) ?? new("RegisterClassEx Fail");
}
// Register main window handle (which is owned by the main application, not by us) // Register main window handle (which is owned by the main application, not by us)
// This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports. // This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports.
@ -657,11 +655,11 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
ImGui.GetPlatformIO().Handle->Monitors = default; ImGui.GetPlatformIO().Handle->Monitors = default;
} }
if (this.classNamePtr != 0) fixed (char* windowClassNamePtr = WindowClassName)
{ {
UnregisterClassW((ushort*)this.classNamePtr, GetModuleHandleW(null)); UnregisterClassW(
Marshal.FreeHGlobal(this.classNamePtr); (ushort*)windowClassNamePtr,
this.classNamePtr = 0; (HINSTANCE)Marshal.GetHINSTANCE(typeof(ViewportHandler).Module));
} }
pio.PlatformCreateWindow = null; pio.PlatformCreateWindow = null;
@ -750,13 +748,8 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
} }
} }
private void* RegisterFunctionPointer<T>(T obj) [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
{ private static void OnCreateWindow(ImGuiViewportPtr viewport)
this.delegateReferences.Add(obj);
return Marshal.GetFunctionPointerForDelegate(obj).ToPointer();
}
private void OnCreateWindow(ImGuiViewportPtr viewport)
{ {
var data = (ImGuiViewportDataWin32*)Marshal.AllocHGlobal(Marshal.SizeOf<ImGuiViewportDataWin32>()); var data = (ImGuiViewportDataWin32*)Marshal.AllocHGlobal(Marshal.SizeOf<ImGuiViewportDataWin32>());
viewport.PlatformUserData = data; viewport.PlatformUserData = data;
@ -784,12 +777,12 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
}; };
AdjustWindowRectEx(&rect, (uint)data->DwStyle, false, (uint)data->DwExStyle); AdjustWindowRectEx(&rect, (uint)data->DwStyle, false, (uint)data->DwExStyle);
fixed (char* pwszWindowTitle = "Untitled") fixed (char* windowClassNamePtr = WindowClassName)
{ {
data->Hwnd = CreateWindowExW( data->Hwnd = CreateWindowExW(
(uint)data->DwExStyle, (uint)data->DwExStyle,
(ushort*)this.classNamePtr, (ushort*)windowClassNamePtr,
(ushort*)pwszWindowTitle, (ushort*)windowClassNamePtr,
(uint)data->DwStyle, (uint)data->DwStyle,
rect.left, rect.left,
rect.top, rect.top,
@ -797,8 +790,8 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
rect.bottom - rect.top, rect.bottom - rect.top,
parentWindow, parentWindow,
default, default,
GetModuleHandleW(null), (HINSTANCE)Marshal.GetHINSTANCE(typeof(ViewportHandler).Module),
default); null);
} }
data->HwndOwned = true; data->HwndOwned = true;
@ -806,7 +799,8 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
viewport.PlatformHandle = viewport.PlatformHandleRaw = data->Hwnd; viewport.PlatformHandle = viewport.PlatformHandleRaw = data->Hwnd;
} }
private void OnDestroyWindow(ImGuiViewportPtr viewport) [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
private static void OnDestroyWindow(ImGuiViewportPtr viewport)
{ {
// This is also called on the main viewport for some reason, and we never set that viewport's PlatformUserData // This is also called on the main viewport for some reason, and we never set that viewport's PlatformUserData
if (viewport.PlatformUserData == null) return; if (viewport.PlatformUserData == null) return;
@ -817,7 +811,11 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
{ {
// Transfer capture so if we started dragging from a window that later disappears, we'll still receive the MOUSEUP event. // Transfer capture so if we started dragging from a window that later disappears, we'll still receive the MOUSEUP event.
ReleaseCapture(); ReleaseCapture();
SetCapture(this.input.hWnd); if (viewport.ParentViewportId != 0)
{
var parentViewport = ImGui.FindViewportByID(viewport.ParentViewportId);
SetCapture((HWND)parentViewport.PlatformHandle);
}
} }
if (data->Hwnd != nint.Zero && data->HwndOwned) if (data->Hwnd != nint.Zero && data->HwndOwned)
@ -836,7 +834,8 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
viewport.PlatformUserData = viewport.PlatformHandle = null; viewport.PlatformUserData = viewport.PlatformHandle = null;
} }
private void OnShowWindow(ImGuiViewportPtr viewport) [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
private static void OnShowWindow(ImGuiViewportPtr viewport)
{ {
var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData;
@ -846,7 +845,8 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
ShowWindow(data->Hwnd, SW.SW_SHOW); ShowWindow(data->Hwnd, SW.SW_SHOW);
} }
private void OnUpdateWindow(ImGuiViewportPtr viewport) [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
private static void OnUpdateWindow(ImGuiViewportPtr viewport)
{ {
// (Optional) Update Win32 style if it changed _after_ creation. // (Optional) Update Win32 style if it changed _after_ creation.
// Generally they won't change unless configuration flags are changed, but advanced uses (such as manually rewriting viewport flags) make this useful. // Generally they won't change unless configuration flags are changed, but advanced uses (such as manually rewriting viewport flags) make this useful.
@ -907,17 +907,18 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
} }
} }
private Vector2* OnGetWindowPos(Vector2* returnStorage, ImGuiViewportPtr viewport) [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
private static Vector2* OnGetWindowPos(Vector2* returnValueStorage, ImGuiViewportPtr viewport)
{ {
var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData;
var pt = new POINT { x = 0, y = 0 }; var pt = new POINT { x = 0, y = 0 };
ClientToScreen(data->Hwnd, &pt); ClientToScreen(data->Hwnd, &pt);
returnStorage->X = pt.x; *returnValueStorage = new(pt.x, pt.y);
returnStorage->Y = pt.y; return returnValueStorage;
return returnStorage;
} }
private void OnSetWindowPos(ImGuiViewportPtr viewport, Vector2 pos) [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
private static void OnSetWindowPos(ImGuiViewportPtr viewport, Vector2 pos)
{ {
var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData;
var rect = new RECT((int)pos.X, (int)pos.Y, (int)pos.X, (int)pos.Y); var rect = new RECT((int)pos.X, (int)pos.Y, (int)pos.X, (int)pos.Y);
@ -934,17 +935,18 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
SWP.SWP_NOACTIVATE); SWP.SWP_NOACTIVATE);
} }
private Vector2* OnGetWindowSize(Vector2* returnStorage, ImGuiViewportPtr viewport) [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
private static Vector2* OnGetWindowSize(Vector2* returnValueStorage, ImGuiViewportPtr viewport)
{ {
var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData;
RECT rect; RECT rect;
GetClientRect(data->Hwnd, &rect); GetClientRect(data->Hwnd, &rect);
returnStorage->X = rect.right - rect.left; *returnValueStorage = new(rect.right - rect.left, rect.bottom - rect.top);
returnStorage->Y = rect.bottom - rect.top; return returnValueStorage;
return returnStorage;
} }
private void OnSetWindowSize(ImGuiViewportPtr viewport, Vector2 size) [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
private static void OnSetWindowSize(ImGuiViewportPtr viewport, Vector2 size)
{ {
var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData;
@ -962,7 +964,8 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
SWP.SWP_NOACTIVATE); SWP.SWP_NOACTIVATE);
} }
private void OnSetWindowFocus(ImGuiViewportPtr viewport) [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
private static void OnSetWindowFocus(ImGuiViewportPtr viewport)
{ {
var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData;
@ -971,26 +974,30 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
SetFocus(data->Hwnd); SetFocus(data->Hwnd);
} }
private bool OnGetWindowFocus(ImGuiViewportPtr viewport) [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
private static byte OnGetWindowFocus(ImGuiViewportPtr viewport)
{ {
var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData;
return GetForegroundWindow() == data->Hwnd; return GetForegroundWindow() == data->Hwnd ? (byte)1 : (byte)0;
} }
private bool OnGetWindowMinimized(ImGuiViewportPtr viewport) [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
private static byte OnGetWindowMinimized(ImGuiViewportPtr viewport)
{ {
var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData;
return IsIconic(data->Hwnd); return IsIconic(data->Hwnd) ? (byte)1 : (byte)0;
} }
private void OnSetWindowTitle(ImGuiViewportPtr viewport, string title) [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
private static void OnSetWindowTitle(ImGuiViewportPtr viewport, byte* title)
{ {
var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData;
fixed (char* pwszTitle = title) fixed (char* pwszTitle = MemoryHelper.ReadStringNullTerminated((nint)title))
SetWindowTextW(data->Hwnd, (ushort*)pwszTitle); SetWindowTextW(data->Hwnd, (ushort*)pwszTitle);
} }
private void OnSetWindowAlpha(ImGuiViewportPtr viewport, float alpha) [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
private static void OnSetWindowAlpha(ImGuiViewportPtr viewport, float alpha)
{ {
var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData;
var style = GetWindowLongW(data->Hwnd, GWL.GWL_EXSTYLE); var style = GetWindowLongW(data->Hwnd, GWL.GWL_EXSTYLE);