常用的處理窗體的API函數如下(注意:API函數必須放在窗體中...):
使用C#語言,要引用DllImport,必須要添加using System.Runtime.InteropServices命名空間
(1)獲得當前前台窗體句柄
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] public static extern IntPtr GetForegroundWindow();
返回值類型是IntPtr,即為當前獲得焦點窗口的句柄
使用方法 :
IntPtr myPtr=GetForegroundWindow();
(2)枚舉所有屏幕上的頂層窗口,並將窗口句柄傳送給應用程序定義的回調函數,利用該法可以獲得所有當前打開的窗體的句柄信息
[DllImport("user32.dll")] public static extern bool EnumWindows(WNDENUMPROC lpEnumFunc,LPARAM lParam);
其中lpEnumFunc指向一個應用程序定義的回調函數指針;
lparam指向一個傳遞給回調函數的應用程序的定義值;
回調函數原型
bool CALLBACK EnumWindowsProc(HWND hwnd,LPARAM lparam);
其中hwnd是一個頂層窗口的句柄
lparam是一個應用程序定義的一個值(即EnumWindows中lParam)
下面用一個例子對該方法說明
程序中要實現一個功能:可以在當前打開的窗體中找到目標窗體,並在需要時將其激活,置為前台窗體
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices;//調用DLLIMPORT namespace EmuWindowInfor { /// <summary> /// 調用API的EnumWindows來枚舉窗口 /// </summary> class Program { //定義句柄的全局變量 public int HANDLE; //定義回調函數的委托 public delegate bool CALLBACK(int hwnd,int lparm); //用於獲取前台窗口句柄,設置當前窗口句柄 [DllImport("user32.dll")] public static extern int EnumWindows(CALLBACK x, int y); [DllImport("user32.dll", CharSet = CharSet.Auto)] static extern int GetWindowText(int hWnd, StringBuilder lpText, int nCount); static void Main(string[] args) { CALLBACK myCallBack = new CALLBACK(Report); EnumWindows(myCallBack, 0); Console.ReadKey(); } //實例化回調函數(可以在回調函數中根據窗體名稱找到目標窗體句柄) public static bool Report(int hwnd,int lparm) { //分配空間 var sb = new StringBuilder(50); GetWindowText(hwnd, sb, sb.Capacity); //注意某些窗口沒有標題 if (sb.ToString() != String.Empty) Console.WriteLine(sb.ToString()); //if (sb.ToString() == "Microsoft PowerPoint - [les_03_使用_rman [兼容模式]]") // Console.WriteLine(hwnd.ToString()); //回調函數有返回值 return true; } } }
以上代碼實現了通過窗體名稱找到目標窗體的句柄,再利用API函數SetForegroundWindow來將該窗體激活並前置
[DllImport("user32.dll")] public static extern bool SetForegroundWindow(int hWnd);
其中hWnd就是目標窗體的句柄
(3)根據窗體的類名和窗口的名稱獲得目標窗體
[DllImport("coredll.dll", EntryPoint = "FindWindow")] private extern static IntPtr FindWindow(string lpClassName, string lpWindowName);
如果coredll.dll,出現找不到,可以使用user32.dll代替
[DllImport("user32.dll", EntryPoint = "FindWindow")] private extern static IntPtr FindWindow(string lpClassName, string lpWindowName);
其中lpClassName是要找的窗口的類
lpWindowName是要找的窗口的標題,當然在搜索的時候不一定兩者都要知道,但至少要知道一個。根據窗口標題查找的一般多用在多窗口的應用程序中,因為程序中的窗體標題固定,方便搜索。但要是對於系統中的一些窗體,例如記事本窗體,PPT等,窗體的標題是不定的,所以用窗口類搜索更方便。當然有關常見的窗口類可以很方便找到,下面是一個搜索當前打開文本文檔的窗口句柄的代碼
IntPtr ParenthWnd = new IntPtr(0); ParenthWnd = FindWindow(null,"窗口標題"); //或者用ParenthWnd = FindWindow("窗口類名",null); //判斷這個窗體是否有效 if (ParenthWnd != IntPtr.Zero) { MessageBox.Show("找到窗口"); } else { MessageBox.Show("沒有找到窗口"); }
可以使用工具AccExplorer32.exe來查找窗口所屬的類和窗體的標題。
如果用VC開發平台,可以使用其中的Spy快速的找到窗口的類型,在Spy++中有一個FindWindow工具,它允許你使用鼠標選擇窗口,然後Spy++會顯示這個窗口的類。
同時在微軟的幫助文檔中也給出了對微軟常用OFFICE工具窗體句柄查找的方法,同樣是用FindWindow()方法,鏈接:http://support.microsoft.com/kb/302295/zh-cn
(4)查找子窗體的方法
[DllImport("user32.dll", EntryPoint = "FindWindow")] private static extern IntPtr FindWindowEx( IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow );
其中hwndParent是要查找子窗口的父窗口句柄,如果hwndParent為Null,則函數以桌面窗口為父窗口,查找桌面窗口的所有子窗口;如果hwndParent是HWND_MESSAGE,函數僅查找所有消息窗口;
hwndChildAfter :子窗口句柄。查找從在Z序中的下一個子窗口開始。子窗口必須為hwndPareRt窗口的直接子窗口而非後代窗口。如果HwndChildAfter為NULL,查找從hwndParent的第一個子窗口開始。如果hwndParent 和 hwndChildAfter同時為NULL,則函數查找所有的頂層窗口及消息窗口。
lpszClass:指向一個指定了類名的空結束字符串,或一個標識類名字符串的成員的指針。如果該參數為一個成員,則它必須為前次調用theGlobaIAddAtom函數產生的全局成員。該成員為16位,必須位於lpClassName的低16位,高位必須為0。
lpszWindow:指向一個指定了窗口名(窗口標題)的空結束字符串。如果該參數為 NULL,則為所有窗口全匹配。返回值:如果函數成功,返回值為具有指定類名和窗口名的窗口句柄。如果函數失敗,返回值為NULL。總之,這個函數查找子窗口,從排在給定的子窗口後面的下一個子窗口開始。在查找時不區分大小寫。
下面通過一個簡單的例子來說明對子窗口的查找。相信大家都有QQ號,那麼就寫一個簡單的外掛:通過查找QQ登陸窗口並模擬按鍵實現QQ的自動登陸,以下只是介紹其中如何通過父窗體查找子窗體
const int BM_CLICK = 0xF5; IntPtr maindHwnd = FindWindow(null, "QQ用戶登錄"); //獲得QQ登陸框的句柄 if (maindHwnd != IntPtr.Zero) { IntPtr childHwnd = FindWindowEx(maindHwnd, IntPtr.Zero, null, "登錄"); //獲得按鈕的句柄 if (childHwnd != IntPtr.Zero) { SendMessage(childHwnd, BM_CLICK, 0, 0); //發送點擊按鈕的消息 } else { MessageBox.Show("沒有找到子窗口"); } } else { MessageBox.Show("沒有找到窗口"); }
(5)找到窗體後對其的簡單處理,比如開關,隱藏
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] public static extern int ShowWindow(IntPtr hwnd, int nCmdShow);
其中ShowWindow(IntPtr hwnd, int nCmdShow);
nCmdShow的含義
0 隱藏窗口
1 正常大小顯示窗口
2 最小化窗口
3 最大化窗口
(6)獲取窗口大小及位置
[DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect); [StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left; //最左坐標 public int Top; //最上坐標 public int Right; //最右坐標 public int Bottom; //最下坐標 } 示例: InPtr awin = GetForegroundWindow(); //獲取當前窗口句柄 RECT rect = new RECT(); GetWindowRect(awin, ref rect); int width = rc.Right - rc.Left; //窗口的寬度 int height = rc.Bottom - rc.Top; //窗口的高度 int x = rc.Left; int y = rc.Top;
(7)常用操作:
GetClassName( hWnd: HWND; {指定窗口句柄} lpClassName: PChar; {緩沖區} nMaxCount: Integer {緩沖區大小} ): Integer; {返回類名大小; 失敗返回 0}獲取指定窗口的類名 GetNextWindow( hWnd: HWND; {指定的窗口句柄} uCmd: UINT {指定的關系選項} ): HWND; {失敗返回0; 成功返回符合的窗口句柄} //uCmd 可選值: GW_HWNDNEXT = 2; {同級別 Z 序之下} GW_HWNDPREV = 3; {同級別 Z 序之上}獲取指定窗口Z上或Z下的窗口的句柄 GetTopWindow( hWnd: HWND; {指定的窗口句柄} ): HWND; {失敗返回0; 成功返回最頂層的子窗口句柄}獲取指定窗口的子窗口中最頂層的窗口句柄 GetWindow( hWnd: HWND; {指定的窗口句柄} uCmd: UINT {指定的關系選項} ): HWND; {失敗返回0; 成功返回符合的窗口句柄} //uCmd 可選值: GW_HWNDFIRST = 0; {同級別第一個} GW_HWNDLAST = 1; {同級別最後一個} GW_HWNDNEXT = 2; {同級別下一個} GW_HWNDPREV = 3; {同級別上一個} GW_OWNER = 4; {屬主窗口} GW_CHILD = 5; {子窗口}獲取與指定窗口具有指定關系的窗口的句柄 GetWindowTextLength( hWnd: HWND {窗口句柄} ): Integer; {返回窗口標題長度} 獲取窗口標題長度 SetWindowText( hWnd: HWND; {窗口句柄} lpString: PChar {新標題串指針} ): BOOL;設置窗口標題 GetDesktopWindow: HWND; {無參數; 返回桌面窗口的句柄}
前面我們提到找到目標句柄後通過SetForeGroudWindow(int hwnd)方法可以將其激活並設置為前台窗口,但是如果只是想將其激活而不設置為前台的話就要用到函數SetActiveWindow()
但是使用該方法要特別注意,當在其他線程中對當前線程窗體進行激活使用該方法是沒有作用的。
想在找到目標窗體的前提下,讓目標窗體和其它窗體同時出現在桌面上,同時要保證只有目標窗體是處於激活狀態,也就是說只有目標窗體可以接受到模擬按鍵消息。終於找到了,可以通過SetForwardWindow(int handle)方法將目標窗體激活並處於最前窗體。同時利用另外一個API函數SetWindowPos來設置其他窗體,使其可以同處桌面。下面就簡單介紹下這個方法:
static extern bool SetWindowPos( HWND hWnd, //窗口句柄 HWND hWndInsertAfter, //排列順序的句柄 int X, //水平坐標 int Y, //垂直坐標 int cx, //寬 int cy, //高 UINT uFlags //窗口定位標識 );
其中
返回值:
BOOL,如果返回值非零表示成功,返回零表示失敗。錯誤信息請參看GetLastError函數。
參數表:
參數 類型及說明
hwnd HWND,欲定位的窗口句柄
hWndInsertAfter HWND,置於hwnd前面的窗口句柄。這個參數必須是窗口的句柄或是下面的值之一: HWND_BOTTOM 將窗口置於其它所有窗口的底部
HWND_NOTOPMOST 將窗口置於其它所有窗口的頂部,並位於任何最頂部窗口的後面。如果這個窗口非頂部窗口,這個標記對該窗口並不產生影響
HWND_TOP 將窗口置於它所有窗口的頂部
HWND_TOPMOST 將窗口置於其它所有窗口的頂部,並位於任何最頂部窗口的前面。即使這個窗口不是活動窗口,也維持最頂部狀態
x:
int,指定窗口新的X坐標
Y:
int,指定窗口新的Y坐標
cx:
int,指定窗口新的寬度
cy:
int,指定窗口新的高度
wFlags:
UINT,指定窗口狀態和位置的標記。這個參數使用下面值的組合: SWP_DRAWFRAME 圍繞窗口畫一個框
SWP_FRAMECHANGED 發送一條WM_NCCALCSIZE消息進入窗口,即使窗口的大小沒有發生改變。如果不指定這個參數,消息WM_NCCALCSIZE只有在窗口大小發生改變時才發送
SWP_HIDEWINDOW 隱藏窗口
SWP_NOACTIVATE 不激活窗口
SWP_NOCOPYBITS 屏蔽客戶區域
SWP_NOMOVE 保持當前位置(X和Y參數將被忽略)
SWP_NOOWNERZORDER 不改變所有窗口的位置和排列順序
SWP_NOREDRAW 窗口不自動重畫
SWP_NOREPOSITION 與SWP_NOOWNERZORDER標記相同
SWP_NOSENDCHANGING 防止這個窗口接受WM_WINDOWPOSCHANGING消息
SWP_NOSIZE 保持當前大小(cx和cy會被忽略)
SWP_NOZORDER 保持窗口在列表的當前位置(hWndInsertAfter將被忽略)
SWP_SHOWWINDOW 顯示窗口
備注:
如果設置了SWP_SHOWWINDOW或SWP_HIDEWINDOW標記,這個窗口不發生移動或改變大小。窗口成為最頂級窗口後,它的所有子窗口也會進入最頂級。一旦將其設為非最頂級,則它的所有子窗口也會轉為非最頂級。
程序中引用如下:
[DllImport("user32.dll", EntryPoint = "SetWindowPos")] static extern bool SetWindowPos( IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags ); static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2); static readonly IntPtr HWND_TOP = new IntPtr(0); const UInt32 SWP_NOSIZE = 0x0001; const UInt32 SWP_NOMOVE = 0x0002; const UInt32 SWP_NOZORDER = 0x0004; const UInt32 SWP_NOREDRAW = 0x0008; const UInt32 SWP_NOACTIVATE = 0x0010; const UInt32 SWP_FRAMECHANGED = 0x0020; const UInt32 SWP_SHOWWINDOW = 0x0040; const UInt32 SWP_HIDEWINDOW = 0x0080; const UInt32 SWP_NOCOPYBITS = 0x0100; const UInt32 SWP_NOOWNERZORDER = 0x0200; const UInt32 SWP_NOSENDCHANGING = 0x0400; const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE;