用MFC实现WebGUI--(CDHtmlDialog)

自从去年年底一次棘手的界面,开始研究用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功能组件,功能组件化不仅使代码封装性好,而且可以用于多种语言。

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

posted on 2006-12-15 21:11 万连文 阅读(18641) 评论(38)  编辑 收藏 引用 所属分类: MFC

FeedBack:

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2006-12-19 22:40 | noname

的确不错. 用 html 做界面在开发效率, 扩展性等方面都很好. 正巧这两天需要做一个程序. 界面要求比较灵活. 就试试万兄的 idea 了.

用 WTL 做了一第一次做这样的应用, 先不考虑代码复用了. 所以没有做成 DLL. 连页面的 ExternalDispatch 都是在程序内部实现的.

既然万兄没有给出源码, 我把个 demo. 因为是代码放上来. 算是狗尾续貂把. :)

http://nicoster.googlepages.com/wtlhtml.rar

lieph $(at) 163 $(dot) com  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2006-12-20 14:10 | cooelaf

看了万兄的blog,很赞叹万兄在这方面的造诣。  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2006-12-25 10:51 | shaolong

万哥,可否把你说的这个例子代码公开一下呢?我刚入门做嵌入html的vc项目,也是CDHTMLDialog派生的类,但不知该如何实现函数接口的对应,时间很紧,郁闷的很,谢谢了!  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2006-12-25 11:04 | 万连文

邮件给我,我发你。 
  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2006-12-26 23:59 | xhl

我在VC6下曾做过WEBGUI,对你的想法很感兴趣,想知道如何用com封装功能,已经在html中怎样调用com组件。 
能把你的示例代码发一份给我吗? 
我的邮箱[email protected] 
多谢!  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2006-12-27 00:01 | xhl

我在VC6下曾做过WEBGUI,对你的想法很感兴趣,想知道如何用com封装功能,以及在html中怎样调用com组件。 
能把你的示例代码发一份给我吗? 
我的邮箱[email protected] 
多谢!  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2007-01-28 14:04 | xie

我是个初学者也想要一份多谢了  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2007-02-25 17:55 | kukustream

很感兴趣,可否将示例的源码发给我一份,谢谢! [email protected]  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2007-03-05 17:24 | xq

万前辈,我早就想想学学这种技术,苦于资料难找,今得遇高人。 
给我一份,万分感谢。[email protected]  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2007-03-05 22:47 | 万连文

看有如此多人索要代码,实感意外。由于当时调试代码比较凌乱外加当初是为公司开发,代码没有公开。现在看来公司不会采用了,可能思考方式不同。最近在搞毕业开题,过1周后我将整理代码并公布于本页下载。  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2007-03-10 13:08 | 万连文

下载文件已经提供,vs2005环境,关于其他环境本人不打算移植。  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2007-08-15 18:44 | lanse

我想要一份 
[email protected] 
  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2007-09-05 11:35 | 我也想要一份,拜托啦

我也想要一份,拜托啦 
[email protected]  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2007-09-05 14:10 | 万连文

唉,真不知该如何说了,下载就在下面,不过字有些小罢了。  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2007-10-13 08:52 | yefeng

我也想要一份,拜托啦 ! 
[email protected] 
  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2007-12-03 16:12 | Ason Jia

最近我也在弄这个,但是我有遇见一个问题,因为中间的html大小(不是文件大小,是html的宽度和高度)是会变的,所以我有想动态从html获取大小,具体做法是,html中有一个变量来保存html大小,当html被加载的时候,就由js动态算出大小,然后我们的dialog去获取这个大小(使用IWebBrowser2直接去查询html中此值),然后动态调整dialog的大小以适应html的大小,但是问题也就出在这儿:html在加载的时候,经常会因为一些原因使得DOM中这个element没有创建出来,或者此element有创建出来,但是大小却没有计算出来(也就是此节点有,但是value是“0”),汗~~~~~这个问题我想了很久都解决不了
不知道作者可否提供一些帮助,再次非常感谢~~~  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2007-12-04 10:10 | 万连文

我的想法: 
html被加载的时候,最好不要去使用html的东西,这个时候dom文档还没有加载完全。你需要在OnDocumentCompleted事件的时候去调用脚本,然后去修改对话框大小。  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2007-12-04 15:04 | Ason Jia

我也想过OnDocumentCompleted消息被触发的时候去获取,但是测试的时候发现,这个消息函数被触发了,但是html还是没有准确的计算出大小。
那你提到的在这个函数里面调用JS的方法算出大小,这个我没去试过,其实我是不知道,汗~~~对COM不太熟悉。
但是这样看来,好像是可行的。  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2007-12-04 15:14 | 万连文

我想你可能错误的认识了这个东西。html是没有办法计算大小的,它的layout依赖于web控件的大小。采取这种方法做界面,最好针对一类html页面大小固定的功能使用一个对话框类,这样设置对话框固定大小即可以,且对于一类对话框,功能处理也比较类似。  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2007-12-05 17:38 | Ason Jia

目前我也是比较倾向这种思路,无奈老板说,俺们的产品要做n国语言,因为语言的不同,还是你提到的控件的不同,直接造成html大小的变化,晕~~~
不过在C中调用js方法,我已经弄出来了,无奈只能在这个方向先做一番苦功先了
汗~~
实在不行,那我就只能写死了~
谢谢老万同学的帮忙和指点,呵呵。。。。再次感谢~
  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2008-06-19 06:39 | arthurlee

一开始以为需要自己实现active scriping的接口,看了这篇文章才知道方法如此简单。 
十分感谢!  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2008-10-13 17:58 | 阿里

实际上这并未提供active scriping的接口,而仅仅是对WebBrowser控件的额外控制,如果不用MFC可能需要用到接口IDocHostUIHandler,IDocHostUIHandler2和IDocHostShowUI。

实现自己的Active Scripting,需要用IActiveScriptSite  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)[未登录]

2008-11-06 12:01 | yy

您好 我也想要一份代码看看,谢谢! 
[email protected]  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2008-11-10 10:28 | 万连文

来自原子时代--您的email有问题???
很抱歉这么晚回复你,因为最近病了。首先,我对一个女孩子从事C++开发表达敬佩。说到界面开发,绝对不会是一个简单的任务,甚至连一本真正的好的技术书籍都没有。CDHtmlDialog是vs2003以后的sdk添加进去的,不过这个用的人也不多,但是简单很多。我一般使用的是atl里面提供的一个窗口类ATLAXWIN_CLASS来实现的,这个在vc6里面也有。根据我的经验,能使用html+css+JS来实现界面编程的人技术都不一般,至少对com有一些了解。对于您目前的情况,如果使用vc6开发可以考虑ATLAXWIN_CLASS这个类进行编程,依赖atl库,跟mfc无关。具体的例子我手头没有,可以查阅www.codeproject.com
www.codeguru.com等技术网站。如果你自己尝试之后还是无法得到结果,请与我联系,我会做一个demo。因为这段时间确实病的不轻,本想顺手做一个给你,但是一看电脑脑袋晕。不好意思。

总是发不出去,再试试.  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)[未登录]

2009-02-23 16:34 | 小四

lz你好,我是谢老师同门师兄,最近也用类似的界面解决方案。 
有空加我msn聊聊 [email protected]  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2009-10-27 18:07 | 

你好,我想学习一下html内嵌到vc中使用,能给我发份代码吗?[email protected] 非常感谢  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2009-10-28 09:02 | WXX

下载就在最后面2个字,不明白为什么那么多人看不见。  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2009-12-30 09:45 | Eleanor

你好,我最近才开始弄html嵌入mfc对话框,我想问一下我只想得到网页的某些按钮的链接地址,调用我的程序去ping它,返回一个ping通时间给网页。我查了下网上说用IHTMLElement里的put_onclick,可是这些接口我不熟,我发现vc2005里的mshtml.h中定义了好几个IHTMLDocument和IHTMLElement接口方便的话加下我msn:[email protected]指导一下,谢谢  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2010-04-18 14:08 | jjqcat

很感兴趣,可否将示例的源码发给我一份,谢谢! 
[email protected]  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2010-08-26 10:19 | yyk

很好。你的想法不错。take a look!!  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2011-01-28 12:07 | 无未

最近也在考虑这个东西 不过令我很为难的是在vc6.0下 加之对com不是很了解…… 努力 关注……  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2011-07-25 19:14 | tammy

我的怎么在DEBUG文件夹下打不开应用程序呢.  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2011-07-26 10:19 | tammy

急....  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2011-07-26 10:19 | tammy

程序运行起来了.但点击浏览按钮时..报错说当前页的脚本运行错误  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2011-07-26 10:23 | 万连文

@tammy 
应该是没有注册com组件。  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2012-09-22 15:25 | 芦苇

前辈 我是刚开始学习这方面内容的新手,您有木有这方面的资料、书籍推荐呢,跪求拜谢。联系方式:[email protected]  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2013-01-30 18:02 | 云怜秀年

下载不到,打不开@noname
  回复  更多评论

# re: 用MFC实现WebGUI--(CDHtmlDialog)

2013-01-30 18:03 | 云怜秀年

我想要一份源码
O(∩_∩)O谢谢
[email protected]  回复  更多评论

时间: 2024-08-29 19:05:12

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

CDHtmlDialog的基本使用

转自:http://blog.csdn.net/sky04/article/details/7587406 因为我的部门只有我一个人(无奈之极,只有我一个做C++的,其他的都在做C#),所以我去跟技术顾问讨论我们客户端到底该怎么呈现.他说按照以往(上一套系统)的经验,升级很频繁,建议还是使用内嵌 HTML的方式来显示界面,这样以后只要升级HTML就可以了.我听了之后,牙掉了一半,我拖了半个月的对话框就这么给否了,心里着实不是滋味. 然后就开始寻找MFC显示HTML页面的方法,找了一些自定义的HT

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

当WebBrowser控件(CDHtmlDialog自动创建了WebBrowser控件)加载的网页中含有错误Javascript代码时默认情况下控件会弹出错误信息提示对话框,相对于用户体验来说这样的提示完全不是开发人员想要的,针对这个问题有两个解决方案,一是完全屏蔽掉错误提示,二是控制错误的提示并且记录错误信息同时也可以控制出现错误后Javascript是否继续执行. 1.屏蔽错误信息提示 1 m_pBrowserApp->put_Silent(VARIANT_TRUE); 在CDHtmlDia

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

原文地址:http://www.vckbase.com/document/viewdoc/?id=1486 浏览器的定制与扩展       作者:李汉鹏 下载源代 码  本文分如下章节: 前 言 在 MFC中使用浏览器 怎 样扩展或定制浏览器 定 制鼠标右键弹出出菜单 实 现脚本扩展(很重要的external接口) C++ 代码中如何调用网页脚本中的函数 定 制消息框的标题 怎 样定制.修改浏览器向Web服务器发送的HTTP请求头 怎 样修改浏览器标识 去 掉讨厌的异常警告 怎 样处理浏览器内的

《深入浅出MFC》第七章 简单而完整:MFC骨干程序

不二法门:熟记MFC类层次结构.经常使用的主要MFC类:CWinApp, CWnd<-CView, CWnd<-CFrameWnd, CFrameWnd<-CMDIFrameWnd, CFrameWnd<-CMDIChildWnd, CWnd<-CDialog, CWnd<-CControlBar, CControlBar<-CStatusBar, CControlBar<-CToolBar, CCmdTarget<-CDocument, CCmdTa

深入浅出MFC——消息映射与命令传递(六)

1. 消息分类: 2. 万流归宗——Command Target(CCmdTarget): 3. "消息映射"是MFC内建的一个信息分派机制.通过三个宏(DECLARE_MESSAGE_MAP/BEGIN.../ON.../END...)完成消息网的建构. 4. 注意:CWinThread派生自CCmdTarget,但没有DECLARE_/BEGIN_/END_宏组. 5. 消息映射与虚函数: 6.

MFC控件使用技巧:List Control

1)每列内容过长,显示不完整 只有加载数据的情况下,才会出现水平滚动条 解决方案: 可以添加如下一个空的内容项: m_List.InsertItem(0,NULL);//为了显示进度条 2)不允许点击修改第一列(当然最多能够让我们改动的也只有他了) MFC默认情况下可以修改第一列,其他的不允许修改(需要定制DrawItem) 解决方案: Edit Labels 属性设置为 False 3)报表的形式表示 View 属性设置为 Report 4)注意:不要和列表框控件混淆(英文名: List Bo

【mfc】用对话框分页实现用户登录

所谓的对话框分页就是点击完一个对话框的按钮,切换到另一个对话框, 这样的对话框多用于一些需要用户登录才能够进行操作的软件, 下面就用对话框分页来实现用户登录系统 一.基本目标 有如下的程序,输入用户名与密码,如果用户名为admin,密码为123456,那么则成功登录,切换到一个有"欢迎登录"与"关闭"按钮的对话框 如果用户名或者密码输入错误则弹出提示, 点击关闭能够关闭这个程序,不弹出用户登录的对话框. 二.制作过程 1.首先如同之前的<[mfc]对于对话框程

MFC中给控件添加变量,DoDataExchange中

DoDataExchange函数其实是一项数据动态绑定技术.比如你在写动态按钮过程中须对按钮添加变量时,怎么添加?控件类已经写好了,其变量是已经固定的.你要添加新的变量就要用到DoDataExchange函数. 你要在对话框的构造函数里面初始化一个变量,再用DoDataExchange函数将它绑定到你的动态按扭中,比如:DDX_Check(pDX, IDC_CHECK1, m_Lesson1);这就是将m_Lesson1(这是一个外部变量,其定义在对话框的构造函数里)绑定到IDC_CHECK1中

MFC消息映射的原理:笔记

多态的实现机制有两种,一是通过查找绝对位置表,二是查找名称表:两者各有优缺点,那么为什么mfc的消息映射采用了第二种方法,而不是c++使用的第一种呢?因为在mfc的gui类库是一个庞大的继承体系,而里面的每个类有很多成员函数(只说消息反映相关的成员函数啊),而且在派生类中,需要改写的也比较少(我用来做练习的程序就是那么一两个,呵呵).那么用c++的虚函数的实现机制会导致什么问题呢?就是大量虚表的建立使得空间浪费掉很多. 嗯-怎么办呢?于是各大c++名库(比如QT,MFC,VCL-)在消息映射的实