C# 获得窗体句柄并发送消息(利用windows API可在不同进程中获取)
?C#使用Windows API獲取窗口句柄控制其他程序窗口
編寫程序模擬鼠標和鍵盤操作可以方便的實現(xiàn)你需要的功能,而不需要對方程序為你開放接口。比如,操作飛信定時發(fā)送短信等。我之前開發(fā)過飛信耗子,用的是對飛信協(xié)議進行抓包,然后分析協(xié)議,進而模擬協(xié)議的執(zhí)行,開發(fā)出了客戶端,與移動服務器進行通信,但是這有一些缺點。如果移動的服務器對接口進行變更,我所編寫的客戶端也要進行相應的升級。如果服務器的協(xié)議進行了更改,甚至個人編寫的這種第三方客戶端需要重寫。而我個人也沒有這個時間和精力,或者說沒有足夠的利益支撐我繼續(xù)去重構(gòu)飛信耗子。因此,這款還算優(yōu)秀的軟件,現(xiàn)在就束之高閣了,我自己也覺得遺憾。上周,某項目驗收,需要修改界面,但是零時找不到源碼了。我在兩三個小時內(nèi)要解決這個問題,時間緊迫。我突然想起室友以前做過模擬鼠標鍵盤去發(fā)送飛信消息的小程序。于是我趕緊電話咨詢了一下。然后掌握了這個技巧,按時解決了問題。我覺得這個技巧還是很有用的,現(xiàn)總結(jié)如下:
首先,引入如下三個API接口:
?第一個與第三個是用于查找窗口句柄的,凡運行于Windows上的窗口,都具有句柄。窗口上的文本框,按鈕之類的,也有其句柄(可看作子窗口句柄)。這些句柄的類型可以通過
Spy++進行查詢。比如C語言編寫的程序中,文本框的句柄類型一般為“EDIT”,C#寫的程序則不是,可以具體去查。第二個接口則是用于向窗口發(fā)送各種消息,比如向文本框發(fā)送
字符串,或者向按鈕發(fā)送按下與彈起的消息等。詳細解釋如下:
?這是用于查找操作系統(tǒng)中打開的窗口中標題名為無標題 - 記事本的窗口。第一個參數(shù)是此窗口的類型。這兩個參數(shù)知道一
個即可,另一個可以填null。但是如果是用窗口類型查找,則可能只能得到其中的一個窗口。因此通過標題進行查找是非常方便的。
?這個函數(shù)用于獲得窗口中子窗口的句柄,子窗口指的其實就是窗口中的各種控件。第一個參數(shù)是父窗口的句柄,第二個參數(shù)指示獲得的是同一類型中的第幾個子窗口。填
IntPtr.Zero則表示獲得第一個子窗口。第三個參數(shù)表示你需要找的子窗口的類型,第四個參數(shù)一般為null。如果一個窗口中有兩個文本框,那么可以用如下操作獲得第二個文本框
的句柄。
?這里只是先將第二個參數(shù)填為IntPtr.Zero,獲取第一個EDIT類型的文本框,然后第二次調(diào)用時,再將第二參數(shù)填為第一個文本框的句柄,那么執(zhí)行返回的就是下一個文本框的句柄
了。因此htextbox2得到的就是第二文本框的句柄。
在可以自由獲得各種窗口及其上控件的句柄后,我們就可以向其發(fā)送各種消息進行鼠標和鍵盤的模擬了。比如:
這句是為文本框填寫相應的字符串name。
?這三句是獲得了窗口的一個button,然后發(fā)送按下,彈起消息給它,模擬了點擊鼠標的動作。
SendMessage函數(shù)的第一個參數(shù)是窗口句柄,或者窗口中控件的句柄,第二個參數(shù)是消息的類型Flag,這些值是在API的一些頭文件中定義好的。你要是在C#中用,就自己去定義他們,比如
還有其他的類型Flag,可以參考上一篇Blog查詢,也可以去查MSDN。第三個參數(shù)和第四個參數(shù)都是消息的具體內(nèi)容。一般我們用的是最后一個參數(shù)。第三個參數(shù)填為IntPtr.Zero。
當然如果是鼠標的動作,那么最后一個參數(shù)就是null。
?//******************************
???在項目中有這樣的需求,在主窗體隱藏時或者主進程運行時對其它窗體的控件或者事件進行控制,而且其它窗體是處于活動狀態(tài),而主窗體或者主進程是隱藏在后面的。這個時候使用句柄和消息來處理就比較好解決這些問題了,當然了也可以使用其它方法。比如將其它窗體在主窗體中申明并且定義,使之和主窗體一樣一直存在于內(nèi)存中,在各個窗體中申明公共方法,在主進程需要調(diào)用時直接調(diào)用即可,但是這樣耗費了大量的系統(tǒng)資源。現(xiàn)在使用消息來解決這個問題。下面提供一個小程序,在主窗體中通過句柄和消息來控制子窗體中Label上文字變化和顏色,代碼如下:
?
Windowns的API類
using?System;
using?System.Runtime.InteropServices;
namespace?TestHwnd
{
????public?class?Win32API
????{
?????????[DllImport("user32.dll ",?CharSet = CharSet.Unicode)]
????????public?static?extern?IntPtr?PostMessage(IntPtr hwnd,?int?wMsg,?string?wParam,?string?lParam);
????}
}
?
主窗體程序(發(fā)送消息):
using?System;
using?System.Collections.Generic;
using?System.ComponentModel;
using?System.Data;
using?System.Drawing;
using?System.Linq;
using?System.Text;
using?System.Windows.Forms;
using?System.Runtime.InteropServices;
namespace?TestHwnd
{
????public?partial?class?Main : Form
????{
????????//定義了一個子窗體的句柄
????????public?IntPtr hwndfrmTest;
????????
????????public?Main()
????????{
????????????InitializeComponent();
????????}
?????
????????private?void?timer1_Tick(object?sender,?EventArgs e)
????????{
????????????if(hwndfrmTest!=(IntPtr)0)
????????????{
????????????????if(DateTime.Now.Second?%?3?==?0)
????????????????{
????????????????????Win32API.PostMessage(hwndfrmTest,?0x60,?"",?"");
????????????????}
????????????????
????????????????if(DateTime.Now.Second?%?5?==?0)
????????????????{
????????????????????Win32API.PostMessage(hwndfrmTest,?0x61,?"",?"");
????????????????}
????????????????
????????????}
????????????
????????}
????????void?Button2Click(object?sender,?EventArgs e)
????????{
????????????frmTest frm=new?frmTest();
????????????frm.Show(this);
????????}
????}
子窗體程序(接收消息)
using?System;
using?System.Drawing;
using?System.Windows.Forms;
namespace?TestHwnd
{
?????///??<summary>
?????///??Description of frmTest.
?????///??</summary>
?????public??partial??class?frmTest : Form
????{
????????Main main;
?????????public??frmTest()
????????{
?????????????//
?????????????// The InitializeComponent() call is required for Windows Forms designer support.
?????????????//
?????????????InitializeComponent();
????????????
?????????????//
?????????????//?TODO: Add constructor code after the InitializeComponent() call.
?????????????//
????????}
????????
?????????void??FrmTest_Load(?object?sender,?EventArgs e)
????????{
????????????main =?this.Owner?as?Main;
?????????????//初始化該窗體的句柄
????????????main.hwndfrmTest =??this.Handle;
????????}
????????
?????????///?重寫窗體的消息處理函數(shù)DefWndProc,從中加入自己定義消息 MYMESSAGE 的檢測的處理入口
?????????protected??override??void??DefWndProc(?ref?Message m)
????????{
?????????????switch?(m.Msg)
????????????{
?????????????????case??0x60:
????????????????????{
????????????????????????label1.ForeColor=Color.Red;
????????????????????????label1.Text = DateTime.Now.?ToString()?+??"-"?+??"測試成功。。。。,呵呵,變紅了";
????????????????????}
?????????????????????break;
?????????????????case??0x61:
????????????????????{
????????????????????????label1.ForeColor=Color.Blue;
????????????????????????label1.Text = DateTime.Now.?ToString()?+??"-"?+??"測試成功。。。。,呵呵,變藍了";
????????????????????}
?????????????????????break;
?????????????????default:
?????????????????????base.?DefWndProc(?ref?m);
?????????????????????break;
????????????}
????????}
????????
????????
????????
?????????void??Button1Click(?object?sender,?EventArgs e)
????????{
????????????????main.hwndfrmTest =?(IntPtr)?(?0);
?????????????this.?Close();
????????}
????}
}
//******************************************
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.IO;
?
namespace findWindowTest
{
? ? public partial class Form1 : Form
? ? {
? ? ? ? public Form1()
? ? ? ? {
? ? ? ? ? ? InitializeComponent();
? ? ? ? }
?
? ? ? ? // Find Window
? ? ? ? // 查找窗體
? ? ? ? // @para1: 窗體的類名 例如對話框類是"#32770"
? ? ? ? // @para2: 窗體的標題 例如打開記事本 標題是"無標題 - 記事本" 注意 - 號兩側(cè)的空格
? ? ? ? // return: 窗體的句柄
? ? ? ? [DllImport("User32.dll", EntryPoint = "FindWindow")]
? ? ? ? public static extern IntPtr FindWindow(string className, string windowName);
? ? ? ?
? ? ? ?
? ? ? ?
? ? ? ?
? ? ? ?
? ? ? ? // Find Window Ex
? ? ? ? // 查找窗體的子窗體
? ? ? ? // @para1: 父窗體的句柄 如果為null,則函數(shù)以桌面窗口為父窗口,查找桌面窗口的所有子窗口
? ? ? ? // @para2: 子窗體的句柄 如果為null,從@para1的直接子窗口的第一個開始查找
? ? ? ? // @para3: 子窗體的類名 為""表示所有類
? ? ? ? // @para4: 子窗體的標題 為""表示要查找的窗體無標題 如空白的textBox控件
? ? ? ? // return: 子窗體的句柄
? ? ? ? [DllImport("user32.dll", EntryPoint = "FindWindowEx")]
? ? ? ? private static extern IntPtr FindWindowEx(
? ? ? ? ? ? IntPtr hwndParent,
? ? ? ? ? ? IntPtr hwndChildAfter,
? ? ? ? ? ? string lpszClass,
? ? ? ? ? ? string lpszWindow);
?
? ? ? ? // SendMessage
? ? ? ? // 向窗體發(fā)送消息
? ? ? ? // @para1: 窗體句柄
? ? ? ? // @para2: 消息類型
? ? ? ? // @para3: 附加的消息信息
? ? ? ? // @para4: 附加的消息信息
? ? ? ? [DllImport("User32.dll", EntryPoint = "SendMessage")]
? ? ? ? private static extern int SendMessage(
? ? ? ? ? ? IntPtr hWnd,
? ? ? ? ? ? int Msg,
? ? ? ? ? ? IntPtr wParam,
? ? ? ? ? ? string lParam);
?
? ? ? ? // 消息類型(部分)
? ? ? ? const int WM_GETTEXT ? ? = 0x000D; ?// 獲得窗體文本 如獲得對話框標題
? ? ? ? const int WM_SETTEXT ? ? = 0x000C; ?// 設(shè)置窗體文本 如設(shè)置文本框內(nèi)容
? ? ? ? const int WM_CLICK ? ? ? = 0x00F5; ?// 發(fā)送點擊消息如調(diào)用該窗體(按鈕)的"button1_Click();"
?
? ? ? ? // 本程序針對指定的另一程序窗體因此聲名了如下變量
? ? ? ? IntPtr Wnd ?= new IntPtr(0);// 一卡通注冊程序主窗體
? ? ? ? IntPtr sWnd = new IntPtr(0);// GroupBox控件 此為“一卡通注冊程序”主窗體的子窗體
? ? ? ? IntPtr txt ?= new IntPtr(0);// 文本框
? ? ? ? IntPtr btn1 = new IntPtr(0);// 查詢按鈕
? ? ? ? IntPtr btn2 = new IntPtr(0);// 注冊按鈕 這三個窗體又為“GroupBox控件”的子窗體
? ? ? ? //IntPtr popW = new IntPtr(0);// 彈出對話框
? ? ? ? //IntPtr popB = new IntPtr(0);// 彈出對話框確定按鈕
?
? ? ? ? // 文件操作
? ? ? ? private String filename = string.Empty;
? ? ? ? private StreamReader reader = null;
?
? ? ? ? // 從“打開文件”對話框打開txt文件 同時獲得需要的窗口句柄
? ? ? ? private void button2_Click(object sender, EventArgs e)
? ? ? ? {
? ? ? ? ? ? label2.Text = "";
? ? ? ? ? ? openFileDialog1.DefaultExt = "txt";
? ? ? ? ? ? openFileDialog1.Filter = "文本文件|*.txt";
? ? ? ? ? ? openFileDialog1.RestoreDirectory = true;
? ? ? ? ? ? openFileDialog1.FilterIndex = 1;
? ? ? ? ? ? if (openFileDialog1.ShowDialog() == DialogResult.OK)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? filename = openFileDialog1.FileName;
? ? ? ? ? ? }
?
? ? ? ? ? ? // 獲得窗口句柄
? ? ? ? ? ? Wnd ?= FindWindowEx((IntPtr)0, (IntPtr)0, null, "讀者一卡通注冊");// 一個注冊程序的窗體
? ? ? ? ? ? sWnd = FindWindowEx(Wnd, ?(IntPtr)0, null, "條件"); // 窗體上的一個GroupBox控件
? ? ? ? ? ? txt ?= FindWindowEx(sWnd, (IntPtr)0, null, ""); ? ? // GroupBox內(nèi)的textBox控件
? ? ? ? ? ? btn1 = FindWindowEx(sWnd, (IntPtr)0, null, "查詢"); // GroupBox內(nèi)的查詢按鈕
? ? ? ? ? ? btn2 = FindWindowEx(sWnd, (IntPtr)0, null, "注冊"); // GroupBox內(nèi)的注冊按鈕
? ? ? ? }
?
? ? ? ? // 重復地把文件內(nèi)讀取的行
? ? ? ? // 將該行發(fā)送給注冊程序窗體上的文本框中
? ? ? ? // 并“點擊”查詢按鈕和注冊按鈕
? ? ? ? // 直到文件讀取完畢
? ? ? ? private void button3_Click(object sender, EventArgs e)
? ? ? ? {
? ? ? ? ? ? //計數(shù)
? ? ? ? ? ? int count = 0;
?
? ? ? ? ? ? //讀取文件
? ? ? ? ? ? if (filename == string.Empty)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? button2.Focus();
? ? ? ? ? ? ? ? return;
? ? ? ? ? ? }
?
? ? ? ? ? ? reader = new StreamReader(filename);
? ? ? ? ? ? if (reader.EndOfStream)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? return;
? ? ? ? ? ? }
?
? ? ? ? ? ? string str = string.Empty;
?
? ? ? ? ? ? do
? ? ? ? ? ? {
? ? ? ? ? ? ? ? //讀取學號 保存在變量str中
? ? ? ? ? ? ? ? str = reader.ReadLine();
?
? ? ? ? ? ? ? ? //設(shè)置學號
? ? ? ? ? ? ? ? SendMessage(txt, WM_SETTEXT, (IntPtr)0, str);
?
? ? ? ? ? ? ? ? //點擊查詢按鈕
? ? ? ? ? ? ? ? SendMessage(btn1, WM_CLICK, (IntPtr)0, "");
?
? ? ? ? ? ? ? ? //點擊注冊按鈕
? ? ? ? ? ? ? ? SendMessage(btn2, WM_CLICK, (IntPtr)0, "");
?
? ? ? ? ? ? ? ? count++;
? ? ? ? ? ? }
? ? ? ? ? ? while(!reader.EndOfStream);
?
? ? ? ? ? ? reader.Close();
? ? ? ? ? ? filename = string.Empty;
? ? ? ? ? ? label1.Text = "注冊人數(shù):";
? ? ? ? ? ? label2.Text = Convert.ToString(count);
? ? ? ? }
? ? }
}
//******************************
C#獲取進程的主窗口句柄
通過調(diào)用Win32 API實現(xiàn)。
?
public?class?User32API
{
????private?static?Hashtable?processWnd?=?null;
????public?delegate?bool?WNDENUMPROC(IntPtr?hwnd,?uint?lParam);
????static?User32API()
????{
????????if?(processWnd?==?null)
????????{
????????????processWnd?=?new?Hashtable();
????????}
????}
????[DllImport("user32.dll",?EntryPoint?=?"EnumWindows",?SetLastError?=?true)]
????public?static?extern?bool?EnumWindows(WNDENUMPROC?lpEnumFunc,?uint?lParam);
????[DllImport("user32.dll",?EntryPoint?=?"GetParent",?SetLastError?=?true)]
????public?static?extern?IntPtr?GetParent(IntPtr?hWnd);
????[DllImport("user32.dll",?EntryPoint?=?"GetWindowThreadProcessId")]
????public?static?extern?uint?GetWindowThreadProcessId(IntPtr?hWnd,?ref?uint?lpdwProcessId);
????[DllImport("user32.dll",?EntryPoint?=?"IsWindow")]
????public?static?extern?bool?IsWindow(IntPtr?hWnd);
????[DllImport("kernel32.dll",?EntryPoint?=?"SetLastError")]
????public?static?extern?void?SetLastError(uint?dwErrCode);
????public?static?IntPtr?GetCurrentWindowHandle()
????{
????????IntPtr?ptrWnd?=?IntPtr.Zero;
????????uint?uiPid?=?(uint)Process.GetCurrentProcess().Id;??//?當前進程?ID
????????object?objWnd?=?processWnd[uiPid];
????????if?(objWnd?!=?null)
????????{
????????????ptrWnd?=?(IntPtr)objWnd;
????????????if?(ptrWnd?!=?IntPtr.Zero?&&?IsWindow(ptrWnd))??//?從緩存中獲取句柄
????????????{
????????????????return?ptrWnd;
????????????}
????????????else
????????????{
????????????????ptrWnd?=?IntPtr.Zero;
????????????}
????????}
????????bool?bResult?=?EnumWindows(new?WNDENUMPROC(EnumWindowsProc),?uiPid);
????????//?枚舉窗口返回?false?并且沒有錯誤號時表明獲取成功
????????if?(!bResult?&&?Marshal.GetLastWin32Error()?==?0)
????????{
????????????objWnd?=?processWnd[uiPid];
????????????if?(objWnd?!=?null)
????????????{
????????????????ptrWnd?=?(IntPtr)objWnd;
????????????}
????????}
????????return?ptrWnd;
????}
????private?static?bool?EnumWindowsProc(IntPtr?hwnd,?uint?lParam)
????{
????????uint?uiPid?=?0;
????????if?(GetParent(hwnd)?==?IntPtr.Zero)
????????{
????????????GetWindowThreadProcessId(hwnd,?ref?uiPid);
????????????if?(uiPid?==?lParam)????//?找到進程對應的主窗口句柄
????????????{
????????????????processWnd[uiPid]?=?hwnd;???//?把句柄緩存起來
????????????????SetLastError(0);????//?設(shè)置無錯誤
????????????????return?false;???//?返回?false?以終止枚舉窗口
????????????}
????????}
????????return?true;
????}
}
?
調(diào)用User32API.GetCurrentWindowHandle()即可返回當前進程的主窗口句柄,如果獲取失敗則返回IntPtr.Zero。
--EOF--
?
2008年10月7日補充:微軟實現(xiàn)的獲取進程主窗口句柄代碼
public?class?MyProcess
{
????private?bool?haveMainWindow?=?false;
????private?IntPtr?mainWindowHandle?=?IntPtr.Zero;
????private?int?processId?=?0;
????private?delegate?bool?EnumThreadWindowsCallback(IntPtr?hWnd,?IntPtr?lParam);
????public?IntPtr?GetMainWindowHandle(int?processId)
????{
????????if?(!this.haveMainWindow)
????????{
????????????this.mainWindowHandle?=?IntPtr.Zero;
????????????this.processId?=?processId;
????????????EnumThreadWindowsCallback?callback?=?new?EnumThreadWindowsCallback(this.EnumWindowsCallback);
????????????EnumWindows(callback,?IntPtr.Zero);
????????????GC.KeepAlive(callback);
????????????this.haveMainWindow?=?true;
????????}
????????return?this.mainWindowHandle;
????}
????private?bool?EnumWindowsCallback(IntPtr?handle,?IntPtr?extraParameter)
????{
????????int?num;
????????GetWindowThreadProcessId(new?HandleRef(this,?handle),?out?num);
????????if?((num?==?this.processId)?&&?this.IsMainWindow(handle))
????????{
????????????this.mainWindowHandle?=?handle;
????????????return?false;
????????}
????????return?true;
????}
????private?bool?IsMainWindow(IntPtr?handle)
????{
????????return?(!(GetWindow(new?HandleRef(this,?handle),?4)?!=?IntPtr.Zero)?&&?IsWindowVisible(new?HandleRef(this,?handle)));
????}
????[DllImport("user32.dll",?CharSet?=?CharSet.Auto,?SetLastError?=?true)]
????public?static?extern?bool?EnumWindows(EnumThreadWindowsCallback?callback,?IntPtr?extraData);
????[DllImport("user32.dll",?CharSet?=?CharSet.Auto,?SetLastError?=?true)]
????public?static?extern?int?GetWindowThreadProcessId(HandleRef?handle,?out?int?processId);
????[DllImport("user32.dll",?CharSet?=?CharSet.Auto,?ExactSpelling?=?true)]
????public?static?extern?IntPtr?GetWindow(HandleRef?hWnd,?int?uCmd);
????[DllImport("user32.dll",?CharSet?=?CharSet.Auto)]
????public?static?extern?bool?IsWindowVisible(HandleRef?hWnd);
}
總結(jié)
以上是生活随笔為你收集整理的C# 获得窗体句柄并发送消息(利用windows API可在不同进程中获取)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MFC和c#中模拟对另一进程的窗口按钮点
- 下一篇: JNDI 是什么