Windows應用程序開發筆記-控制和獲取其他程序窗口控件內容


從以下需求引出的

在自己開發的程序中獲取第三方應用程序窗口中文本框的內容,或者控制按鈕的點擊等行為

有兩種方案:

1. 使用windows api函數FindWindow,FindWindowEx,SendMessage,EnumChildWindows等來完成獲取和控制。函數的具體參數、使用說明等在微軟網站有詳細介紹,以c#中使用FindWindow為例:

 

public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

const int WM_SETTEXT = 0x000C;
const int WM_GETTEXT = 0x000D;
const int WM_CLICK = 0x00F5;

const int WM_LBUTTONDOWN = 0x0201;
const int WM_LBUTTONUP = 0x0202;
const int WM_CLOSE = 0x0010;
const int WM_GETTEXTLENGTH = 0x000E;

/// <summary>
/// Windows API函數,查找窗口句柄,在查找時不區分大小寫。關於句柄以及FindWindow函數的參數,用法等在https://www.cnblogs.com/gyc19920704/p/5430964.html 有說明。
/// </summary>
/// <param name="lpClassName">spy++工具中的 類 沒有則為null</param>
/// <param name="lpWindowName">spy++工具中的 標題 沒有則為null</param>
/// <returns></returns>
[DllImport("user32.dll", EntryPoint = "FindWindow")]
public extern static IntPtr FindWindow(string lpClassName, string lpWindowName);

//查找窗口內控件句柄
[DllImport("user32.dll", EntryPoint = "FindWindowEx", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

//發送消息
[DllImport("user32.dll", EntryPoint = "SendMessageA")]
private static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, StringBuilder lParam);

[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessage1(IntPtr hWnd, int Msg, int wParam, StringBuilder lParam);

[DllImport("user32.dll")]
public static extern int EnumChildWindows(IntPtr hWndParent, CallBack lpfn, int lParam);

[DllImport("user32.dll")]
public static extern int GetWindowText(IntPtr hwnd, StringBuilder sb, int length);

public delegate bool CallBack(IntPtr hwnd, int lParam);


private void button1_Click(object sender, EventArgs e)
{
IntPtr mW = FindWindow(null, "MyForm");
if (mW != IntPtr.Zero)
{
MessageBox.Show("找到程序窗口!");

IntPtr et = FindWindowEx(mW, IntPtr.Zero, "WindowsForms10.EDIT.app.0.141b42a_r7_ad1", null); //第三個參數使用spy++找的"類"
if (et != IntPtr.Zero)
{
MessageBox.Show("找到文本框!");

StringBuilder s1 = new StringBuilder(512);
//SendMessage(et, 0x000d, 510, s1); //0x000d是向控件發送獲取文本消息的消息編號
//SendMessage1(et, WM_SETTEXT, IntPtr.Zero, DateTime.Now.ToString());

MessageBox.Show(s1.ToString());
//WindowsForms10.BUTTON.app.0.141b42a_r7_ad1
IntPtr hbutton = FindWindowEx(mW, IntPtr.Zero, "WindowsForms10.BUTTON.app.0.141b42a_r7_ad1", null);
//SendMessage1(hbutton, WM_CLICK, IntPtr.Zero, null);

//SendMessage1(hbutton, WM_LBUTTONDOWN, IntPtr.Zero, null);
//SendMessage1(hbutton, WM_LBUTTONUP, IntPtr.Zero, null);
}
else
{
MessageBox.Show("沒找到文本框!");
}
}
else
{
MessageBox.Show("沒有窗口!");
}
}

/// <summary>
        /// 查找窗體內的控件句柄
        /// </summary>
        /// <param name="hwnd">父窗體句柄</param>
        /// <param name="lpszWindow">控件標題(Text)</param>
        /// <param name="bChild">設定是否在子窗體中查找</param>
        /// <returns>控件句柄,沒找到返回IntPtr.Zero</returns>
        public static IntPtr FindWindowExMy(IntPtr hwnd, string lpszWindow, bool bChild)
{
IntPtr iResult = IntPtr.Zero;
            // 首先在父窗體上查找控件
            iResult = FindWindowEx(hwnd, IntPtr.Zero, null, lpszWindow);
            // 如果找到直接返回控件句柄
            if (iResult != IntPtr.Zero)
{
return iResult;
}

            // 如果設定了不在子窗體中查找
            if (!bChild)
{
return iResult;
}

            // 枚舉子窗體,查找所有子窗體里的控件句柄
            int i = EnumChildWindows(
hwnd,
(h, l) =>
{
IntPtr f1 = FindWindowEx(h, IntPtr.Zero, null, lpszWindow);
if (f1 == IntPtr.Zero)
{
return true;
}
else
{
StringBuilder title = new StringBuilder(200);
int len;
len = GetWindowText(hwnd, title, 200);

iResult = f1;
return false;
}
},
0);
            // 返回查找結果
            return iResult;
}


public bool Report(IntPtr hwnd, int lParam)
{
Console.Write("Window handle is :");
Console.WriteLine(hwnd);
return true;
}
// public delegate bool CallBack(int hwnd, int lParam);
[DllImport("user32")]
public static extern int EnumWindows(CallBack x, int y);

 

private void button2_Click(object sender, EventArgs e)
{
CallBack myCallBack = new CallBack(Report);
EnumWindows(myCallBack, 0);
//-----------------------------------------------------------------------------------------------
IntPtr mW = FindWindow(null, "MyForm");
if (mW != IntPtr.Zero)
{
MessageBox.Show("已找到程序窗口!");

IntPtr et = FindWindowExMy(mW, "button1", true);
//IntPtr et = FindWindowEx(mW, IntPtr.Zero, "WindowsForms10.EDIT.app.0.141b42a_r7_ad1", null); //第三個參數使用spy++找的"類"。對於.net winform窗口,每個控件有特定的值
//IntPtr et = (IntPtr)0x0001021A;//句柄 在程序窗口關閉重啟后是會變化的。
if (et != IntPtr.Zero)
{
MessageBox.Show("已找到文本框!");
SendMessage(et, WM_SETTEXT, IntPtr.Zero, new StringBuilder("xxx123"));//為什么不生效?

StringBuilder s1 = new StringBuilder(512);
SendMessage(et, WM_GETTEXT, (IntPtr)512, s1); //0x000d是向控件發送獲取文本消息的消息編號

MessageBox.Show(s1.ToString());

//SendMessage(et, WM_SETTEXT, IntPtr.Zero, new StringBuilder("xxx123"));

SendMessage(et, WM_CLICK, IntPtr.Zero, null);

//SendMessage1(et, WM_LBUTTONDOWN, IntPtr.Zero, null);
//SendMessage1(et, WM_LBUTTONUP, IntPtr.Zero, null);
}
else
{
MessageBox.Show("沒找到文本框!");
}
}
else
{
MessageBox.Show("沒有找到程序窗口!");
}
}
}

獲取句柄的輔助工具有spy++或WinSpy,以下以spy++為例,在visual studio中有集成,“工具=>獲取工具和功能=>單個組件=>C++核心功能”,

 

 

 

打開此工具后按ctrl f 然后點圖標拖到要查找的其他程序窗口上即可顯示句柄

 

 

 

方案2:使用.Net下的UIAutomation技術,網上此類文章很多,例如:https://blog.csdn.net/lassewang/article/details/6693917 有很詳細的例子

以下摘自:https://www.cnblogs.com/miaosha5s/p/4993321.html

一.界面的自動化操作

.Ui自動化測試

.軟件外掛 

二.Win32基礎知識

a.Win32中一切元素皆窗口,窗口之間有父子關系。整個桌面是一個“根窗口”。

b.進程:

根據進程id拿到進程對象Process process = Process.GetProcessById(processId);

啟動一個進程:Process process = Process.Start(exe路徑);

殺死一個進程process.Kill() 

三.UIAutonation基礎

1、需要添加對UIAutomationClient、 UIAutomationProvider、 UIAutomationTypes的引用

2、AutomationElement.RootElement是窗口根元素

AutomationElement.FromHandle(IntPtr hwnd)從窗口句柄拿到AutomationElement對象。

3、遍歷:

mainElement.FindAll(TreeScope.Descendants,

new PropertyCondition(AutomationElement.ClassNameProperty, "TLabeledEdit"));

TreeScope.Descendants代表遞歸從所有子孫元素中遞歸查找;如果是從直接子節點查找,則使用TreeScope.Children。

Condition是過濾條件,可以根據類名等查找,如果是不指定查詢條件則使用Condition.True Condition。

FindFirst是查到第一個。 

4、點擊按鈕、設置文本、讀取文本使用Pattern來實現。不是所有Pattern都支持

(1) 設置控件的值

ValuePattern

valuePattern = (ValuePattern)element.GetCurrentPattern(ValuePattern.Pattern);

valuePattern.SetValue("rupeng.com");

(2) 得到文本控件的值

TextPattern

valuePattern = (TextPattern)element.GetCurrentPattern(TextPattern.Pattern);

string v= valuePattern.DocumentRange.GetText(-1);

(3) 調用控件,比如點擊按鈕

var clickPattern = (InvokePattern)element.GetCurrentPattern(InvokePattern.Pattern);

clickPattern.Invoke();

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM