154 lines
7.3 KiB
C#
154 lines
7.3 KiB
C#
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
|
|
using System;
|
|
using System.Runtime.InteropServices;
|
|
|
|
/// <summary>
|
|
/// 데스크톱 오버레이에 필요한 Win32 선언 모음.
|
|
/// 선언 자체는 무해하며, 실제 호출부에서 에디터 여부를 가드한다.
|
|
/// </summary>
|
|
internal static class Win32
|
|
{
|
|
public const int GWL_STYLE = -16;
|
|
public const int GWL_EXSTYLE = -20;
|
|
|
|
public const uint WS_POPUP = 0x80000000;
|
|
public const uint WS_VISIBLE = 0x10000000;
|
|
|
|
public const uint WS_EX_TOOLWINDOW = 0x00000080; // 작업표시줄/Alt+Tab 에서 숨김
|
|
public const uint WS_EX_APPWINDOW = 0x00040000;
|
|
public const uint WS_EX_TRANSPARENT = 0x00000020; // 마우스 히트테스트 통과
|
|
public const uint WS_EX_NOACTIVATE = 0x08000000; // 클릭해도 포커스를 뺏지 않음
|
|
public const uint WS_EX_LAYERED = 0x00080000; // 최상위 창의 클릭 통과에 필요
|
|
|
|
public const uint LWA_COLORKEY = 0x00000001;
|
|
public const uint LWA_ALPHA = 0x00000002;
|
|
|
|
public static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
|
|
public const uint SWP_NOSIZE = 0x0001;
|
|
public const uint SWP_NOMOVE = 0x0002;
|
|
public const uint SWP_NOACTIVATE = 0x0010;
|
|
public const uint SWP_FRAMECHANGED = 0x0020;
|
|
public const uint SWP_SHOWWINDOW = 0x0040;
|
|
|
|
public const int SM_CXSCREEN = 0; // 주 모니터 너비
|
|
public const int SM_CYSCREEN = 1; // 주 모니터 높이
|
|
public const int SM_XVIRTUALSCREEN = 76;
|
|
public const int SM_YVIRTUALSCREEN = 77;
|
|
public const int SM_CXVIRTUALSCREEN = 78;
|
|
public const int SM_CYVIRTUALSCREEN = 79;
|
|
|
|
// DwmGetWindowAttribute 속성 번호
|
|
public const int DWMWA_EXTENDED_FRAME_BOUNDS = 9; // 보이는 실제 프레임(투명 여백 제외)
|
|
public const int DWMWA_CLOAKED = 14; // UWP 등 "숨겨졌지만 살아있는" 창
|
|
|
|
public const uint SPI_GETWORKAREA = 0x0030;
|
|
|
|
public const int SW_HIDE = 0;
|
|
public const int SW_SHOW = 5;
|
|
|
|
public const int VK_LBUTTON = 0x01;
|
|
public const int VK_RBUTTON = 0x02;
|
|
public const int VK_ESCAPE = 0x1B;
|
|
public const int VK_SHIFT = 0x10;
|
|
public const int VK_CONTROL = 0x11;
|
|
public const int VK_MENU = 0x12; // Alt
|
|
public const int VK_1 = 0x31;
|
|
public const int VK_2 = 0x32;
|
|
public const int VK_3 = 0x33;
|
|
public const int VK_N = 0x4E;
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct MARGINS
|
|
{
|
|
public int cxLeftWidth, cxRightWidth, cyTopHeight, cyBottomHeight;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct POINT
|
|
{
|
|
public int x, y;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct RECT
|
|
{
|
|
public int left, top, right, bottom;
|
|
public int Width => right - left;
|
|
public int Height => bottom - top;
|
|
}
|
|
|
|
[DllImport("user32.dll")] public static extern IntPtr GetActiveWindow();
|
|
[DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow(); // 지금 활성화된 창
|
|
[DllImport("user32.dll", CharSet = CharSet.Unicode)] public static extern IntPtr FindWindowW(string lpClassName, string lpWindowName);
|
|
[DllImport("user32.dll")] public static extern uint GetWindowLongW(IntPtr hWnd, int nIndex);
|
|
[DllImport("user32.dll")] public static extern uint SetWindowLongW(IntPtr hWnd, int nIndex, uint dwNewLong);
|
|
[DllImport("user32.dll")] 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 bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
|
[DllImport("user32.dll")] public static extern int GetSystemMetrics(int nIndex);
|
|
[DllImport("user32.dll")] public static extern bool GetCursorPos(out POINT lpPoint);
|
|
[DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
|
|
[DllImport("user32.dll")] public static extern short GetAsyncKeyState(int vKey);
|
|
|
|
// --- 창 열거 ---
|
|
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
|
|
|
|
[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")] public static extern bool IsIconic(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 bool SystemParametersInfoW(uint uiAction, uint uiParam, out RECT pvParam, uint fWinIni);
|
|
|
|
/// <summary>DWMWA_CLOAKED 등 int 값을 받는 속성용.</summary>
|
|
[DllImport("dwmapi.dll")] public static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out int pvAttribute, int cbAttribute);
|
|
|
|
/// <summary>DWMWA_EXTENDED_FRAME_BOUNDS 처럼 RECT 를 받는 속성용.</summary>
|
|
[DllImport("dwmapi.dll")] public static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out RECT pvAttribute, int cbAttribute);
|
|
[DllImport("user32.dll")] public static extern bool SetLayeredWindowAttributes(IntPtr hWnd, uint crKey, byte bAlpha, uint dwFlags);
|
|
[DllImport("dwmapi.dll")] public static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS pMarInset);
|
|
|
|
// --- 포커스 ---
|
|
[DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr hWnd);
|
|
[DllImport("user32.dll")] public static extern IntPtr SetFocus(IntPtr hWnd);
|
|
[DllImport("user32.dll")] public static extern bool IsWindow(IntPtr hWnd);
|
|
[DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
|
|
[DllImport("user32.dll")] public static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
|
|
[DllImport("kernel32.dll")] public static extern uint GetCurrentThreadId();
|
|
|
|
public static bool IsKeyDown(int vKey) => (GetAsyncKeyState(vKey) & 0x8000) != 0;
|
|
|
|
/// <summary>
|
|
/// 창을 확실히 활성화한다.
|
|
///
|
|
/// SetForegroundWindow 는 그냥 부르면 자주 무시된다. 윈도우는 포그라운드를
|
|
/// 가로채는 것을 막으려고 "마지막 입력을 받은 프로세스"만 허용하는데, 우리 창은
|
|
/// WS_EX_NOACTIVATE 라 그 판정에서 밀리는 경우가 있다. 현재 포그라운드 창의
|
|
/// 입력 큐에 잠깐 붙었다 떼면 같은 스레드로 취급돼 통과한다.
|
|
/// </summary>
|
|
public static void ForceForeground(IntPtr hwnd)
|
|
{
|
|
if (hwnd == IntPtr.Zero) return;
|
|
|
|
IntPtr foreground = GetForegroundWindow();
|
|
if (foreground == hwnd)
|
|
{
|
|
SetFocus(hwnd);
|
|
return;
|
|
}
|
|
|
|
uint currentThread = GetCurrentThreadId();
|
|
uint foregroundThread = foreground != IntPtr.Zero
|
|
? GetWindowThreadProcessId(foreground, out _)
|
|
: currentThread;
|
|
|
|
bool attached = foregroundThread != 0 && foregroundThread != currentThread &&
|
|
AttachThreadInput(currentThread, foregroundThread, true);
|
|
|
|
SetForegroundWindow(hwnd);
|
|
SetFocus(hwnd);
|
|
|
|
if (attached) AttachThreadInput(currentThread, foregroundThread, false);
|
|
}
|
|
}
|
|
#endif
|