公司现在在研发基于.Net中WPF技术的产品,由于要兼容旧有产品,比如一些旧有的Win32程序、第三方的Win32程序等等,还要实现自动登录这些外部Win32程序,因此必须能够将这些程序整合到我们的系统中来,让使用者看起来它们好像是一个程序。

在MSDN中有专门的章节提到了在WPF中嵌入Win32控件的办法,那就是使用 HwndHost ,只要把 Win32控件的句柄传递给 HwndHost 就可以了。MSDN中的例子演示的都是在同一个进程内创建的 Win32控件,我一开始认为只要通过FindWindow等Win32API得到外部Win32程序的窗口句柄,然后将窗口句柄交给 HwndHost 就可以了。实现核心代码如下:

protected   override   HandleRef  BuildWindowCore( HandleRef  hwndParent)

{

appProc =  new   Process ();

appProc.StartInfo.WindowStyle =  ProcessWindowStyle .Hidden;

appProc.StartInfo.FileName =  @"D:\greeninst\netterm\netterm.exe" ;

appProc.Start();

//等待初始化完成,实现有点土

Thread .Sleep(1000);

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下需要做进一步的改进。

3、 自动登录。在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);

}

}

REF:http://www.blogjava.net/huanzhugege/archive/2008/04/24/195516.html

WPF中嵌入普通Win32程序的方法的更多相关文章

  1. 在WPF中嵌入WebBrowser可视化页面

    无论是哪种C/S技术,涉及数据可视化就非常的累赘了,当然大神也一定有,只不过面向大多数人,还是通过网页来实现,有的时候不想把这两个功能分开,一般会是客户的原因,所以我们打算在WPF中嵌入WebBrow ...

  2. Qt界面中嵌入其他exe程序的界面,使用Qt5

    下面用一个小例子来演示如何在Qt的界面中嵌入其他exe程序的界面,最终效果如下图所示.本文参考了 http://blog.csdn.net/jiaoyaziyang/article/details/4 ...

  3. WPF中嵌入Office编辑器(支持Word、Excel、PPT、Visio等)

    现在有一个项目,需要使用wpf做一个简单的客户端,用来生成word.excel.ppt.visio等文档,这就需要能够在wpf中嵌入office的编辑器,并对office文档进行编辑. 在网上搜索了一 ...

  4. WPF中嵌入WinForm中的webbrowser控件

    原文:WPF中嵌入WinForm中的webbrowser控件 使用VS2008创建WPF应用程序,需使用webbrowser.从工具箱中添加WPF组件中的webbrowser发现其中有很多属性事件不能 ...

  5. vc++ 在程序中运行另一个程序的方法

    在vc++ 程序中运行另一个程序的方法有三个: WinExec(),ShellExcute()和CreateProcess() 三个SDK函数: WinExec,ShellExecute ,Creat ...

  6. 提供PPT嵌入Winform/WPF解决方案,Winform/WPF 中嵌入 office ppt 解决方案

    Winform/WPF 中嵌入 office ppt(powerpoint)解决方案示: 1. 在winform中操作ppt,翻页.播放.退出:显示 总页数.当前播放页数 2. 启动播放ppt时录制视 ...

  7. WPF中Binding使用StringFormat格式化字符串方法

    原文:WPF中Binding使用StringFormat格式化字符串方法 货币格式 <TextBlock Text="{Binding Price, StringFormat={}{0 ...

  8. 总结:WPF中MultiBinding多值绑定的方法

    原文:总结:WPF中MultiBinding多值绑定的方法 一.Xaml中绑定代码: <TextBlock  Grid.Row="5" Grid.Column="3 ...

  9. 把演讲人的桌面、头像、声音合成后推送到 指定的直播流平台上; 录制电脑桌面、摄像头头像、声音保存为本地视频; 适用于讲课老师、医生等演讲内容保存为视频; 提供PPT嵌入Winform/WPF解决方案,Winform/WPF 中嵌入 office ppt 解决方案

    提供PPT嵌入Winform/WPF解决方案,Winform/WPF 中嵌入 office ppt 解决方案 Winform/WPF 中嵌入 office ppt(powerpoint)解决方案示: ...

随机推荐

  1. i2c设备驱动移植笔记(二)

    说明:上一篇博客写了我在移植android驱动之TEF6606的苦逼遭遇,即驱动层向应用层提供接口支持,查找了两天的资料,不得不放弃,转而进行IIC下移植RTC设备的实验. 第一步:查找设备的数据手册 ...

  2. DIV设置overflow无效的原因

    因为项目需求需要在一个div中添加多个checked 添加的时候使用了 <label><input type="checkbox" value="123 ...

  3. 响应式注意要添加“视口”约束标记---viewport

    视口:我们试图在iPhone中输出屏幕宽度,你会发现屏幕宽度是980,却和PC屏幕差不多大.原因是苹果主导的这些手机厂商,为了使用户获得完整web体验,很多设备都会欺骗浏览器返回一个数值较大的“视口” ...

  4. iOS开发拓展篇—xib中关于拖拽手势的潜在错误

    iOS开发拓展篇—xib中关于拖拽手势的潜在错误 一.错误说明 自定义一个用来封装工具条的类 搭建xib,并添加一个拖拽的手势. 主控制器的代码:加载工具条 封装工具条以及手势拖拽的监听事件 此时运行 ...

  5. 在Ubuntu上安装JDK、Ant、Jmeter和Jenkins

    一.前期准备 1. 在win7下载VMware.Ubuntu(用迅雷下比较快) 2. 安装完VMware后新建虚拟机,选择iso: 3. 具体配置参考如下,至此Ubantu安装完成 二.在Ubuntu ...

  6. 为什么使用 Bootstrap?

    为什么使用 Bootstrap? 移动设备优先:自 Bootstrap 3 起,框架包含了贯穿于整个库的移动设备优先的样式. 浏览器支持:所有的主流浏览器都支持 Bootstrap.      容易上 ...

  7. java基础之 内部类

    Java中的内部类共分为四种: 静态内部类static inner class (also called nested class) 成员内部类member inner class 局部内部类loca ...

  8. 拔靴法--Bootstrap--R语言实现

    拔靴法属于重复抽样(resampling)方法,与Monte Carlo相比,二者真实的母体不同.它是将已有的观察值作为母体重复抽样, 以求取原先资料不足二无法探讨的资料特性. 举个例子,假设x1,x ...

  9. gulp ---攻略一

    根据项目需要可能会出连载 项目需要现在用gulp进行js的质量检测.合并.压缩.发布,未来需要进行sass的编译.合并.压缩,html.img的压缩以及md5戳.reload等功能,暂时先测试js的质 ...

  10. 北京网赛I题 hiho1391 (树状数组、区间覆盖最大值问题)

    题目链接:http://hihocoder.com/problemset/problem/1391 题意:A国和B国向对方分别投射N枚和M枚导弹(发射时间,飞行时间,伤害值),同时两国各自都有防御系统 ...