Skip to content

Instantly share code, notes, and snippets.

@fangzhangmnm
Last active January 9, 2026 07:02
Show Gist options
  • Select an option

  • Save fangzhangmnm/f11c89e0922ff34bbb8f6912a6d9dbaa to your computer and use it in GitHub Desktop.

Select an option

Save fangzhangmnm/f11c89e0922ff34bbb8f6912a6d9dbaa to your computer and use it in GitHub Desktop.
Toggle fullscreen in Unity3D using F11 hotkey
/*********
This script enables F11 to toggle fullscreen mode in the Unity Editor.
Warning: Code is blind-generated by AI without human inspection, use at your own risk.
Author: chatgpt.com
Supervisor: fangzhangmnm, Jan.9 2026
License: MIT
*********/
#if UNITY_EDITOR
using System;
using System.Reflection;
using UnityEditor;
using UnityEditor.ShortcutManagement;
using UnityEngine;
#if UNITY_EDITOR_WIN
using System.Runtime.InteropServices;
#endif
/// <summary>
/// F11 fullscreen toggle:
/// - Edit Mode (Windows): toggles the main Unity Editor window borderless fullscreen.
/// - Play Mode: toggles a borderless fullscreen GameView popup.
///
/// Notes:
/// - In some Unity versions, shortcuts won't fire when the GameView popup is focused or right after entering Play Mode.
/// On Windows, a Win32 hotkey poller (GetAsyncKeyState) keeps F11 working.
/// - Some GameView settings (eg "Warn if No Cameras Rendering") are stored in Unity internals with version-specific names.
/// We mirror them by heuristic reflection.
/// </summary>
[InitializeOnLoad]
public static class EditorFullscreenF11
{
private const string GamePopupTitle = "Game (F11 Fullscreen)";
private static readonly Type GameViewType = Type.GetType("UnityEditor.GameView,UnityEditor");
private static readonly PropertyInfo ShowToolbarProp =
GameViewType?.GetProperty("showToolbar", BindingFlags.Instance | BindingFlags.NonPublic);
private static EditorWindow s_PrePopupFocused;
#if UNITY_EDITOR_WIN
private static bool s_PollF11; // Poll while popup exists
private static double s_SuppressPollUntil; // Debounce to avoid immediate re-toggles
#endif
static EditorFullscreenF11()
{
#if UNITY_EDITOR_WIN
EditorApplication.update -= PollF11;
EditorApplication.update += PollF11;
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
EditorApplication.quitting -= OnQuitting;
EditorApplication.quitting += OnQuitting;
#endif
// Close popup when leaving play mode (works on all platforms)
EditorApplication.playModeStateChanged -= ClosePopupOnPlayMode;
EditorApplication.playModeStateChanged += ClosePopupOnPlayMode;
}
// ============================================================
// F11 entry point (Unity Shortcut)
// ============================================================
[Shortcut("Custom/F11 Fullscreen Toggle", KeyCode.F11)]
private static void OnF11()
{
if (EditorApplication.isPlaying)
{
#if UNITY_EDITOR_WIN
// If the poller is active, suppress it for this same keypress.
SuppressPoll(0.25);
ConsumeF11Transition();
#endif
ToggleGamePopup();
return;
}
#if UNITY_EDITOR_WIN
ToggleMainEditorBorderless();
#else
ToggleFocusedDockedMaximizeFallback();
#endif
}
// ============================================================
// Play Mode fullscreen GameView popup
// ============================================================
private static void ToggleGamePopup()
{
if (CloseGamePopupWindows())
return;
OpenGamePopupWindow();
}
private static void OpenGamePopupWindow()
{
if (GameViewType == null)
{
Debug.LogError("UnityEditor.GameView type not found (Unity internals changed?).");
return;
}
s_PrePopupFocused = EditorWindow.focusedWindow;
var popup = (EditorWindow)ScriptableObject.CreateInstance(GameViewType);
popup.titleContent = new GUIContent(GamePopupTitle);
// Hide toolbar if Unity exposes it (internal; not guaranteed)
try { ShowToolbarProp?.SetValue(popup, false); } catch { /* ignore */ }
popup.ShowPopup();
// Initial best-effort fullscreen sizing
var res = Screen.currentResolution;
popup.position = new Rect(0, 0, res.width, res.height);
popup.Focus();
// Copy settings after internal init
EditorApplication.delayCall += () => ApplyPopupGameViewSettings(popup);
#if UNITY_EDITOR_WIN
// Enable Win32 polling so F11 works even when popup is focused.
s_PollF11 = true;
SuppressPoll(0.35);
ConsumeF11Transition();
// Taskbar-cover + topmost after native HWND exists.
EditorApplication.delayCall += () => EditorApplication.delayCall += ForcePopupTopmostAndCoverTaskbar;
#endif
}
private static void ClosePopupOnPlayMode(PlayModeStateChange state)
{
if (state == PlayModeStateChange.ExitingPlayMode ||
state == PlayModeStateChange.EnteredEditMode ||
state == PlayModeStateChange.ExitingEditMode)
{
CloseGamePopupWindows();
}
}
private static bool CloseGamePopupWindows()
{
if (GameViewType == null) return false;
bool closed = false;
var all = Resources.FindObjectsOfTypeAll(GameViewType);
foreach (var obj in all)
{
if (obj is not EditorWindow ew) continue;
if (ew.titleContent == null) continue;
if (ew.titleContent.text == GamePopupTitle)
{
ew.Close();
closed = true;
}
}
if (closed)
{
#if UNITY_EDITOR_WIN
s_PollF11 = false;
SuppressPoll(0.20);
ConsumeF11Transition();
#endif
AfterPopupClosed();
}
return closed;
}
private static void AfterPopupClosed()
{
// GameView can remain stretched until it receives focus. Mimic the click.
EditorApplication.delayCall += () =>
{
try
{
if (GameViewType != null)
EditorWindow.FocusWindowIfItsOpen(GameViewType);
}
catch { /* ignore */ }
EditorApplication.QueuePlayerLoopUpdate();
try { UnityEditorInternal.InternalEditorUtility.RepaintAllViews(); } catch { /* ignore */ }
// Optional: restore focus back to whatever window you were using.
try { s_PrePopupFocused?.Focus(); } catch { /* ignore */ }
};
}
#if UNITY_EDITOR_WIN
private static void ForcePopupTopmostAndCoverTaskbar()
{
var hwnd = Win32.FindWindowByExactOrContains(GamePopupTitle);
if (hwnd == IntPtr.Zero) return;
var bounds = Win32.GetMonitorRect(hwnd); // rcMonitor (covers taskbar)
Win32.MakeBorderless(hwnd);
Win32.SetTopmostFullscreen(hwnd, bounds);
}
#endif
// ============================================================
// Copy GameView settings (heuristic reflection)
// ============================================================
private static void ApplyPopupGameViewSettings(EditorWindow popup)
{
if (popup == null || GameViewType == null) return;
var source = FindPrimaryGameViewForSettings();
// Warn if No Cameras Rendering
bool warn = false;
if (source != null && TryGetNoCameraWarningHeuristic(source, out var fromSource))
warn = fromSource;
// Some Unity versions store this globally/static; apply to both.
ApplyNoCameraWarningHeuristic(popup, warn);
ApplyNoCameraWarningHeuristic(GameViewType, warn, includeStatic: true);
// Target display (best-effort)
if (source != null && TryGetIntByPredicate(source, out var display, mi => NameContains(mi, "display")))
TrySetIntByPredicate(popup, display, mi => NameContains(mi, "display"), includeStatic: false);
try { popup.Repaint(); } catch { /* ignore */ }
}
private static EditorWindow FindPrimaryGameViewForSettings()
{
if (GameViewType == null) return null;
var all = Resources.FindObjectsOfTypeAll(GameViewType);
EditorWindow firstNonPopup = null;
foreach (var obj in all)
{
if (obj is not EditorWindow ew) continue;
if (ew.titleContent == null) continue;
// Skip our popup(s)
if (ew.titleContent.text == GamePopupTitle)
continue;
// Prefer Unity's default title
if (ew.titleContent.text == "Game")
return ew;
firstNonPopup ??= ew;
}
return firstNonPopup;
}
private static bool TryGetNoCameraWarningHeuristic(object obj, out bool value)
=> TryGetBoolByPredicate(obj, out value, mi => NameContains(mi, "warn") && NameContains(mi, "camera"));
private static void ApplyNoCameraWarningHeuristic(object target, bool warn, bool includeStatic = false)
{
TrySetBoolByPredicate(target, warn, mi => NameContains(mi, "warn") && NameContains(mi, "camera"), includeStatic);
// Historic/common names (backup)
TrySetBoolMember(target, warn,
"warnIfNoCameraRendering",
"warnIfNoCamerasRendering",
"m_WarnIfNoCameraRendering",
"m_WarnIfNoCamerasRendering",
"showNoCameraWarning",
"m_ShowNoCameraWarning",
"m_ShowNoCamerasWarning");
}
// --- Reflection helpers (compact, heuristic) ---
private static bool NameContains(MemberInfo mi, string token)
=> mi != null && mi.Name.IndexOf(token, StringComparison.OrdinalIgnoreCase) >= 0;
private static int ScoreMemberName(string name)
{
if (string.IsNullOrEmpty(name)) return 0;
var n = name.ToLowerInvariant();
int s = 0;
if (n.Contains("nocamera")) s += 5;
if (n.Contains("nocameras")) s += 5;
if (n.Contains("render")) s += 2;
if (n.Contains("warn")) s += 2;
return s;
}
private static bool TryGetBoolByPredicate(object obj, out bool value, Func<MemberInfo, bool> predicate)
{
value = default;
if (obj == null) return false;
var t = obj as Type ?? obj.GetType();
const BindingFlags Inst = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
const BindingFlags Stat = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
bool found = false;
bool best = default;
int bestScore = int.MinValue;
foreach (var p in t.GetProperties(Inst | Stat))
{
if (p.PropertyType != typeof(bool) || !p.CanRead) continue;
if (!predicate(p)) continue;
try
{
bool v = (bool)p.GetValue(obj is Type ? null : obj);
int score = ScoreMemberName(p.Name);
if (score > bestScore) { bestScore = score; best = v; found = true; }
}
catch { }
}
foreach (var f in t.GetFields(Inst | Stat))
{
if (f.FieldType != typeof(bool)) continue;
if (!predicate(f)) continue;
try
{
bool v = (bool)f.GetValue(obj is Type ? null : obj);
int score = ScoreMemberName(f.Name);
if (score > bestScore) { bestScore = score; best = v; found = true; }
}
catch { }
}
if (!found) return false;
value = best;
return true;
}
private static void TrySetBoolByPredicate(object obj, bool value, Func<MemberInfo, bool> predicate, bool includeStatic)
{
if (obj == null) return;
var t = obj as Type ?? obj.GetType();
const BindingFlags Inst = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
const BindingFlags Stat = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
var flags = Inst | (includeStatic ? Stat : 0);
foreach (var p in t.GetProperties(flags))
{
if (p.PropertyType != typeof(bool) || !p.CanWrite) continue;
if (!predicate(p)) continue;
try { p.SetValue(obj is Type ? null : obj, value); } catch { }
}
foreach (var f in t.GetFields(flags))
{
if (f.FieldType != typeof(bool) || f.IsInitOnly) continue;
if (!predicate(f)) continue;
try { f.SetValue(obj is Type ? null : obj, value); } catch { }
}
}
private static bool TryGetIntByPredicate(object obj, out int value, Func<MemberInfo, bool> predicate)
{
value = default;
if (obj == null) return false;
var t = obj as Type ?? obj.GetType();
const BindingFlags Inst = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
const BindingFlags Stat = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
foreach (var p in t.GetProperties(Inst | Stat))
{
if (p.PropertyType != typeof(int) || !p.CanRead) continue;
if (!predicate(p)) continue;
try { value = (int)p.GetValue(obj is Type ? null : obj); return true; } catch { }
}
foreach (var f in t.GetFields(Inst | Stat))
{
if (f.FieldType != typeof(int)) continue;
if (!predicate(f)) continue;
try { value = (int)f.GetValue(obj is Type ? null : obj); return true; } catch { }
}
return false;
}
private static void TrySetIntByPredicate(object obj, int value, Func<MemberInfo, bool> predicate, bool includeStatic)
{
if (obj == null) return;
var t = obj as Type ?? obj.GetType();
const BindingFlags Inst = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
const BindingFlags Stat = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
var flags = Inst | (includeStatic ? Stat : 0);
foreach (var p in t.GetProperties(flags))
{
if (p.PropertyType != typeof(int) || !p.CanWrite) continue;
if (!predicate(p)) continue;
try { p.SetValue(obj is Type ? null : obj, value); } catch { }
}
foreach (var f in t.GetFields(flags))
{
if (f.FieldType != typeof(int) || f.IsInitOnly) continue;
if (!predicate(f)) continue;
try { f.SetValue(obj is Type ? null : obj, value); } catch { }
}
}
private static void TrySetBoolMember(object obj, bool value, params string[] names)
{
if (obj == null) return;
var t = obj as Type ?? obj.GetType();
const BindingFlags F = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
foreach (var n in names)
{
var p = t.GetProperty(n, F);
if (p != null && p.PropertyType == typeof(bool) && p.CanWrite)
{
try { p.SetValue(obj is Type ? null : obj, value); return; } catch { }
}
var f = t.GetField(n, F);
if (f != null && f.FieldType == typeof(bool) && !f.IsInitOnly)
{
try { f.SetValue(obj is Type ? null : obj, value); return; } catch { }
}
}
}
// ============================================================
// Non-Windows fallback (docked maximize)
// ============================================================
private static void ToggleFocusedDockedMaximizeFallback()
{
var w = EditorWindow.focusedWindow;
if (w == null) return;
w.maximized = !w.maximized;
w.Focus();
}
#if UNITY_EDITOR_WIN
// ============================================================
// Windows: F11 poller for Play Mode and popup focus quirks
// ============================================================
private const int VK_F11 = 0x7A;
private static void PollF11()
{
// Poll in two cases:
// - While popup exists (shortcuts often stop firing when popup is focused)
// - While in Play Mode (sometimes you need to click editor before shortcuts work)
if (!EditorApplication.isPlaying && !s_PollF11)
return;
// In Play Mode, allow even if Unity isn't foreground yet (matches controller-input observation).
// Out of play mode, require Unity foreground to avoid toggling while typing elsewhere.
if (!EditorApplication.isPlaying && !Win32.IsUnityForegroundProcess())
return;
// If not playing, polling only makes sense while the popup exists.
if (!EditorApplication.isPlaying && !Win32.WindowExists(GamePopupTitle))
{
s_PollF11 = false;
return;
}
if (EditorApplication.timeSinceStartup < s_SuppressPollUntil)
return;
if ((Win32.GetAsyncKeyState(VK_F11) & 1) != 0)
{
SuppressPoll(0.20);
ToggleGamePopup();
}
}
private static void SuppressPoll(double seconds)
=> s_SuppressPollUntil = Math.Max(s_SuppressPollUntil, EditorApplication.timeSinceStartup + seconds);
private static void ConsumeF11Transition()
=> _ = Win32.GetAsyncKeyState(VK_F11);
// ============================================================
// Windows: Borderless fullscreen main Unity Editor window
// ============================================================
private const string K_IsBorderless = "F11Borderless.IsBorderless";
private const string K_WasBorderlessBeforePlay = "F11Borderless.WasBeforePlay";
private const string K_OrigStyle = "F11Borderless.OrigStyle";
private const string K_OrigExStyle = "F11Borderless.OrigExStyle";
private const string K_OrigRectL = "F11Borderless.OrigRectL";
private const string K_OrigRectT = "F11Borderless.OrigRectT";
private const string K_OrigRectR = "F11Borderless.OrigRectR";
private const string K_OrigRectB = "F11Borderless.OrigRectB";
private static void ToggleMainEditorBorderless()
{
if (!Win32.TryGetUnityMainWindow(out var hwnd))
{
Debug.LogWarning("Could not find Unity main window handle. Make sure the Unity Editor is the foreground window.");
return;
}
if (!SessionState.GetBool(K_IsBorderless, false))
EnterBorderless(hwnd);
else
ExitBorderless(hwnd);
}
private static void OnPlayModeStateChanged(PlayModeStateChange state)
{
// Before entering play mode: restore normal window to avoid getting stuck after domain reload.
if (state == PlayModeStateChange.ExitingEditMode)
{
if (SessionState.GetBool(K_IsBorderless, false) && Win32.TryGetUnityMainWindow(out var hwnd))
{
SessionState.SetBool(K_WasBorderlessBeforePlay, true);
ExitBorderless(hwnd);
}
else
{
SessionState.SetBool(K_WasBorderlessBeforePlay, false);
}
}
// After returning to edit mode: re-apply borderless if it was enabled before.
if (state == PlayModeStateChange.EnteredEditMode)
{
if (SessionState.GetBool(K_WasBorderlessBeforePlay, false) && Win32.TryGetUnityMainWindow(out var hwnd))
{
EnterBorderless(hwnd, preserveOriginals: true);
SessionState.SetBool(K_WasBorderlessBeforePlay, false);
}
}
}
private static void OnQuitting()
{
if (SessionState.GetBool(K_IsBorderless, false) && Win32.TryGetUnityMainWindow(out var hwnd))
ExitBorderless(hwnd);
}
private static bool HasStoredOriginals() => SessionState.GetInt(K_OrigStyle, 0) != 0;
private static void EnterBorderless(IntPtr hwnd, bool preserveOriginals = false)
{
if (!preserveOriginals || !HasStoredOriginals())
{
SessionState.SetInt(K_OrigStyle, Win32.GetWindowLongSafe(hwnd, Win32.GWL_STYLE));
SessionState.SetInt(K_OrigExStyle, Win32.GetWindowLongSafe(hwnd, Win32.GWL_EXSTYLE));
Win32.GetWindowRect(hwnd, out var r);
SessionState.SetInt(K_OrigRectL, r.left);
SessionState.SetInt(K_OrigRectT, r.top);
SessionState.SetInt(K_OrigRectR, r.right);
SessionState.SetInt(K_OrigRectB, r.bottom);
}
int originalStyle = SessionState.GetInt(K_OrigStyle, Win32.GetWindowLongSafe(hwnd, Win32.GWL_STYLE));
Win32.SetWindowLongSafe(hwnd, Win32.GWL_STYLE, Win32.BorderlessStyleFrom(originalStyle));
Win32.SetFullscreenRect(hwnd, Win32.GetMonitorRect(hwnd));
SessionState.SetBool(K_IsBorderless, true);
}
private static void ExitBorderless(IntPtr hwnd)
{
if (!HasStoredOriginals())
{
Win32.SetWindowLongSafe(hwnd, Win32.GWL_STYLE, Win32.RestoreableStyleFrom(Win32.GetWindowLongSafe(hwnd, Win32.GWL_STYLE)));
SessionState.SetBool(K_IsBorderless, false);
return;
}
Win32.SetWindowLongSafe(hwnd, Win32.GWL_STYLE, SessionState.GetInt(K_OrigStyle, Win32.GetWindowLongSafe(hwnd, Win32.GWL_STYLE)));
Win32.SetWindowLongSafe(hwnd, Win32.GWL_EXSTYLE, SessionState.GetInt(K_OrigExStyle, Win32.GetWindowLongSafe(hwnd, Win32.GWL_EXSTYLE)));
var r = new Win32.RECT
{
left = SessionState.GetInt(K_OrigRectL, 0),
top = SessionState.GetInt(K_OrigRectT, 0),
right = SessionState.GetInt(K_OrigRectR, 0),
bottom = SessionState.GetInt(K_OrigRectB, 0),
};
Win32.SetFullscreenRect(hwnd, r);
SessionState.SetBool(K_IsBorderless, false);
}
// ============================================================
// Win32 helpers (contained)
// ============================================================
private static class Win32
{
// Window styles
public const int GWL_STYLE = -16;
public const int GWL_EXSTYLE = -20;
private const int WS_CAPTION = 0x00C00000;
private const int WS_THICKFRAME = 0x00040000;
private const int WS_MINIMIZE = 0x20000000;
private const int WS_MAXIMIZEBOX = 0x00010000;
private const int WS_SYSMENU = 0x00080000;
private const uint SWP_NOSENDCHANGING = 0x0400;
private const uint SWP_FRAMECHANGED = 0x0020;
private const uint SWP_NOZORDER = 0x0004;
private const uint SWP_NOACTIVATE = 0x0010;
private const uint SWP_NOMOVE = 0x0002;
private const uint SWP_NOSIZE = 0x0001;
private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
private const uint MONITOR_DEFAULTTONEAREST = 2;
public static bool IsUnityForegroundProcess()
{
IntPtr hwnd = GetForegroundWindow();
if (hwnd == IntPtr.Zero) return false;
GetWindowThreadProcessId(hwnd, out uint pid);
return pid == (uint)System.Diagnostics.Process.GetCurrentProcess().Id;
}
public static bool TryGetUnityMainWindow(out IntPtr hwnd)
{
hwnd = GetForegroundWindow();
if (hwnd == IntPtr.Zero) return false;
GetWindowThreadProcessId(hwnd, out uint pid);
if (pid != (uint)System.Diagnostics.Process.GetCurrentProcess().Id)
return false;
// Ensure it's a top-level window
if (GetParent(hwnd) != IntPtr.Zero)
return false;
return true;
}
public static bool WindowExists(string exactTitle)
=> FindWindowW(null, exactTitle) != IntPtr.Zero || FindTopLevelWindowContains(exactTitle) != IntPtr.Zero;
public static IntPtr FindWindowByExactOrContains(string title)
{
var hwnd = FindWindowW(null, title);
return hwnd != IntPtr.Zero ? hwnd : FindTopLevelWindowContains(title);
}
public static RECT GetMonitorRect(IntPtr hwnd)
{
IntPtr hMon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
var mi = new MONITORINFO { cbSize = Marshal.SizeOf<MONITORINFO>() };
GetMonitorInfo(hMon, ref mi);
return mi.rcMonitor; // covers taskbar
}
public static void MakeBorderless(IntPtr hwnd)
{
int style = GetWindowLongSafe(hwnd, GWL_STYLE);
SetWindowLongSafe(hwnd, GWL_STYLE, BorderlessStyleFrom(style));
}
public static void SetTopmostFullscreen(IntPtr hwnd, RECT bounds)
{
SetWindowPos(
hwnd,
HWND_TOPMOST,
bounds.left,
bounds.top,
bounds.right - bounds.left,
bounds.bottom - bounds.top,
SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_NOSENDCHANGING
);
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOSENDCHANGING);
}
public static void SetFullscreenRect(IntPtr hwnd, RECT r)
{
SetWindowPos(
hwnd,
IntPtr.Zero,
r.left,
r.top,
r.right - r.left,
r.bottom - r.top,
SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_NOSENDCHANGING
);
}
public static int BorderlessStyleFrom(int style)
{
int s = style;
s &= ~WS_CAPTION;
s &= ~WS_THICKFRAME;
s &= ~WS_SYSMENU;
s &= ~WS_MINIMIZE;
s &= ~WS_MAXIMIZEBOX;
return s;
}
public static int RestoreableStyleFrom(int style)
=> style | (WS_CAPTION | WS_THICKFRAME | WS_SYSMENU | WS_MINIMIZE | WS_MAXIMIZEBOX);
public static int GetWindowLongSafe(IntPtr hWnd, int nIndex)
{
IntPtr result = IntPtr.Size == 8 ? GetWindowLongPtr(hWnd, nIndex) : new IntPtr(GetWindowLong(hWnd, nIndex));
return result.ToInt32();
}
public static void SetWindowLongSafe(IntPtr hWnd, int nIndex, int dwNewLong)
{
if (IntPtr.Size == 8) SetWindowLongPtr(hWnd, nIndex, new IntPtr(dwNewLong));
else SetWindowLong(hWnd, nIndex, dwNewLong);
}
private static IntPtr FindTopLevelWindowContains(string contains)
{
IntPtr found = IntPtr.Zero;
EnumWindows((h, _) =>
{
if (!IsWindowVisible(h)) return true;
int len = GetWindowTextLengthW(h);
if (len <= 0) return true;
var sb = new System.Text.StringBuilder(len + 1);
GetWindowTextW(h, sb, sb.Capacity);
var title = sb.ToString();
if (!string.IsNullOrEmpty(title) && title.Contains(contains))
{
found = h;
return false;
}
return true;
}, IntPtr.Zero);
return found;
}
// --- Win32 P/Invoke ---
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")] public static extern IntPtr GetParent(IntPtr hWnd);
[DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
public static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi);
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr FindWindowW(string lpClassName, string lpWindowName);
[DllImport("user32.dll")] public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int GetWindowTextLengthW(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int GetWindowTextW(IntPtr hWnd, System.Text.StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll")]
public static extern short GetAsyncKeyState(int vKey);
[DllImport("user32.dll", EntryPoint = "GetWindowLong")]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "SetWindowLong")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll", EntryPoint = "GetWindowLongPtr")]
private static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
private static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
[StructLayout(LayoutKind.Sequential)]
public struct RECT { public int left, top, right, bottom; }
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct MONITORINFO
{
public int cbSize;
public RECT rcMonitor;
public RECT rcWork;
public uint dwFlags;
}
}
#endif
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment