VC浏览器的定制与扩展
前言
由于本人在开发中经常要在程序中嵌入浏览器,为了符合自己的需求经常要对浏览器进行扩展和定制, 解决这些问题需在网上找资料和学习的过程,我想可能很多开发者或许会遇到同样的问题,特写此文,以供大家参考。
在MFC中使用浏览器
在MFC中微软为我们提供了CHtmlView、CDHtmlDialog类让我们的程序很方便的嵌入浏览器和进行浏览器的二次开发,这比直 接使用WebBrowser控件要方便很多,所以本文中讨论的浏览器的问题都是针对CHtmlView来讨论的。文中将提到一个类 CLhpHtmlView, 它是CHtmlView的派生类,文中提及的扩展或定制都将在CLhpHtmlView类(或派生类)上实现。
怎样扩展或定制浏览器
浏览器定义了一些扩展接口(如IDocHostUIHandler可以定制浏览器界面有关的行为),以便开发者进行定制和扩展。浏览 器会在需要的时候向他的控制站点查询这些接口,在控制站点里实现相应的接口就可以进行相应的扩展。在MFC7.01类 库中,CHtmlView使用的控制站点是CHtmlControlSite的,在CHtmlControlSite类中 只实现了接口IDocHostUIHandler, 而要实现更多的扩展接口,必须用自定义的控制站类来取代CHtmlControlSite,在下文中提及的类CDocHostSite即为自定义 的控制站类。
关于接口的介绍请参考:
http://dev.csdn.net/develop/article/48/48483.shtm
如何使自定义的控制站点来替换默认的控制站点呢?在MFC7.0中只需重载CHtmlView的虚函数CreateControlSite即可:
BOOL CLhpHtmlView::CreateControlSite(COleControlContainer * pContainer, COleControlSite ** ppSite, UINT /*nID*/, REFCLSID /*clsid*/){ *ppSite = new CDocHostSite(pContainer, this);// 创建自己的控制站点实例 return (*ppSite) ? TRUE : FALSE;}
VC6.0要替换控制站要复杂的多,这里就不讨论了,如需要6.0版本的请给我发邮件到yourshine@21cn.com。
定制鼠标右键弹出出菜单
要定制浏览器的鼠标右键弹出菜单,必须在自定义的控制站点类中实现IDocHostUIHandler2接口,并且IE的 版本是5.5或以上。在接口IDocHostUIHandler2的ShowContextMenu方法中调用浏览器类的OnShowContextMenu虚函数,我们 在浏览器类的派生类重载此虚函数即可实现右键菜单的定制,参见代码
HRESULT CDocHostSite::XDocHostUIHandler::ShowContextMenu(DWORD dwID, POINT * ppt, IUnknown * pcmdtReserved, IDispatch * pdispReserved){ METHOD_PROLOGUE(CDocHostSite, DocHostUIHandler); return pThis->m_pView->OnShowContextMenu( dwID, ppt, pcmdtReserved,pdispReserved );} HRESULT CLhpHtmlView::OnShowContextMenu(DWORD dwID, LPPOINT ppt, LPUNKNOWN pcmdtReserved, LPDISPATCH pdispReserved){ HRESULT result = S_FALSE; switch(m_ContextMenuMode) { case NoContextMenu: // 无菜单 result=S_OK; break; case DefaultMenu: // 默认菜单 break; case TextSelectionOnly: // 仅文本选择菜单 if(!(dwID == CONTEXT_MENU_TEXTSELECT || dwID == CONTEXT_MENU_CONTROL)) result=S_OK; break; case CustomMenu: // 自定义菜单 if(dwID!=CONTEXT_MENU_TEXTSELECT) result=OnShowCustomContextMenu(ppt,pcmdtReserved,pdispReserved); break; } return result;}
在CLhpHtmlView中定义的枚举类型CONTEXT_MENU_MODE举出了定制右键弹出菜单的四种类型
enum CONTEXT_MENU_MODE // 上下文菜单{ NoContextMenu, // 无菜单 DefaultMenu, // 默认菜单 TextSelectionOnly, // 仅文本选择菜单 CustomMenu // 自定义菜单};
通过CLhpHtmlView的函数SetContextMenuMode来设置右键菜单的类型。如果设定的右键弹出菜单是“自定义菜单”类型, 我们只要在CLhpHtmlView的派生类中重载OnShowCustomContextMenu虚函数即可,如下代码 CDemoView是CLhpHtmlView的派生类
HRESULT CDemoView::OnShowCustomContextMenu(LPPOINT ppt, LPUNKNOWN pcmdtReserved,LPDISPATCH pdispReserved){ if ((ppt==NULL)||(pcmdtReserved==NULL)||(pcmdtReserved==NULL)) return S_OK; HRESULT hr=0; IOleWindow *oleWnd=NULL; hr=pcmdtReserved->QueryInterface(IID_IOleWindow, (void**)&oleWnd); if((hr != S_OK)||(oleWnd == NULL)) return S_OK; HWND hwnd=NULL; hr=oleWnd->GetWindow(&hwnd); if((hr!=S_OK)||(hwnd==NULL)) { oleWnd->Release(); return S_OK; } IHTMLElementPtr pElem=NULL; hr = pdispReserved->QueryInterface(IID_IHTMLElement, (void**)&pElem); if(hr != S_OK) { oleWnd->Release(); return S_OK; } IHTMLElementPtr pParentElem=NULL; _bstr_t tagID; BOOL go=TRUE; pElem->get_id(&tagID.GetBSTR()); while(go && tagID.length()==0) { hr=pElem->get_parentElement(&pParentElem); if(hr==S_OK && pParentElem!=NULL) { pElem->Release(); pElem=pParentElem; pElem->get_id(&tagID.GetBSTR()); } else go=FALSE; }; if(tagID.length()==0) tagID="no id"; CMenu Menu,SubMenu; Menu.CreatePopupMenu(); CString strTagID = ToStr(tagID); if(strTagID == "red") Menu.AppendMenu(MF_BYPOSITION, ID_RED, "您点击的是红色"); else if(strTagID == "green") Menu.AppendMenu(MF_BYPOSITION, ID_GREEN, "您点击的是绿色"); else if(strTagID == "blue") Menu.AppendMenu(MF_BYPOSITION, ID_BLUE, "您点击的是蓝色"); else Menu.AppendMenu(MF_BYPOSITION, ID_NONE, "你点了也白点,请在指定的地方点击"); int MenuID=Menu.TrackPopupMenu(TPM_RETURNCMD|TPM_LEFTALIGN|TPM_RIGHTBUTTON,ppt->x, ppt->y, this); switch(MenuID) { case ID_RED: MessageBox("红色"); break; case ID_GREEN: MessageBox("红色"); break; case ID_BLUE: MessageBox("红色"); break; case ID_NONE: MessageBox("haha"); break; } oleWnd->Release(); pElem->Release(); return S_OK;}
实现脚本扩展(很重要的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--------------------------------------------------------------------------- ...... οnclick="external.WobbleWnd()" ......
这里我要介绍一下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 2213HRESULT 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.0Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, /application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*Referer: http://localhostAccept-Language: en-usUser-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: localhostConnection: 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("http://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++的代码才有机会处理拖放事件,代码如下:
......<td οndragenter="event.returnValue = false" οndragοver="event.returnValue = false" /οndrοp="event.returnValue = false">......
如果要使整个视图都接受拖放,则在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'');
或者
<body onselectstart="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浏览器的定制与扩展相关推荐
- 去掉更新Chrome浏览器后工具栏的扩展程序图标
更新Chrome83版本后任务栏增加了一个扩展程序图标,关闭插件就自动消失,打开插件后会出现不能隐藏. 关闭方法: 在标签页面输入Chrome://flags 打开实验功能页面 找到 Extensio ...
- WEB之浏览器使用入门--chrome扩展插件安装及好用的扩展插件小集合
chrome插件 chrome本身是浏览器,提供最基本的谷歌提供的浏览器相关的功能.但是仅仅有基本的chrome本体在很多时候使用还是会感到力不从心,对浏览器本身通过安装一些常用的扩展(extenti ...
- 一个Google Chrome浏览器的英文字典扩展应用
作为程序猿经常需要阅读英文资料,有很多不认识的生词,我又懒得每次手动复制到金山词霸里,所以想找一个能直接在浏览器里查单词的扩展应用. 万能的Chrome再次没有辜负我的期望. 在chrome应用商店里 ...
- 浏览器提取已安装扩展教程
Google Chrome 打开扩展程序页 chrome://extensions/ 打开扩展安装路径 C:\Users\你的用户名\AppData\Local\Google\Chrome\User ...
- vc浏览器_自主创业项目推荐,晨兴资本刘芹:我的市场非共识+超配投资原则VC洞见...
我每天都对风险投资有所了解 破坏性创新隐含在科技投资中.生物进化反映了技术的发展,每一代公司都需要创造新的商机.它必须摧毁并改变前几代人的技术观念,因此进化思维可以指导模棱两可的中小型公司的几代技术公 ...
- 谷歌chrome浏览器怎么导出打包扩展程序crx
背景 chrome浏览器中有很多有用的扩展程序, 但是可能因为长城防火墙我们不能直接去扩展程序商店进行下载获取, 如果我们自己电脑上已经通过某种方式添加了扩展程序, 想把它移植到其他没有翻墙或者压根没 ...
- HTML代码via浏览器logo,Via浏览器怎么定制首页 几步轻松完成
Via浏览器是一款大家常用的手机浏览器软件,大家可以使用软件上网浏览网页,但是现在的手机浏览器都长得差不多,尤其是首页,都是千篇一律单调乏味,而这款软件其实是可以自己设计首页,大家可以根据自己的喜好设 ...
- google浏览器Chrome离线安装扩展主题皮肤教程
由于国内不能打开Chrome应用商店,只能通过国内第三方网站,下载主题扩展程序包,来安装主题皮肤 1.百度搜索"google 皮肤" 下载主题皮肤到本地 本人使用主题网站:http ...
- Android chrome浏览器的定制
最近在做海外项目需要定制chrome浏览器的书签和主页,在8.1的项目上实现,当时手机还没有预制GMS,只能下载chrome 这个apk做实验,出现了很多问题,书签没有作用,安装了gms里面的chro ...
最新文章
- 初步了解:使用JavaScript进行表达式(De Do Do Do,De Da Da Da)
- 关系数据库概念辨析❤️
- Nginx配置——防盗链
- FPGA实现序列检测(训练testbench写法)
- python3字符编码str_聊聊Python 3 的字符串:str 和 bytes 的区别
- eclipse java 1.8 vm_GGTS(Eclipse)和JAVA 1.8中不兼容的JVM
- EMNLP2018论文解读 | 利用篇章信息提升机器翻译质量
- python学习笔记之迭代器
- 解决wordpress无法离线发布(远程发布)的故障
- bmp转换tiff c++代码_如何用Java语言将图像转换为PDF?Spire.PDF for Java轻松搞定
- linux没有那个文件或目录_Linux安装python faiss模块
- Map集合的遍历方法
- OpenMP简介和基本命令
- Maven工程 报 Diamond types are not supported at language level '5'
- 大数据推荐系统算法(1)大数据框架介绍
- Excel粘贴时如何跳过隐藏单元格
- WGS84地球坐标系,GCJ02火星坐标系,BD09百度坐标系简介与转换
- java集合转字符串 字符串转集合 StringUtils 一个强大的字符串操作工具
- 手机连接投影机的步骤_苹果手机如何连投影仪_苹果手机与投影仪连接的方法教程(步骤)...
- php博客系统答辩ppt,基于PHP实现的WEB图片共享系统-php(开题报告+源程序+论文+答辩PPT+文献综述)...
热门文章
- Java计算机毕业设计水果购物商城源码+系统+数据库+lw文档
- 强化学习1 高斯赛德尔迭代
- C#控制台实现停车管理系统
- stat() /root/xxx/index.html failed (13: Permission denied)
- 希腊字母读音及科学上的应用
- android img 文件解包
- 由海天瑞声支持,全球最大多领域英语开源数据集发布
- pygame--图片随键盘移动
- Assets file ‘E:\*\*\*\obj\project.assets.json‘ not found.Run a NuGet package restore to generate
- 谈谈WebService开发-基础篇