自从去年年底一次棘手的界面,开始研究用web做界面到现在大约1年,这一年间不是局限在实现层面,也并非一直研究这一个问题,有很多问题其实不是问题,只是自己没有想清楚或者思想没放开。对于一个界面开发人员,想必拉的对话框不少于100个,腻味不必说,光是对话框大小改变导致控件跟着变化都需要一番功夫,加上界面美观,界面的风格统一,界面的灵活多变......,头痛。在对话框里面加载位图,加载gif,超链接......,啊,没法控制了吧!在考虑远点,现在.net3.0技术已经完全打破应用和桌面的界限,我们的界面html资源完全可以放在一个web站点上,这样界面是完全动态的。

其间写过2篇这方面的文章,基于vc6实现,绕弯很大。在vc7.1、vc8里面要简单很多,主要是把几个以前为公开的类公开了,最重要的是在CWnd里面加入了一个虚函数CreateControlSite使得有机会改变控件站点以修改控件行为。在mfc类层次上,CHTMLView和CDHtmlDialog为开发者提供了创建webgui的一系列基础设施,包括事件机制、窗口行为、以及对html文档操纵接口。我们在此基础上实现webgui很简单,然而仍然困惑我很久,经理也催过我几次我一直未肯决定最终方案。在我脑袋里一直琢磨是要应用程序完全操纵html文档,还是html访问应用获取信息,其实就是它们之间的通信模式。一直到昨天我才定下方案,应用通过IWebBrowser2接口操纵html元素,html通过vbscript、javascript脚本响应本身事件,访问应用。主要是考虑通信自然畅通,而以前我一味想通过应用指令完全控制html元素,导致去解析html文档,费力不讨好。下面开始我的想法:

写一个dll,封装CDHtmlDialog,提供一个类似html容器的对话框,功能就是加载html网页,以及创建与html呼应的com组件。它本身不包含与应用功能有关代码,应用有关的部分是html页面和对于的com功能组件。这里需要对CDHtmlDialog进行了适当的改造以适合自己的目标:

首先从CDHtmlDialog派生一个类CHTMLContainerDlg,默认情况下会生成一个网页资源,这个网页是这个对话框创建时加载的,我们需要的其实是一个容器而不是一个具体的对话框,所以删除网页资源,修改对话框头文件:

enum { IDD = IDD_HTMLCONTAINERDLG, IDH = 0 };

这里把IDH修改为0,因为我们删除了网页资源。然而在对话框创建后会加载该资源,在CDHtmlDialog的OnInitDialog函数里面我们可以看到:

if (m_nHtmlResID)
        LoadFromResource(m_nHtmlResID);
    else if (m_szHtmlResID)
        LoadFromResource(m_szHtmlResID);
    else if (m_strCurrentUrl)
        Navigate(m_strCurrentUrl);

结果就是对话框一出现就会出现加载一个无效地址的页面,出现无法打开链接的页面,为了避免这个问题,需要重载OnInitDialog函数。其实就是拷贝mfc代码然后去掉上面那段代码就ok,强制不加载页面。那么为了加载指定页面,需要一个函数:

void CHTMLContainerDlg::SetHtmlAndCom(CString strURL, CString strProg)
{
    HRESULT        hr        = NOERROR;
    m_strURL = strURL;
    hr = m_spComDisp.CoCreateInstance(strProg);
    if(FAILED(hr))
    {
        TRACE(_T("Some error when create com object\n"));
    }
    SetExternalDispatch(m_spComDisp);
}

指定html的url和对应功能组件的progid,这样在网页里面可以通过脚本window.external访问该com组件。
这样就可以加载html网页,但是html页面里面的元素风格却是2k风格(至少在ie7以下版本是如此),这个怕是没起到一点美观作用,为之我考虑了半天,问过做web的人是否有办法,最终还是灵感光临,误撞上了。重载GetHostInfo函数:

STDMETHODIMP CHTMLContainerDlg::GetHostInfo(DOCHOSTUIINFO* pInfo)
{
    pInfo->dwFlags = DOCHOSTUIFLAG_THEME;
    return S_OK;
}

这个多得不说,^_^。
下面就可以演示了,在vs2005里面找个向导来show一下:

CHTMLContainerDlg    dlg;
    TCHAR                szPath[MAX_PATH] = { 0 };
    CString                strPath;
    GetCurrentDirectory(MAX_PATH, szPath);
    strPath = szPath;
    strPath += _T("\\Default.htm");
    dlg.SetHtmlAndCom(strPath, _T("TestWebCom.WebComCtrl"));
    dlg.DoModal();


对话框标题其实可以通过解析html文档获取title标题设置,目前还未处理。下面看看html与应用交互的组件。
生成一个atl工程,TestWebCom,添加一个com组件WebComCtrl,添加方法处理上面那个带...的按钮(文件夹浏览按钮):

STDMETHODIMP CWebComCtrl::ShowFolderBrowser(void)
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());

    // TODO: 在此添加实现代码
    AfxMessageBox(_T("In Com, you can show folder select dialog"));
    return S_OK;
}

这里不作具体处理,只是象征性弹出一个对话框。好了,上面我们在对话框里面已经设置了com组件的progid,这里可以把html和组件关联上了,通过脚本可以访问com组件方法:

<BUTTON CLASS="buttonClass3Custom" ID="BrowseBtn" TYPE="BUTTON" TITLE="浏览头文件。" onClick="OnBrowseHeaderFile();"></BUTTON>

脚本如下:

function OnBrowseHeaderFile()
{
    window.external.ShowFolderBrowser();
}

下面运行试一试,按下选择文件夹按钮会出现如下询问组件是否安全的对话框:

这个很恼人,用户可没有耐心忍受每次多弹出这个对话框询问组件是否安全。我开始打算将组件实现安全接口解决掉此问题,不过不知道缘何,没有成功,网上搜索一下好像说在ie7里面无效,没办法还是看mfc源码来解决问题。
CDHtmlDialog类获取external代码如下:

STDMETHODIMP CDHtmlDialog::GetExternal(IDispatch **ppDispatch)
{
    if(ppDispatch == NULL)
        return E_POINTER;
        
    *ppDispatch = NULL;
    if (m_spExternalDisp.p && CanAccessExternal())
    {
        m_spExternalDisp.p->AddRef();
        *ppDispatch = m_spExternalDisp.p;
        return S_OK;
    }
    return E_NOTIMPL;
}

看到CanAccessExternal函数,肯定就是验证安全性的代码,找到函数声明,幸好是虚函数,重载直接返回TRUE:

BOOL CHTMLContainerDlg::CanAccessExternal()
{
    // we trust all com object (haha, you can make virus)
    return TRUE;
}

有兴趣的朋友可以看下内部实现。
这下就好了,按下网页选择文件夹按钮,弹出对话框:

一套流程完备,方案个人觉得不错,各司其职,通信自然畅通,一个html配对一个com功能组件,功能组件化不仅使代码封装性好,而且可以用于多种语言。

由于此技术不用于公司开发,今整理提供下载

from:http://www.cppblog.com/wlwlxj/archive/2006/12/15/16495.aspx

用MFC实现WebGUI--(CDHtmlDialog)的更多相关文章

  1. MFC CDHtmlDialog 加载本地资源

    步骤:1.资源视图 项目右击选择资源添加,自定义添加新类型 如:JS(会增加JS文件夹)2. 选择1新建的文件夹右击 添加资源 导入 选择js文件引入3. 在资源文件Resource.h文件夹能找到资 ...

  2. CDHtmlDialog的基本使用

    转自:http://blog.csdn.net/sky04/article/details/7587406 因为我的部门只有我一个人(无奈之极,只有我一个做C++的,其他的都在做C#),所以我去跟技术 ...

  3. CDHtmlDialog探索----WebBrowser扩展和网页Javascript错误处理

    当WebBrowser控件(CDHtmlDialog自动创建了WebBrowser控件)加载的网页中含有错误Javascript代码时默认情况下控件会弹出错误信息提示对话框,相对于用户体验来说这样的提 ...

  4. 利用MFC实现浏览器的定制与扩展(JavaScript与C++交互)

    原文地址:http://www.vckbase.com/document/viewdoc/?id=1486 浏览器的定制与扩展       作者:李汉鹏 下载源代 码  本文分如下章节: 前 言 在 ...

  5. vs2010静态链接MFC库报链接错误

    由于需要将MFC程序在其它电脑上运行,所以需要将动态链接的MFC改成静态链接,本以为很简单,没想到链接的时候出现下面的链接错误: uafxcw.lib(afxmem.obj) : error LNK2 ...

  6. MFC中成员变量的声明顺序与析构顺序

    第一次用博客,第一篇随笔,就写今天遇到的一个问题吧. 在VS2008的MFC对话框程序,窗口成员变量的声明顺序与其析构顺序相反,即,先声明的变量后析构,后声明的变量先析构.未在其他模式下测试. cla ...

  7. VC中的MFC到底是什么?

    1. 微软基础类库(英语:Microsoft Foundation Classes,简称MFC)是一个微软公司提供的类库(class libraries),以C++类的形式封装了Windows API ...

  8. MFC&Halcon之实时视频监控

    上一篇实现了在MFC的窗体内显示图片,本篇介绍如何在MFC窗体内实时显示摄像头的影像. 要实现的功能是点击一个“开始”按钮,可以显示影像,再点击“停止”按钮,可以停止显示. 因为实时显示影像需要在一个 ...

  9. MFC快速入门 - 菜单

    本文仅用于学习交流,商业用途请支持正版!转载请注明:http://www.cnblogs.com/mxbs/p/6231104.html 打开VS2010,依次打开File – New – Proje ...

随机推荐

  1. 项目总结SpringMVC+hibernate框架 web.xml 分析(2)

    紧接 项目总结SpringMVC+hibernate框架 原理(MVC) applicationContext.xml 文件(3) 这一步讲解项目模块化的配置,项目中每个模块配置一个文件,命名规则为 ...

  2. hpu第五届acm比赛

    vijos P1211生日日数   描述 CCC老师的生日是YY年MM月DD日,他想知道自己出生后第一万天纪念日的日期(出生日算第0天). 格式 输入格式 从文件的第一行分别读入YY,MM,DD其中1 ...

  3. Unity3d 网络编程(二)(Unity3d内建网络各项參数介绍)

    这里是全部Unity3d在网络中能用到相关的类及方法.纵观參数功能, Unity3d来写一个手游是不二的选择: RPC 能够传递的參数 int float string NetworkPlayer N ...

  4. C++11 thread::detach(2)

    原文地址:http://www.cplusplus.com/reference/thread/thread/detach/ public member function <thread> ...

  5. iOS WebCore的WebEvent和EventHandler

    WebEvent是iOS专有的类,负责封装和携带从UIKit得到的系统事件信息,并由WebKit层的WAKResponder子类传递到WebCore的EventHandler. UIKit层的逻辑可参 ...

  6. stm32之CAN发送、接收详解

    CAN接收报文并过滤之标识符过滤:(重点.难点) 在CAN协议里,报文的标识符不代表节点的地址,而是跟报文的内容相关的.因此,发送者以广播的形式把报文发送给所有的接收者.节点在接收报文时-根据标识符的 ...

  7. WinForm - 两个窗体之间的方法调用

    方法1:   所有权法//Form1://需要有一个公共的刷新方法public   void   Refresh_Method(){ //...} //在调用Form2时,要把Form2的所有者设为F ...

  8. ZOJ 3483 简单if-else

    提醒:答案要约分,不然会错! #include<iostream> #include<cstdio> #include<cstring> #include<a ...

  9. IO操作之使用zip包压缩和解压缩文件

    转自:http://www.cdtarena.com/java.html​​Java API中的import java.util.zip.*;包下包含了Java对于压缩文件的所有相关操作. 我们可以使 ...

  10. Linux下搭建Hadoop集群

    本文地址: 1.前言 本文描述的是如何使用3台Hadoop节点搭建一个集群.本文中,使用的是三个Ubuntu虚拟机,并没有使用三台物理机.在使用物理机搭建Hadoop集群的时候,也可以参考本文.首先这 ...