文章出处:
https://www.jianshu.com/p/c81342e96def
using System;
using System.Runtime.InteropServices;
using System.Text;
using UnityEngine;
public static class PInvoke
{
static IntPtr ptr;
public static IntPtr UnityHWnd
{
get
{
if (ptr == null || ptr == IntPtr.Zero)
{
ptr = GetUnityWindow();
}
return ptr;
}
}
#region 常量
//https://docs.microsoft.com/zh-cn/windows/win32/winmsg/window-styles
public const ulong WS_MAXIMIZEBOX = 0x00010000L; //最大化的按钮禁用
public const ulong WS_DLGFRAME = 0x00400000L; //不现实边框
public const ulong WS_SIZEBOX = 0x00040000L; //调大小的边框
public const ulong WS_BORDER = 0x00800000L; //边框
public const ulong WS_CAPTION = 0x00C00000L; //标题栏
// Retreives pointer to WindowProc function.
public const int GWLP_WNDPROC = -4; //Windows 绘制方法的指针
public const int WM_SIZING = 0x214;
public const int WS_POPUP = 0x800000;
public const int GWL_STYLE = -16;
//边框参数
public const uint SWP_SHOWWINDOW = 0x0040;
public const uint SWP_NOMOVE = 0x0002;
public const int SW_SHOWMINIMIZED = 2;//(最小化窗口)
// Name of the Unity window class used to find the window handle.
public const string UNITY_WND_CLASSNAME = "UnityWndClass";
#endregion
#region Win32 API
// Passes message information to the specified window procedure.
[DllImport("user32.dll")]
public static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
//获得窗口样式
[DllImport("user32.dll", EntryPoint = "GetWindowLongPtr")]
public static extern IntPtr GetWindowLongPtr(IntPtr hwnd, int nIndex);
// Retrieves the dimensions of the bounding rectangle of the specified window.
// The dimensions are given in screen coordinates that are relative to the upper-left corner of the screen.
[DllImport("user32.dll", SetLastError = true)]
public static extern bool GetWindowRect(IntPtr hwnd, ref RECT lpRect);
// Retrieves the coordinates of a window's client area. The client coordinates specify the upper-left
// and lower-right corners of the client area. Because client coordinates are relative to the upper-left
// corner of a window's client area, the coordinates of the upper-left corner are (0,0).
[DllImport("user32.dll")]
public static extern bool GetClientRect(IntPtr hWnd, ref RECT lpRect);
// 改变指定窗口的属性 ,该函数还在额外窗口内存中的指定偏移处设置一个值。
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", CharSet = CharSet.Auto)]
public static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
//设置当前窗口的显示状态
[DllImport("user32.dll")]
public static extern bool ShowWindow(System.IntPtr hwnd, int nCmdShow);
//设置窗口位置,大小
[DllImport("user32.dll")]
public static extern bool SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
// 通过将每个窗口的句柄依次传递给应用程序定义的回调函数,枚举与线程关联的所有非子窗口。
[DllImport("user32.dll")]
private static extern bool EnumThreadWindows(uint dwThreadId, EnumWindowsProc lpEnumFunc, IntPtr lParam);
private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
//检索调用线程的线程标识符。
[DllImport("kernel32.dll")]
private static extern uint GetCurrentThreadId();
// 检索指定窗口所属的类的名称。
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int GetClassName(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
//窗口拖动
[DllImport("user32.dll")]
public static extern bool ReleaseCapture();
[DllImport("user32.dll")]
public static extern bool SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);
#endregion
#region Static Function
//最小化窗口
//具体窗口参数看这 https://msdn.microsoft.com/en-us/library/windows/desktop/ms633548(v=vs.85).aspx
public static void SetMinWindows()
{
if (!Application.isEditor)
{
ShowWindow(UnityHWnd, SW_SHOWMINIMIZED);
}
}
//拖动窗口
public static void DragWindow()
{
ReleaseCapture();
SendMessage(UnityHWnd, 0xA1, 0x02, 0);
SendMessage(UnityHWnd, 0x0202, 0, 0);
}
public static IntPtr GetUnityWindow()
{
var unityHWnd = IntPtr.Zero;
EnumThreadWindows(GetCurrentThreadId(), (hWnd, lParam) =>
{
var classText = new StringBuilder(UNITY_WND_CLASSNAME.Length + 1);
GetClassName(hWnd, classText, classText.Capacity);
if (classText.ToString() == UNITY_WND_CLASSNAME)
{
unityHWnd = hWnd;
return false;
}
return true;
}, IntPtr.Zero);
return unityHWnd;
}
#endregion
#region Assistant
///
/// WinAPI RECT definition.
///
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public override string ToString()
{
return "left = {Left}\nright = {Right}\ntop = {Top}\nbottom = {Bottom}";
}
}
#endregion
}
实现:
无边框
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSplashScreen)]
static void InitAppWindow()
{
if (Application.isEditor) return;
var dwStyles = GetWindowLongPtr(UnityHWnd, GWL_STYLE);
var sty = ((ulong)dwStyles);
sty &= ~(WS_CAPTION| WS_DLGFRAME)&WS_POPUP;
SetWindowLongPtr(UnityHWnd, GWL_STYLE, (IntPtr)sty);
}
注意:设置无边框后,任务栏点击无法实现 App 最小化,如果有解决方案欢迎提 PR
最小化
//最小化窗口
//具体窗口参数看这 https://msdn.microsoft.com/en-us/library/windows/desktop/ms633548(v=vs.85).aspx
public static void SetMinWindows()
{
if (!Application.isEditor)
{
ShowWindow(UnityHWnd, SW_SHOWMINIMIZED);
}
}
//设置当前窗口的显示状态
[DllImport(“user32.dll”)]
public static extern bool ShowWindow(System.IntPtr hwnd, int nCmdShow);
Tips: 代码中用到的 win32 api 以及常量请参阅本文配套 GitHub 仓库:点我。
using UnityEngine;
using UnityEngine.EventSystems;
using static PInvoke;
public class WindowResizeHandler : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IBeginDragHandler, IEndDragHandler, IDragHandler
{
bool isDragging = false;
bool isInsideOfHandler = false;
public Vector2 hotspot = Vector2.zero;
// Minimum and maximum values for window width/height in pixel.
[SerializeField]
private int minWidthPixel = 768;
[SerializeField]
private int maxWidthPixel = 2048;
public Texture2D wnes;
private float aspect = 16 / 9f;
void IBeginDragHandler.OnBeginDrag(PointerEventData eventData) => isDragging = eventData.pointerId==-1;
void IDragHandler.OnDrag(PointerEventData eventData) => WindowProcess(eventData);
void IEndDragHandler.OnEndDrag(PointerEventData eventData)
{
isDragging = false;
if (!isInsideOfHandler)
{
Cursor.SetCursor(default, default, CursorMode.Auto);
}
}
void IPointerEnterHandler.OnPointerEnter(PointerEventData eventData)
{
isInsideOfHandler = true;
Cursor.SetCursor(wnes, hotspot, CursorMode.Auto);
}
void IPointerExitHandler.OnPointerExit(PointerEventData eventData)
{
isInsideOfHandler = false;
if (!isDragging)
{
Cursor.SetCursor(default, default, CursorMode.Auto);
}
}
private void WindowProcess(PointerEventData eventData)
{
if (Application.isEditor || eventData.pointerId != -1) return;
RECT rc = default;
GetWindowRect(UnityHWnd, ref rc);
int newWidth = Mathf.Clamp(rc.Right - rc.Left + Mathf.RoundToInt(eventData.delta.x), minWidthPixel, maxWidthPixel);
int newHeight = Mathf.RoundToInt(newWidth / aspect);
SetWindowPos(UnityHWnd, 0, rc.Left, rc.Top, newWidth, newHeight, SWP_SHOWWINDOW);
}
}
Tips: 代码中用到的 win32 api 以及常量请参阅本文配套 GitHub 仓库:点我。
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using static PInvoke;
[RequireComponent(typeof(Graphic))]
public class WindowMoveHandler : MonoBehaviour,IPointerDownHandler,IPointerUpHandler,IPointerExitHandler
{
static bool isDrag = false;
void IPointerDownHandler.OnPointerDown(PointerEventData eventData) => isDrag = eventData.pointerId==-1;
void IPointerExitHandler.OnPointerExit(PointerEventData eventData) => isDrag = false;
void IPointerUpHandler.OnPointerUp(PointerEventData eventData) => isDrag = !(eventData.pointerId==-1);
private void Update()
{
if (!Application.isEditor&&isDrag)
{
DragWindow();
}
}
}
//拖动窗口
public static void DragWindow()
{
ReleaseCapture();
SendMessage(UnityHWnd, 0xA1, 0x02, 0);
SendMessage(UnityHWnd, 0x0202, 0, 0);
}
置顶窗体(Topmost)
// 应用窗口置顶
public static void SetTopmost(bool isTopmost)
{
if (!Application.isEditor)
{
int ptr = isTopmost ? -1 : -2;
SetWindowPos(UnityHWnd, ptr, 0, 0, 0, 0, 1 | 2 | 64);//0x0040
}
else
{
Debug.LogWarning($“{nameof(PInvoke)}: 为避免编辑器行为异常,请打包 exe 后测试!”);
}
}
Tips: 代码中用到的 win32 api 以及常量请参阅本文配套 GitHub 仓库:点我。
扩展阅读:
System-Tray-Icon-For-Unity - 为 Unity 开发的 App 提供 System Tray Icon,后面计划使用 Unity MenuItem 的方式,现在使用起来感觉不怎么便利。
UnitySkipSplash - 几句话跳过 Unity Logo 闪屏界面,别问我为何这么 big 胆,仅供学习用途嘛。
Simple-Customize-ERP-System - 本文配套的 GitHub 仓库。
写到最后:
版权所有,转载请注明出处!
作者:雨落随风
链接:https://www.jianshu.com/p/c81342e96def
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。