VC编程之使用vc+如何自定义菜单2
小标 2019-01-15 来源 : 阅读 819 评论 0

摘要:本文主要向大家介绍了VC编程之使用vc+如何自定义菜单2,通过具体的内容向大家展示,希望对大家学习VC编程有所帮助。

本文主要向大家介绍了VC编程之使用vc+如何自定义菜单2,通过具体的内容向大家展示,希望对大家学习VC编程有所帮助。

VC编程之使用vc+如何自定义菜单2

实现脚本扩展(很重要的external接口)
  在你嵌入了浏览器的工程中,如果网页的脚本中能调用C++代码,那将是一件很惬意的事情,要实现这种交互,就必须实现脚本扩展。实现脚本扩展就是在程序中实现一个IDispatch接口,通过CHtmlView类的OnGetExternal虚函数返回此接口指针,这样就可以在脚本中通过window.external.XXX(关键字window可以省略)来 引用接口暴露的方法或属性(XXX为方法或属性名)。在MFC中从CCmdTarget派生的类都可以实现自动化,而不必在MFC工程中引入繁杂的ATL。从CCmdTarget派生的类实现自动化接口的时候不要忘了在构造函数中调用EnableAutomation函数。   要使虚函数OnGetExternal发挥作用必须在 自定义的控制站点类中实现IDocHostUIHandler,在接口IDocHostUIHandler的GetExternal方法中调用浏览器类的OnGetExternal虚函数,我们在浏览器类的派生类重载OnGetExternal虚函数, 通过参数lppDispatch返回一个IDispatch指针,这样脚本中引用window.external时就是引用的返回的接口,参见代码 HRESULT CDocHostSite::XDocHostUIHandler::GetExternal(IDispatch ** ppDispatch) { METHOD_PROLOGUE(CDocHostSite, DocHostUIHandler); return pThis->m_pView->OnGetExternal( ppDispatch ); }
CLhpHtmlView::CLhpHtmlView(BOOL isview) { ...... EnableAutomation();// 允许自动化 }
HRESULT CLhpHtmlView::OnGetExternal(LPDISPATCH *lppDispatch) { *lppDispatch = GetIDispatch(TRUE);// 返回自身的IDispatch接口 return S_OK; }
请注意上面代码中,在OnGetExternal返回的是自身IDispatch接口, 这样就不比为脚本扩展而另外写一个从CCmdTarget派生的新类, CLhpHtmlView本身就是从CCmdTarget派生,直接在上面实现接口就是。
下用具体示例来说明怎样实现脚本扩展
示例会在网页上点击一个按钮而使整个窗口发生抖动
从CLhpHtmlView派生一个类CDemoView,在类中实现IDispatch, 并通过IDispatch暴露方法WobbleWnd
文件 DemoView.h
....... class CDemoView : public CLhpHtmlView { ...... DECLARE_DISPATCH_MAP() // 构建dispatch映射表以暴露方法或属性 ...... void WobbleWnd();// 抖动窗口 };

文件 DemoView.cpp
......
// 把成员函数映射到Dispatch映射表中,暴露方法给脚本 BEGIN_DISPATCH_MAP(CDemoView, CLhpHtmlView) DISP_FUNCTION(CDemoView, "WobbleWnd", WobbleWnd, VT_EMPTY, VTS_NONE) END_DISPATCH_MAP()
......
void CDemoView::WobbleWnd() { // 在这里实现抖动窗口 ...... }

文件 Demo.htm
...... ......
这里我要介绍一下DISP_FUNCTION宏,它的作用是将一个函数映射到Dispatch映射表中,我们看 DISP_FUNCTION(CDemoView, "WobbleWnd", WobbleWnd, VT_EMPTY, VTS_NONE) CDemoView是宿主类名, "WobbleWnd"是暴露给外面的名字(脚本调用时使用的名字), VT_EMPTY是返回值得类型为空,VTS_NONE说明此方法没有参数,如果要映射的函数有返回值和参数该 如何映射,通过下面举例来说明
DISP_FUNCTION(CCalendarView,"TestFunc",TestFunc,VT_BOOL,VTS_BSTR VTS_I4 VTS_I4)
BOOL TestFunc(LPCSTR param1, int param2, int param3) { ..... }
  参数表VTS_BSTR VTS_I4 VTS_I4是用空格分隔,他们的类型映射请参考MSDN,这要提醒的是不要把VTS_BSTR与CString对应,而应与LPCSTR对应。
C++代码中如何调用网页脚本中的函数
  IHTMLDocument2::scripts属性表示HTML文档中所有脚本对象。使用脚本对象的IDispatch接口的GetIDsOfNames方法可以得到脚本函数的 DispID,得到DispID后,使用IDispatch的Invoke函数可以调用对应的脚本函数。CLhpHtmlView提供了方便的调用JavaScript的函数,请参考CLhpHtmlView中有关键字“JScript”的代码。
定制消息框的标题
  我们在脚本中调用alert弹出消息框时,消息框的标题是微软预定义的“Microsoft Internet Explorer”,如下图:
  在自定义的控制站点类中实现IDocHostShowUI接口,在接口的ShowMessage方法中调用浏览器的OnShowMessage,我们重载 OnShowMessage虚函数即可定制消息框的标题,实现代码如下: // 窗口标题"Microsoft Internet Explorer"的资源标识 #define IDS_MESSAGE_BOX_TITLE 2213 HRESULT CLhpHtmlView::OnShowMessage(HWND hwnd, LPOLESTR lpstrText, LPOLESTR lpstrCaption, DWORD dwType, LPOLESTR lpstrHelpFile, DWORD dwHelpContext, LRESULT * plResult) { //载入Shdoclc.dll 和IE消息框标题字符串 HINSTANCE hinstSHDOCLC = LoadLibrary(TEXT("SHDOCLC.DLL")); if (hinstSHDOCLC == NULL) return S_FALSE;
CString strBuf,strCaption(lpstrCaption);
strBuf.LoadString(hinstSHDOCLC, IDS_MESSAGE_BOX_TITLE);

// 比较IE消息框标题字符串和lpstrCaption// 如果相同,用自定义标题替换if(strBuf==lpstrCaption)
    strCaption = m_DefaultMsgBoxTitle;

// 创建自己的消息框并且显示
*plResult = MessageBox(CString(lpstrText), strCaption, dwType);

//卸载Shdoclc.dll并且返回
FreeLibrary(hinstSHDOCLC);return S_OK;
}
从代码中可以看到通过设定m_DefaultMsgBoxTitle的值来改变消息宽的标题,修改此值是同过SetDefaultMsgBoxTitle来实现 void CLhpHtmlView::SetDefaultMsgBoxTitle(CString strTitle) { m_DefaultMsgBoxTitle=strTitle; }
怎样定制、修改浏览器向Web服务器发送的HTTP请求头
  在集成了WebBrowser控件的应用中,Web服务器有时可能希望客户端(浏览器)发送的HTTP请求中附带一些额外的信息或自定义的 HTTP头字段,这样就必须在浏览器中控制向Web服务器发送的HTTP请求。 下面是捕获的一个普通的用浏览器发送的HTTP请求头: GET /text7.htm HTTP/1.0 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, 
application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, / Referer: //localhostAccept-Language: en-us User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Poco 0.31; LHP Browser 1.01; 
.NET CLR 1.1.4322) Host: localhost Connection: Keep-Alive
CHtmlView的 void Navigate2( LPCTSTR lpszURL, DWORD dwFlags = 0, LPCTSTR lpszTargetFrameName = NULL, LPCTSTR lpszHeaders = NULL, LPVOID lpvPostData = NULL, DWORD dwPostDataLen = 0 ); 函数参数lpszHeaders可以指定HTTP请求头,示例如下: Navigate2(_T("//localhost"),NULL,NULL, "MyDefineField: TestValue"); 我们捕获的HTTP头如下:
怎样修改浏览器标识
  在HTTP请求头中User-Agent字段表明了浏览器的版本以及操作系统的版本等信息。WEB服务器经常需要知道用户请求页面时是来自IE还是来自自己的客户端中的WebBrowser控件, 以便分开处理,而WebBrowser控件向WEB服务器发送的浏览器标识(User-Agent字段)跟用IE发送的是一样的,怎样区分自己的浏览器和IE呢? 微软没有提供现成的方法,要自己想法解决。 前面讨论的定制HTTP请求头就是为这一节准备的。 思路是这样的: 在自己的浏览器里处理每一个U页面请求,把请求头User-Agent改成自己想要的。 在CHtmlView的OnBeforeNavigate2虚函数里来修改HTTP请求是再好不过了, #define WM_NVTO (WM_USER+1000)
class NvToParam { public: CString URL; DWORD Flags; CString TargetFrameName; CByteArray PostedData; CString Headers; };
void CDemoView::OnBeforeNavigate2(LPCTSTR lpszURL, DWORD nFlags, LPCTSTR lpszTargetFrameName, CByteArray& baPostedData, LPCTSTR lpszHeaders, BOOL* pbCancel) { CString strHeaders(lpszHeaders); if(strHeaders.Find("User-Agent:LHPBrowser 1.0") < 0)// 检查头里有没有自定义的User-Agent串 { *pbCancel = TRUE;// 没有,取消这次导航
    if(!strHeaders.IsEmpty())
        strHeaders += "\r\n";
    strHeaders += "User-Agent:LHPBrowser 1.0";// 加上自定义的User-Agent串

    NvToParam* pNvTo = new NvToParam;
    pNvTo->URL = lpszURL;
    pNvTo->Flags = nFlags;
    pNvTo->TargetFrameName = lpszTargetFrameName;
    baPostedData.Copy(pNvTo->PostedData);
    pNvTo->Headers = strHeaders;

    // 发送一个自定义的导航消息,并把参数发过去
    PostMessage(WM_NVTO,(WPARAM)pNvTo);
    return;
}

CHtmlView::OnBeforeNavigate2(lpszURL, 
                             nFlags, 
                             lpszTargetFrameName, 
                             baPostedData, 
                             lpszHeaders, 
                             pbCancel);
}
LRESULT CDemoView::OnNvTo(WPARAM wParam, LPARAM lParam) { NvToParam* pNvTo = (NvToParam*)wParam; Navigate2((LPCTSTR)pNvTo->URL, pNvTo->Flags, pNvTo->PostedData, (LPCTSTR)pNvTo->TargetFrameName, (LPCTSTR)pNvTo->Headers); delete pNvTo; return 1; }   在OnBeforeNavigate2中如果发现没有自定义的User-Agent串,就加上这个串,并取消本次导航,再Post一个消息(一定要POST,让OnBeforeNavigate2跳出以后再进行导航 ),在消息中再次导航,再次导航时请求头已经有了自己的标识,所以能正常的导航。
去掉讨厌的异常警告
  在程序中使用了CHtmlView以后,我们在调整窗口大小的时候经常会看到输出窗口输出的异常警告: ReusingBrowser.exe 中的 0x77e53887 处最可能的异常: Microsoft C++ exception: COleException @ 0x0012e348 。 Warning: constructing COleException, scode = DISP_E_MEMBERNOTFOUND($80020003). 这是由于CHtmlView在处理WM_SIZE消息时的一点小问题引起的,采用如下代码处理WM_SIZE消息就不会有此警告了 void CLhpHtmlView::OnSize(UINT nType, int cx, int cy) { CFormView::OnSize(nType, cx, cy);
if (::IsWindow(m_wndBrowser.m_hWnd)) 

    CRect rect; 
    GetClientRect(rect); 
    // 就这一句与CHtmlView的不同
    ::AdjustWindowRectEx(rect, GetStyle(), FALSE, WS_EX_CLIENTEDGE);
    m_wndBrowser.SetWindowPos(NULL, 
                              rect.left, 
                              rect.top, 
                              rect.Width(), 
                              rect.Height(), 
                              SWP_NOACTIVATE | SWP_NOZORDER); 

}
怎样处理浏览器内的拖放
  有时可能有这样的需求,我们希望在资源管理器里托一个文件到浏览器而做出相应的处理,甚至是将文件拖到某一个网页元素上来做出相应的处理,而浏览器默认的处理拖放文件操作是将文件打开,但WebBrowser控件给了我们一个自己处理拖放的机会。 那就是在自定义的控制站点类中实现IDocHostUIHandler,在接口IDocHostUIHandler的GetDropTarget方法中调用 浏览器类的OnGetDropTarget虚函数。要处理网页内的拖放,必需在OnGetDropTarget函数中返回一个自己定义的IDropTarget接口指针, 所以我们自己写一个类CMyOleDropTarget从COleDropTarget类派生,并且在实现IDropTarget接口,此类的代码在这就不列出了,请下载演示 程序,参考文件MyOleDropTarget.h和MyOleDropTarget.cpp。我们看CLhpHtmlView中OnGetDropTarget的代码 HRESULT CLhpHtmlView::OnGetDropTarget(LPDROPTARGET pDropTarget, LPDROPTARGET* ppDropTarget ) { m_DropTarget.SetIEDropTarget(pDropTarget);
LPDROPTARGET pMyDropTarget;
pMyDropTarget = (LPDROPTARGET)m_DropTarget.GetInterface(&IID_IDropTarget);if(pMyDropTarget)
{
    *ppDropTarget = pMyDropTarget;
    pMyDropTarget->AddRef();
    return S_OK;
}

return S_FALSE;
}   m_DropTarget即为自定义的处理拖放的对象。这样就能通过在从CLhpHtmlView派生的类中重载OnDragEnter、OnDragOver、 OnDrop、OnDragLeave虚函数来处理拖放了。在这里顺带讲一下视图是怎样处理拖放的。 要使视图处理拖放,首先在视图里添加一个COleDropTarget(或派生类)成员变量,如CLhpHtmlView中的“CMyOleDropTarget m_DropTarget;”,再在 视图创建时调用COleDropTarget对象的Register,即把视图与COleDropTarget对象关联起来,如CLhpHtmlView中的“m_DropTarget.Register(this);”,再对拖放 触发的事件进行相应的处理, OnDragEnter 把某对象拖入到视图时触发,在此检测拖入的对象是不是视图想接受的对象,如是返回“DROPEFFECT_MOVE”表示接受此对象,如 if(pDataObject->IsDataAvailable(CF_HDROP))// 被拖对象是文件吗? return DROPEFFECT_MOVE;   OnDragOver 被拖对象在视图上移动,同OnDragEnter一样检测拖入对象,如果要接受此对象返回“DROPEFFECT_MOVE”。 OnDrop 拖着被拖对象在视图上放开鼠标,在这里对拖入对象做出处理; OnDragLeave 拖着被拖对象离开视图。 C++的代码写好了,但事情还没完,还必须在网页里用脚本对拖放事件进行处理, 即页面里哪个元素要接受拖放对象哪个元素就要处理ondragenter、ondragover、ondrop,代码其实很简单,让事件的返回值为false即可,这样 C++的代码才有机会处理拖放事件,代码如下: ......

 ......   如果要使整个视图都接受拖放,则在Body元素中处理此三个事件。 注意:别忘了让工程对OLE的支持即在初始化应用程序时调用AfxOleInit()。
怎样禁止网页元素的选取
  用网页做界面时多数情况下是不希望网页上的元素是能够被鼠标选中的, 要使网页元素不能被选中做法是:给浏览器的“宿主信息标记”加上DOCHOSTUIFLAG_DIALOG标记。
“宿主信息标记”用N个标记位来控制浏览器的许多性质,如: 禁用浏览器的3D的边缘; 禁止滚动条; 禁用脚本; 定义双击处理的方式; 禁用浏览器的自动完成功能; ...... 更多详情请参考MSDN的DOCHOSTUIFLAG帮助。
怎样修改“宿主信息标记”?
在CDocHostSite中实现IDocHostUIHandler, 在GetHostInfo方法中调用浏览器的OnGetHostInfo虚函数,在虚函数OnGetHostInfo中便可指定“宿主信息标记”,如:
HRESULT CLhpHtmlView::OnGetHostInfo(DOCHOSTUIINFO * pInfo) { pInfo->cbSize = sizeof(DOCHOSTUIINFO); pInfo->dwFlags = DOCHOSTUIFLAG_DIALOG | DOCHOSTUIFLAG_THEME | DOCHOSTUIFLAG_NO3DBORDER | DOCHOSTUIFLAG_SCROLL_NO; pInfo->dwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT;
return S_OK;
} 用脚本也可实现: 在Head中加入脚本: document.onselectstart=new Function(''return false''); 或者

其它
在CLhpHtmlView中还提供了几个函数, 修改网页元素的内容: BOOL PutElementHtml(CString ElemID,CString Html); 取表单元素的值: BOOL GetElementValue(CString ElemID,CString& Value); 设置表单元素的值: BOOL PutElementValue(CString ElemID,CString Value); 给表单元素设置焦点: void ElementSetFocus(CString EleName);        

以上就介绍了VC/MFC的学习,希望对VC/MFC有兴趣的朋友有所帮助。了解更多内容,请关注职坐标编程语言VC/MFC频道!


本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程