C# 強行鎖定 第三方 外部 應用程序窗體窗口的分辨率尺寸大小 禁止鼠標拖拽改變窗口大小

我們也許會有一些奇怪的需求,比如說禁止一個外部程序的窗口大小更改。

如果我們沒法修改外部程序的代碼,那要怎麼做呢?

當然,我們可以通過DLL注入目標程序的方式去Hook或registry一個事件來檢測,但這也太麻煩了吧。

如果想做非侵入式的,那就需要用到Windows下的系統函數去完成工作。

查來查去,最好用的是MoveWindow函數

1 MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint)

可以看到,這個函數要求傳入一個IntPtr類型的句柄、一組int類型的坐標、以及int類型的窗口的寬度和高度和最後的bool類型的是否刷新顯示。

句柄大家應該都知道是什麼,相當於是ID身份證一樣的存在,坐標就是指你把窗口移動到屏幕上的那個坐標。高寬不必說,這就是我們要改的。至於刷新顯示我們也無需過多理解,一個性能問題罷了。

 

首先我們要獲取坐標,先寫一個窗口枚舉器

    /*窗口句柄枚舉器*/
    public static class WindowsEnumerator
    {
        private delegate bool EnumWindowsProc(IntPtr windowHandle, IntPtr lParam);
        [DllImport("user32.dll", SetLastError = true)]
        private static extern bool EnumWindows(EnumWindowsProc callback, IntPtr lParam);
        [DllImport("user32.dll", SetLastError = true)]
        private static extern bool EnumChildWindows(IntPtr hWndStart, EnumWindowsProc callback, IntPtr lParam);
        [DllImport("user32.dll", SetLastError = true)]
        static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
        [DllImport("user32.dll", SetLastError = true)]
        static extern int GetWindowTextLength(IntPtr hWnd);
        private static List<IntPtr> handles = new List<IntPtr>();
        private static string targetName;
        public static List<IntPtr> GetWindowHandles(string target)
        {
            targetName = target;
            EnumWindows(EnumWindowsCallback, IntPtr.Zero);
            return handles;
        }
        private static bool EnumWindowsCallback(IntPtr HWND, IntPtr includeChildren)
        {
            StringBuilder name = new StringBuilder(GetWindowTextLength(HWND) + 1);
            GetWindowText(HWND, name, name.Capacity);
            if (name.ToString() == targetName)
                handles.Add(HWND);
            EnumChildWindows(HWND, EnumWindowsCallback, IntPtr.Zero);
            return true;
        }
    }

調用方法是

WindowsEnumerator.GetWindowHandles("窗口名字")

然後這個方法返回的是一個數組,我們需要用到foreach去遍歷裏面的東西

foreach (var item in WindowsEnumerator.GetWindowHandles("窗口名字"))

這個item的數據類型是IntPtr,也就是moveWindow函數所需的第一個參數hWnd——句柄

 

我們接着看第二組參數,需要傳入X和Y,也就是窗口移動到屏幕上的位置。

如果把這個寫死,你的窗口就無法移動了,只會固定在一個地方。

所以我們需要動態的去獲取窗口當前位置,然後把位置傳入給moveWindow方法所需的X和Y參數。

 

我們需要用到GetWindowRect函數,需要傳入hWnd和RECT

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);

        [StructLayout(LayoutKind.Sequential)]
        public struct RECT //定位當前窗口位置
        {
            public int x;                             //最左坐標
            public int y;                             //最上坐標

        }

然後我們需要在執行MoveWindow函數之前執行getWindowRect函數,放到上面寫的foreach里就好了。

記得new一個RECT結構體出來。

Rect rect = new Rect();
GetWindowRect(item, ref rect);

 

因為我們禁止窗口修改不是一次性的,需要循環去檢測,我們把檢測方法寫個死循環While(true)就好了,然後開闢新的線程去執行這個死循環。

因為C# 是有垃圾回收策略的,我們無需擔心性能開支過大所造成的的問題,畢竟就是寫着玩,學習學習(難道這有人有這個需求嗎不會把不會把?)

總結一下就是如下代碼:

RECT rect = new RECT();


 //禁止修改窗口
public void Pck(){
 while (true){
        foreach (var item in WindowsEnumerator.GetWindowHandles("Minecraft 1.12.2")){
             GetWindowRect(item, ref rect);//獲取窗口在屏幕上的坐標
             MoveWindow(item, rect.x, rect.y, 1024, 768, true); //鎖定為1024*768分辨率
          }
             Thread.Sleep(2000); //2秒檢測一次
   }
}

 

然後調用的時候直接:

 

Thread thread = new Thread(new ThreadStart(Pck));
 thread.Start();

 

 

隨筆到此結束