基于个人需要,想控制其他程序,获取其他程序接运行结果。
具体来说,现在我想要在我的程序中控制金山词霸查询一个单词,最后能在我的程序中显示查询的结果。
就像把其他程序作为一个函数来调用,首先想到的是发送消息。
需要做到的有
1. 填入要查询的的单词
2. 单词“查词”按钮
3. 获取查询的结果并显示在程序中
前两步很容易做到,获取相应的控件的句柄,然后发送相应的消息就行了。
具体用到了
/// <summary>
/// 获取子窗体
/// </summary>
private void GetChildren()
{
Children.Clear();
IntPtr hwnd = IntPtr.Zero;
hwnd = User32.FindWindowEx(Wnd, hwnd, null, null);
while (hwnd != IntPtr.Zero)
{
WindowInfo child = new WindowInfo(hwnd, this);
Children.Add(child);
hwnd = User32.FindWindowEx(Wnd, hwnd, null, null);
}
}
/// <summary>
/// 设置文本
/// </summary>
/// <param name="text"></param>
public void SetText(string text)
{
//User32.SetWindowText(Wnd, text);
//下面这个能够设置Edit,上面则不行
User32.SendMessage(Wnd, Constant_WM.WM_SETTEXT, IntPtr.Zero, text);
}
/// <summary>
/// 单击按钮
/// </summary>
public void ButtonClick()
{
User32.SendMessage(Wnd, Constant_BM.BM_CLICK, IntPtr.Zero, "0");
}
为了处理上方便,写了个WindowInfo类,封装了句柄相关的信息,内部还有个Children,形成了一个控件树,便于获取子控件。
(User32是一个封装了User32.dll里面的API函数的静态类)
获取界面上的主要控件:
protected override void GetInfo()
{
base.GetInfo();
GetBtnSearch();
TxtInput = Window.GetChildByClass("Edit");
TxtResult = Window.GetChildByClass("Internet Explorer_Server");
}
public WindowInfo TxtInput { get; set; }
public WindowInfo BtnSearch { get; set; }
public WindowInfo TxtResult { get; set; }
private void GetBtnSearch()
{
foreach (WindowInfo child in Window.Children)
{
if (child.Width == 59 && child.Height == 29)
{
BtnSearch = child;
}
}
}
第三步比较麻烦,是一个Internet Explorer_Server类型的控件。
幸亏网上直接有解决方案,他们的目的是要控制IE里面的内容,我只需要获取内容,再把内容给一个WebBrowser控件就行了。
整理了一下,做成一个静态类了HtmlDocumentHelper(代码在最后),需要引用COM里面的“Microsoft HTML Object Library”。
最后就完成了一个XDict类,应该算是一种代理吧,用它来间接控制金山词霸。
public class XDict : ThirdTool
{
public XDict()
{
Name = "XDict";
Path = ConfigurationManager.AppSettings["XDictPath"];
//Init();
}
protected override void GetInfo()
{
base.GetInfo();
GetBtnSearch();
TxtInput = Window.GetChildByClass("Edit");
TxtResult = Window.GetChildByClass("Internet Explorer_Server");
}
public WindowInfo TxtInput { get; set; }
public WindowInfo BtnSearch { get; set; }
public WindowInfo TxtResult { get; set; }
private void GetBtnSearch()
{
foreach (WindowInfo child in Window.Children)
{
if (child.Width == 59 && child.Height == 29)
{
BtnSearch = child;
}
}
}
public string Search(string word,bool getResult=true)
{
if (Process == null) return null;
string html = "";
if (TxtInput == null) GetInfo();
if (TxtInput == null) return null;
TxtInput.SetText(word);
if (BtnSearch == null) GetBtnSearch();
if (BtnSearch == null) return null;
BtnSearch.ButtonClick();
if (getResult)
{
html = HtmlDocumentHelper.GetHtml(TxtResult.Wnd);
}
return html;
}
}
ThirdTool是负责打开程序,获取程序句柄等通用功能的类,实际上现在已经在写其他程序的代理类了。
这个技术完善的话还是比较通用的:自己做不到的,首先找开源,开源的没有或无法满足的则直接控件已有该功能的软件。嘛,估计上不了大雅之堂,只能个人玩玩。
不知道有没有这种的完善的框架了,先自己研究下,以后再找找看。
HtmlDocumentHelper:
public static class HtmlDocumentHelper
{
public const int SMTO_ABORTIFHUNG = 0x2;
public static Guid IID_IHTMLDocument = new Guid("626FC520-A41E-11CF-A731-00A0C9082637");
[DllImport("OLEACC.dll")]
public static extern int ObjectFromLresult(int lResult, ref Guid riid, int wParam, ref IHTMLDocument2 ppvObject);
public static IHTMLDocument2 GetIEDocument()
{
Process[] processes = Process.GetProcessesByName("iexplore");
IntPtr hWnd = IntPtr.Zero;
if (processes.Length > 1)
{
foreach (Process process in processes)
{
if (process.MainWindowTitle != "")
{
hWnd = process.MainWindowHandle;
}
}
}
return HtmlDocumentHelper.GetDocument(hWnd);
}
public static IHTMLDocument2 GetDocument(IntPtr hWnd)
{
IHTMLDocument2 document=new HTMLDocumentClass();
if (hWnd != IntPtr.Zero)
{
//IntPtr hWnd = processes[2].MainWindowHandle;
int lngMsg = 0;
int lRes = 0;
User32.EnumProc proc = new User32.EnumProc(EnumWindows);
User32.EnumChildWindows(hWnd, proc, ref hWnd);
if (!hWnd.Equals(IntPtr.Zero))
{
lngMsg = User32.RegisterWindowMessage("WM_HTML_GETOBJECT");
if (lngMsg != 0)
{
User32.SendMessageTimeout(hWnd, lngMsg, 0, 0, SMTO_ABORTIFHUNG, 1000, ref lRes);
if (!(bool)(lRes == 0))
{
int hr = ObjectFromLresult(lRes, ref IID_IHTMLDocument, 0, ref document);
if ((bool)(document == null))
{
//MessageBox.Show("No IHTMLDocument Found!", "Warning");
}
}
}
}
}
return document;
}
public static string GetHtml(IntPtr hWnd)
{
IHTMLDocument2 document = GetDocument(hWnd);
return GetHtml(document);
}
public static string GetHtml(IHTMLDocument2 document)
{
string text = "";
foreach (IHTMLElement element in document.all)
{
text += element.innerHTML;
}
return text;
}
private static int EnumWindows(IntPtr hWnd, ref IntPtr lParam)
{
int retVal = 1;
StringBuilder classname = new StringBuilder(128);
User32.GetClassName(hWnd, classname, classname.Capacity);
/// check if the instance we have found is Internet Explorer_Server
if ((bool)(string.Compare(classname.ToString(), "Internet Explorer_Server") == 0))
{
lParam = hWnd;
retVal = 0;
}
return retVal;
}
}