公司如今在研发基于.Net中WPF技术的产品,因为要兼容旧有产品,好比一些旧有的Win32程序、第三方的Win32程序等等,还要实现自动登陆这些外部Win32程序,所以必须可以将这些程序整合到咱们的系统中来,让使用者看起来它们好像是一个程序。 html
在MSDN中有专门的章节提到了在WPF中嵌入Win32控件的办法,那就是使用 HwndHost ,只要把 Win32控件的句柄传递给 HwndHost 就能够了。MSDN中的例子演示的都是在同一个进程内建立的 Win32控件,我一开始认为只要经过FindWindow等Win32API获得外部Win32程序的窗口句柄,而后将窗口句柄交给 HwndHost 就能够了。实现核心代码以下: java
protected override HandleRef BuildWindowCore( HandleRef hwndParent) python
{ 服务器
appProc = new Process (); app
appProc.StartInfo.WindowStyle = ProcessWindowStyle .Hidden; ide
appProc.StartInfo.FileName = @"D:\greeninst\netterm\netterm.exe" ; 工具
appProc.Start(); ui
//等待初始化完成,实现有点土 this
Thread .Sleep(1000); spa
hwndHost = Win32Native .FindWindow( "NetTermClass" , null );
// 嵌入在HwnHost中的窗口必需要 设置为WS_CHILD风格
uint oldStyle = Win32Native .GetWindowLong(hwndHost, Win32Native .GWL_STYLE);
Win32Native .SetWindowLong(hwndHost, Win32Native .GWL_STYLE, (oldStyle | Win32Native .WS_CHILD));
//将netterm的父窗口设置为HwndHost
Win32Native .SetParent(hwndHost, hwndParent.Handle);
return new HandleRef ( this , hwndHost);
}
这里启动的是NetTerm这个外部程序。实践证实我这种想法是可行的,可是惟一的问题就是虽然 外部Win32程序显示到WPF程序中来了,可是很奇怪的是嵌入的Win32程序再也没法点击了,点击按钮、输入按键都不起做用,程序好像死了同样。通过分析,我认为因为经过 SetParent 这个 Win32API 将NetTerm的父窗口设置为了 HwndHost ,这样 NetTerm就再也不有本身独立的窗口消息循环,而是眼巴巴等着 HwndHost 这个爹给他发 消息。可能因为WPF对于消息循环的处理 不一样于之前的Win32程序,致使全部的鼠标点击、按键 消息都不能被传递给NetTerm这个儿子,这样NetTerm就得不到任何消息,因此就像死了同样。
解决这个问题的思路是截获WPF的窗口消息,而后把它经过 SendMessage 这个Win32API 转发给NetTerm。可是找了半天也没找到WPF的消息处理的地方,请教同事之后得知WPF根本不像传统的Win32程序那样有窗口消息循环,而是本身搞了一套。郁闷了一下子,忽然灵光一现:管它什么WPF不WPF,它本质上仍是Win32程序,只不过是一个内部使用了DirectX技术的Win32程序而已,只要是Win32程序必定有办法拿到它的窗口消息循环。啥办法呢?对!就是窗口钩子。使用 SetWindowsHookEx 这个Win32API能够截获一个窗口全部的 消息循环,这样只要挑出来发给 HwndHost 的消息,而后把它转发给 NetTerm窗口就ok了。通过改造之后NetTerm终于活过来了!!!
解决了最核心的问题就该处理普通问题了,主要问题及对策以下:
1、隐藏NetTerm的窗口边框,这样看起来就感受不出来NetTerm是一个外部程序了。思路很简单使用 GetWindowLong 获得窗口原来的风格,而后再附加一个 WS_BORDER 风格就ok了。
//设置为WS_CHILD风格
uint oldStyle = Win32Native .GetWindowLong(hwndHost, Win32Native .GWL_STYLE);
//&~WS_BORDER去掉边框,这样看起来更像一个内嵌的程序,注意()的做用,改变默认的优先级
Win32Native .SetWindowLong(hwndHost, Win32Native .GWL_STYLE, (oldStyle | Win32Native .WS_CHILD)&~ Win32Native .WS_BORDER);
2、隐藏NetTerm在任务栏上的按钮
只要找到任务栏的句柄,而后首先向它发送TB_BUTTONCOUNT获得它上边按钮的个数,因为NetTerm是刚刚启动的,能够认为最后一个按钮就是NetTerm的按钮,只要向任务栏的句柄发送TB_DELETEBUTTON消息将最后一个按钮删掉就ok了。
private void HideTaskBarButton()
{
IntPtr vHandle = Win32Native.FindWindow("Shell_TrayWnd", null);
vHandle = Win32Native.FindWindowEx(vHandle, IntPtr.Zero,
"ReBarWindow32", IntPtr.Zero);
vHandle = Win32Native.FindWindowEx(vHandle,
IntPtr.Zero, "MSTaskSwWClass", IntPtr.Zero);
vHandle = Win32Native.FindWindowEx(vHandle, IntPtr.Zero,
"ToolbarWindow32", IntPtr.Zero);
//获得任务栏中按钮的数目
int vCount = Win32Native.SendMessage(new HandleRef(this, vHandle),
(uint)Win32Native.TB_BUTTONCOUNT, IntPtr.Zero, IntPtr.Zero).ToInt32();
//认为最后一个按钮就是被嵌套程序的按钮,删除它
Win32Native.SendMessage(new HandleRef(this, vHandle),
Win32Native.TB_DELETEBUTTON, new IntPtr(vCount - 1), IntPtr.Zero);
}
这是在WinXP下的处理。好像Win2000、Vista的任务栏的结构是不一样的,若是须要运行在这些OS下须要作进一步的改进。
三、 自动登陆。在NetTerm启动之后自动登陆到服务器,而且自动输入用户名、密码,而且启动指定的程序。NetTerm支持在启动参数中指定要链接的服务器地址,这样能够解决自动登陆到服务器的问题;使用 SendMessage( handle , Win32Native.WM_CHAR, ch , IntPtr.Zero) 向NetTerm窗口发送模拟按键就能够实现自动键入Linux指令的效果。因为Linux指令须要必定的处理的时间,因此每发完一条指令就要Sleep一下子以防止键入指令速度过快。
运行效果以下:主要代码以下, Win32Native .cs是咱们写的一个对Win32API的调用声明,都是简单的PInvoke声明,因为尺寸比较大这里就不贴出来了,你们能够查MSDN本身来声明。
=======================================NetTermHost.cs============================
namespace Client.Pages
{
class NetTermHost : HwndHost
{
public IntPtr hwndHost;
private IntPtr hookId = new IntPtr(3);
private HookProc hookProc;
private Process appProc;
protected override HandleRef BuildWindowCore(HandleRef hwndParent)
{
appProc = new Process();
appProc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
appProc.StartInfo.FileName = @"D:\greeninst\netterm\netterm.exe";
//设置要链接的主机名,这样启动之后就当即链接了
appProc.StartInfo.Arguments = "192.168.88.128";
appProc.Start();
//等待初始化完成,实现有点土
Thread.Sleep(1000);
hwndHost = Win32Native.FindWindow("NetTermClass", null);
//设置为WS_CHILD风格
uint oldStyle = Win32Native.GetWindowLong(hwndHost, Win32Native.GWL_STYLE);
//&~WS_BORDER去掉边框,这样看起来更像一个内嵌的程序,注意()的做用,改变默认的优先级
Win32Native.SetWindowLong(hwndHost, Win32Native.GWL_STYLE, (oldStyle | Win32Native.WS_CHILD)&~Win32Native.WS_BORDER);
//将netterm的父窗口设置为HwndHost,爹地我来了
Win32Native.SetParent(hwndHost, hwndParent.Handle);
//窗口最大化
Win32Native.ShowWindow(hwndHost.ToInt32(), Win32Native.SW_MAXIMIZE);
//隐藏netterm在任务栏上的按钮
HideTaskBarButton();
//隐藏netterm的工具栏
HideNetTermToolBar();
//因为登陆过程很是长,因此不要在这里等过久,不然界面像死了同样,因此启动线程来操做
ThreadStart ts = new ThreadStart(
delegate()
{
//自动登陆telnet
AutoLogin();
}
);
Thread thread = new Thread(ts);
thread.Start();
hookProc = new HookProc(MyHookHandler);
//设置钩子,截获主窗口界面消息循环
//对当前的窗口,使用IntPtr.Zero
HookApi.SetWindowsHookEx(hookId.ToInt32(), hookProc, IntPtr.Zero,
HookApi.GetCurrentThreadId());
return new HandleRef(this, hwndHost);
}
private int MyHookHandler(int code, IntPtr wparam, ref MSG msg)
{
//若是是当前Host的消息,则将其转发给netterm程序
if (msg.hwnd == this.Handle)
{
HandleRef handleRef = new HandleRef(this, hwndHost);
Win32Native.SendMessage(handleRef, (uint)msg.message, msg.wParam, msg.lParam);
}
int nextHook = HookApi.CallNextHookEx(hookId, code, wparam, ref msg);
return nextHook;
}
private void HideTaskBarButton()
{
IntPtr vHandle = Win32Native.FindWindow("Shell_TrayWnd", null);
vHandle = Win32Native.FindWindowEx(vHandle, IntPtr.Zero, "ReBarWindow32", IntPtr.Zero);
vHandle = Win32Native.FindWindowEx(vHandle, IntPtr.Zero, "MSTaskSwWClass", IntPtr.Zero);
vHandle = Win32Native.FindWindowEx(vHandle, IntPtr.Zero, "ToolbarWindow32", IntPtr.Zero);
//获得任务栏中按钮的数目
int vCount = Win32Native.SendMessage(new HandleRef(this, vHandle), (uint)Win32Native.TB_BUTTONCOUNT, IntPtr.Zero, IntPtr.Zero).ToInt32();
//认为最后一个按钮就是被嵌套程序的按钮,删除它
Win32Native.SendMessage(new HandleRef(this, vHandle), Win32Native.TB_DELETEBUTTON, new IntPtr(vCount - 1), IntPtr.Zero);
}
private void HideNetTermToolBar()
{
IntPtr toolBarWin = Win32Native.FindWindowEx(hwndHost, IntPtr.Zero, "ToolbarWindow32", IntPtr.Zero);
Win32Native.ShowWindow(toolBarWin.ToInt32(), 0);
}
private void AutoLogin()
{
Thread.Sleep(10000);
//输用户名
SendString("yzk\n");
Thread.Sleep(1000);
//输密码
SendString("123456\n");
Thread.Sleep(1000);
//进入目录
SendString("cd /mnt/hgfs/NAHA/src/\n");
Thread.Sleep(1000);
//运行字符终端
SendString("python FrontEnd.py\n");
}
//模拟按键
private void SendString(String s)
{
foreach(char c in s)
{
Win32Native.SendMessage(new HandleRef(this, hwndHost), Win32Native.WM_CHAR, new IntPtr(c), IntPtr.Zero);
}
}
protected override void DestroyWindowCore(HandleRef hwnd)
{
HandleRef handleRef = new HandleRef(this, hwndHost);
//关闭netterm窗口
//Win32Native.SendMessage(handleRef, Win32Native.WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
//很黄很暴力,直接杀死
appProc.Kill();
//有bug,若是netterm已经连上远程主机,那么若是不退出就close的话会弹出对话框,这就会形成主程序没法退出
//几种策略:杀死netterm、发送模拟键点击“是”按钮、把netterm释放出来让用户决定、只是demo而已无论它
//没有主菜单的bug
//因为是拦截消息循环搞的,因此有可能有潜在的bug
Win32Native.DestroyWindow(hwnd.Handle);
HookApi.UnhookWindowsHookEx(hookId);
}
}
}
=======================TradeNetTermHost.cs===========================================
public partial class TradeNetTermHost : UserControl
{
private NetTermHost ch;
public TradeNetTermHost()
{
Win32Native.InitCommonControls();
InitializeComponent();
ch = new NetTermHost();
this.Win32HosterBorder.Child = ch;
Loaded += new RoutedEventHandler(TradeNetTermHost_Loaded);
}
void TradeNetTermHost_Loaded(object sender, RoutedEventArgs e)
{
//设置netterm容器为焦点,不然消息不会发给它
Win32Native.SetFocus(ch.Handle);
}
}
引用:http://www.blogjava.net/huanzhugege/archive/2008/04/24/195516.html