Programmer:)

[Unity] user32.dll을 이용한 윈도우 컨트롤 (feat.Title bar 제거) 본문

DEV/Unity

[Unity] user32.dll을 이용한 윈도우 컨트롤 (feat.Title bar 제거)

ryeggg 2021. 10. 19. 08:24
반응형

Unity에서 fullScreen mode 가 아닌 window mode 에서 윈도우 상단바(타이틀 바)를 제거하고 싶었다.

 

using System;
using System.Runtime.InteropServices; //user32.dll 임포트
using UnityEngine;

public class Window : MonoBehaviour
{
    #region DLLstuff
    const int SWP_HIDEWINDOW = 0x80;
    const int SWP_SHOWWINDOW = 0x40;
    const int SWP_NOMOVE = 0x0002;
    const int SWP_NOSIZE = 0x0001;
    const uint WS_SIZEBOX = 0x00040000;
    const int GWL_STYLE = -16;
    const int WS_MINIMIZE = 0x20000000;
    const int WS_MAXMIZE = 0x01000000;
    const int WS_BORDER = 0x00800000;
    const int WS_DLGFRAME = 0x00400000;
    const int WS_CAPTION = WS_BORDER | WS_DLGFRAME;
    const int WS_SYSMENU = 0x00080000;
    const int WS_MAXIMIZEBOX = 0x00010000;
    const int WS_MINIMIZEBOX = 0x00020000;

    [DllImport("user32.dll")]
    static extern System.IntPtr GetActiveWindow();

    [DllImport("user32.dll")]
    static extern int FindWindow(string lpClassName, string lpWindowName);

    /// <summary>
    /// window 크기와 위치변경
    /// </summary>
    /// <param name="hWnd">변경할 window handle</param>
    /// <param name="hWndInsertAfter"> Z순서상 변경할 handle 앞에 올 handle</param>
    /// HWND_BOTTOM : Z순서의 맨 아래에 위도우를 놓는다.
    /// HWND_NOTOPMOST : 맨 위에있는 모든 윈도우 뒤에 윈도우를 놓는다.
    /// HWND_TOP : Z순서상 맨 위에 윈도우를 놓는다.
    /// HWND_TOPMOST : 최상위 위치를 유지(비활성와 되어도)
    /// <param name="X"></param>
    /// <param name="Y"></param>
    /// <param name="cx">넓이</param>
    /// <param name="cy">높이</param>
    /// <param name="uFlags">플래그</param>
    /// SWP_SHOWWINDOW : 윈도우 표시(이동 크기변경 무시)
    /// SWP_HIDEWINDOW : 윈도우를 숨김(이동 크기변경 무시)
    /// SWP_DRAWFRAME : 윈도우 주변에 프레임을 그림
    /// SWP_NOACTIVATE : 크기 변경 후 윈도우를 활성화 시키지 않음
    /// SWP_NOMOVE : 현재위치 유지
    /// SWP_NOSIZE : 현재크기 유지
    /// SWP_NOZORDER : 현재 Z순서를 그대로 유지
    /// <returns></returns>
    [DllImport("user32.dll")]
    static extern bool SetWindowPos(
        System.IntPtr hWnd, //window handle
        System.IntPtr hWndInsertAfter, // window 배치 순서
        short X, // x position
        short Y, // y position
        short cx, // window width
        short cy, // window height
        uint uFlags // window flags.
    );

    /// <summary>
    /// 특정 윈도우 속성 변경 (32,64bit 모두 호환)
    /// </summary>
    /// <param name="hWnd">window handle</param>
    /// <param name="nIndex">변경할 속성 설정</param>
    /// GWL_EXSTYLE : 새로운 확장 윈도우 스타일  설정
    /// GWL_HINSTANCE : window를 생성한 응용프로그램의 인스턴스 핸들을 변경
    /// GWL_ID : window control ID 변경
    /// GWL_STYLE : 새로운 스타일 설정
    /// GWL_USERDATA : 연관된 32bit 값을 변경
    /// GWL_WNDPROC :  window WndProc(프로시저)의 주소를 변경
    /// <param name="dwNewLong"></param>
    /// WS_BORDER : 테두리있음
    /// WS_CAPTION : 제목 표시줄 있음
    /// WS_HSCROL : 가로 스크록 막대가 있음 
    /// WS_ICONIC / WS_MINIMIZE : 최소화로 시작
    /// WS_MAXIMIZE : 최대화로 시작
    /// WS_MAXIMIZEBOX : 최대화 버튼 있음
    /// WS_MINIMIZEBOX : 최소화 버튼 있음
    /// WS_SIZEBOX / WS_THICKFRAME : 크기조정 테두리가 있음
    /// WS_SYSMENU :  제목 표시줄에는 창 메뉴가 있음
    /// <returns></returns>
    [DllImport("user32.dll")] 
    static extern System.IntPtr SetWindowLong(
         System.IntPtr hWnd, // window handle
         int nIndex,
         uint dwNewLong
    );

    /// <summary>
    ///  윈도우 정보를 가져온다
    /// </summary>
    /// <param name="hWnd">window handle</param>
    /// <param name="nIndex">어느 값을 가져올지</param>
    /// GWL_EXSTYLE : 확장 윈도우 스타일을 가져옴
    /// GWL_HINSTANCE : 응용프로그램 인스턴스 핸들
    /// GWL_ID : windowID
    /// GWL_STYLE : 윈도우 스타일
    /// GWL_USERDATA : 윈도우와 관련된 사용자 정보
    /// GWL_WNDPROC : window Proc 
    /// <returns></returns>
    [DllImport("user32.dll")]
    static extern System.IntPtr GetWindowLong(
        System.IntPtr hWnd,
        int nIndex
    );

    System.IntPtr hWnd;
    System.IntPtr HWND_TOP = new System.IntPtr(0);
    System.IntPtr HWND_TOPMOST = new System.IntPtr(-1);
    System.IntPtr HWND_NOTOPMOST = new System.IntPtr(-2);

    #endregion

    [SerializeField] bool hideOnStart = false;

    public void ShowWindowBorders(bool value)
    {
        if (Application.isEditor) return;

        int style = GetWindowLong(hWnd, GWL_STYLE).ToInt32();

        if (value)
        {
            SetWindowLong(hWnd, GWL_STYLE, (uint)(style | WS_CAPTION | WS_SIZEBOX));
            SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
        }
        else //remove title bar
        {
            SetWindowLong(hWnd, GWL_STYLE, (uint)(style &~(WS_CAPTION | WS_SIZEBOX))); 
            SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
        }
        //버튼선택영역이 틀어졌을경우 해상도에 맞춰 호출
        //Screen.SetResolution(X, Y, false);
    }

    private void Awake()
    {
        hWnd = GetActiveWindow();
    }
    private void Start()
    {
        if (hideOnStart) ShowWindowBorders(false);
    }
    
}

위 코드가 간헐적인 titlebar 미제거 현상이 발생 

 

 

 

using System;
using UnityEngine;
using System.Runtime.InteropServices;

public class BorderlessWindow
{
    public static bool framed = true;

    [DllImport("user32.dll")]
    private static extern IntPtr GetActiveWindow();
    [DllImport("user32.dll")]
    private static extern int SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewLong);
    [DllImport("user32.dll")]
    private static extern bool ShowWindow(IntPtr hwnd, int nCmdShow);
    [DllImport("user32.dll")]
    private static extern bool MoveWindow(IntPtr hWnd, int x, int y, int nWidth, int nHeight, bool bRepaint);
    [DllImport("user32.dll")]
    private static extern bool GetWindowRect(IntPtr hwnd, out WinRect lpRect);

    private struct WinRect { public int left, top, right, bottom; }

    private const int GWL_STYLE = -16;

    private const int SW_MINIMIZE = 6;
    private const int SW_MAXIMIZE = 3;
    private const int SW_RESTORE = 9;

    private const uint WS_VISIBLE = 0x10000000;    
    private const uint WS_POPUP = 0x80000000;
    private const uint WS_BORDER = 0x00800000;
    private const uint WS_OVERLAPPED = 0x00000000;
    private const uint WS_CAPTION = 0x00C00000;
    private const uint WS_SYSMENU = 0x00080000;
    private const uint WS_THICKFRAME = 0x00040000; // WS_SIZEBOX
    private const uint WS_MINIMIZEBOX = 0x00020000;
    private const uint WS_MAXIMIZEBOX = 0x00010000;
    private const uint WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;


    // This attribute will make the method execute on game launch, after the Unity Logo Splash Screen.
    //[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    public static void InitializeOnLoad()
    {
#if !UNITY_EDITOR && UNITY_STANDALONE_WIN   // Dont do this while on Unity Editor!
        SetFramelessWindow();
#endif
    }

    public static void SetFramelessWindow()
    {
        var hwnd = GetActiveWindow();
        SetWindowLong(hwnd, GWL_STYLE, WS_POPUP | WS_VISIBLE);
        framed = false;
    }

    public static void SetFramedWindow()
    {
        var hwnd = GetActiveWindow();
        SetWindowLong(hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE);
        framed = true;
    }

    public static void MinimizeWindow()
    {
        var hwnd = GetActiveWindow();
        ShowWindow(hwnd, SW_MINIMIZE);
    }

    public static void MaximizeWindow()
    {
        var hwnd = GetActiveWindow();
        ShowWindow(hwnd, SW_MAXIMIZE);
    }

    public static void RestoreWindow()
    {
        var hwnd = GetActiveWindow();
        ShowWindow(hwnd, SW_RESTORE);
    }

    public static void MoveWindowPos(Vector2 posDelta, int newWidth, int newHeight)
    {
        var hwnd = GetActiveWindow();

        var windowRect = GetWindowRect(hwnd, out WinRect winRect);

        var x = winRect.left + (int)posDelta.x;
        var y = winRect.top - (int)posDelta.y;
        MoveWindow(hwnd, x, y, newWidth, newHeight, false);
    }
}
반응형
Comments