现今浏览器有很多种,其实大致可以分为两种,其一就是ie内核的浏览器,其二就是非ie内核的浏览器,前者是支持Activex控件的开发,而后者是支持NPAPI控件的开发,想要开发一个既可以支持ie内核的浏览器又支持非ie内核的浏览器控件,firebreath就是一个很好的选择,当然你们也可以开发两种对应不同的浏览器就好。
这里不叙述firebreath的安装和如何使用,网上有很多相关的信息。
安装好firebreath后,先要注册接口,这个接口就是网站向控件传递参数的函数,其实很简单,建立一个firebreath工程里面就有例子,依葫芦画瓢就好,值得注意的是,vs生成的dll是debug版本的,大小接近4MB,有些大,这时你可以把debug换成release的,设置好参数后,生产大小只有1.2MB左右,合适了。
这时我们的接口已经做好,我做了一个截屏的控件,先贴firebreath的部分代码:
.h文件
- #include <string>
- #include <sstream>
- #include <boost/weak_ptr.hpp>
- #include "JSAPIAuto.h"
- #include "BrowserHost.h"
- #include "GoldArmor.h"
- #include <wtypes.h>
- #ifndef H_GoldArmorAPI
- #define H_GoldArmorAPI
- class GoldArmorAPI : public FB::JSAPIAuto
- {
- public:
- GoldArmorAPI(const GoldArmorPtr& plugin, const FB::BrowserHostPtr& host) :
- m_plugin(plugin), m_host(host)
- {
- //绑定版本号
- registerMethod("getversion", make_method(this, &GoldArmorAPI::getversion));
- //绑定图片名和图片地址
- registerMethod("ReturnUrl", make_method(this, &GoldArmorAPI::ReturnUrl));
- //绑定Address
- registerMethod("SendAddress", make_method(this, &GoldArmorAPI::SendAddress));
- //绑定端口
- registerMethod("Setport", make_method(this, &GoldArmorAPI::Setport));
- //绑定Action
- registerMethod("SetAction", make_method(this, &GoldArmorAPI::SetAction));
- //绑定Parater
- registerMethod("SendParater", make_method(this, &GoldArmorAPI::SendParater));
- //绑定Cookies
- registerMethod("CookiesParatar", make_method(this, &GoldArmorAPI::CookiesParatar));
- //绑定截屏端口
- registerMethod("GetCatchScreenAndRM", make_method(this, &GoldArmorAPI::GetCatchScreenAndRM));
- }
- //析构函数
- virtual ~GoldArmorAPI(){;}
- //得到版本号
- FB::variant getversion();
- //得到图片名和图片地址
- void ReturnUrl();
- //设置Address
- void SendAddress(const FB::variant& address);
- //设置端口
- void Setport(const FB::variant& myport);
- //设置Action
- void SetAction(const FB::variant& myaction);
- //设置Parater
- void SendParater(const FB::variant& myparater);
- //设置Cookies
- void CookiesParatar(const FB::variant& mycookies);
- //设置截屏端口
- void GetCatchScreenAndRM();
- private:
- GoldArmorWeakPtr m_plugin;
- FB::BrowserHostPtr m_host;
- //需要保存传过来的数据,c++builder和vs中字符串的格式不一样,所以转换的时候可能有些麻烦。
- //我们直接用wchar_t*来保存数据。
- wchar_t *pictureurl, *picturename, *cookies, *actionparater, *transmitaddress, *port, *action;
- };
- #endif
- #include "JSObject.h"
- #include "variant_list.h"
- #include "DOM/Document.h"
- #include "global/config.h"
- #include "GoldArmorAPI.h"
- #include <atltime.h>
- #include <atlstr.h>
- #include "resource.h"
- #include <windows.h>
- BOOL CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);
- //图标句柄
- HICON hdicon = NULL;
- //返回版本号
- FB::variant GoldArmorAPI::getversion()
- {
- return 6300;
- }
- //得到图片名和图片地址
- void GoldArmorAPI::ReturnUrl()
- {
- CString temp;
- CTime start_time = CTime::GetCurrentTime();
- temp = start_time.Format(_T("%Y_%m_%d_%H_%M_%S")) + ".JPEG";
- this->picturename = temp.AllocSysString();
- //添加系统临时目录
- char *s;
- size_t len;
- errno_t err = _dupenv_s(&s, &len, "TEMP");
- if (err)
- {
- exit(-1);
- }
- CString url = s;
- url = url + "/" + temp;
- this->pictureurl = url.AllocSysString();
- }
- //设置Address
- void GoldArmorAPI::SendAddress(const FB::variant& address)
- {
- std::wstring _transmitaddress = address.convert_cast<std::wstring>();
- CString temp = _transmitaddress.c_str();
- this->transmitaddress = temp.AllocSysString();
- }
- //设置端口
- void GoldArmorAPI::Setport(const FB::variant& myport)
- {
- std::wstring _port = myport.convert_cast<std::wstring>();
- CString temp = _port.c_str();
- this->port = temp.AllocSysString();
- }
- //设置Action
- void GoldArmorAPI::SetAction(const FB::variant& myaction)
- {
- std::wstring _myaction = myaction.convert_cast<std::wstring>();
- CString temp = _myaction.c_str();
- this->action = temp.AllocSysString();
- }
- //设置Parater
- void GoldArmorAPI::SendParater(const FB::variant& myparater)
- {
- std::wstring _myparater = myparater.convert_cast<std::wstring>();
- CString temp = _myparater.c_str();
- this->actionparater = temp.AllocSysString();
- }
- //设置Cookies
- void GoldArmorAPI::CookiesParatar(const FB::variant& mycookies)
- {
- std::wstring _mycookies = mycookies.convert_cast<std::wstring>();
- CString temp = _mycookies.c_str();
- this->cookies = temp.AllocSysString();
- }
- //非模态对话框控件事件
- BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
- {
- switch(message)
- {
- case WM_INITDIALOG:
- {
- //这里加载窗口图标
- SendMessage(hDlg, WM_SETICON, ICON_SMALL, (LPARAM)hdicon);
- return TRUE;
- }
- case WM_COMMAND:
- {
- switch(wParam)
- {
- case IDOK:
- {
- if (hDlg)
- {
- PostQuitMessage(1);
- return TRUE;
- }
- }
- case IDCANCEL:
- {
- if (hDlg)
- {
- PostQuitMessage(0);
- return TRUE;
- }
- }
- }
- break;
- }
- case WM_ACTIVATE:
- {
- switch(wParam)
- {
- case WA_INACTIVE:
- {
- if (hDlg)
- {
- RECT myrerct;
- GetWindowRect(hDlg, &myrerct);
- SetWindowPos(hDlg, HWND_TOPMOST, myrerct.left, myrerct.top, myrerct.right - myrerct.left, myrerct.bottom - myrerct.top, SWP_NOMOVE | SWP_NOSIZE |SWP_NOACTIVATE);
- }
- break;
- }
- }
- break;
- }
- }
- return FALSE;
- }
- //设置截屏端口
- void GoldArmorAPI::GetCatchScreenAndRM()
- {
- //以下适用于谷歌浏览器等,但不适用于火狐.
- HWND isCatchOpen = FindWindow(_T("TCatchScreenForm"), _T("截屏控件"));
- if (IsWindowVisible(isCatchOpen))
- {
- return;
- }
- //适用于火狐的.
- HWND isTipsOpen = FindWindow(NULL, _T("Live800准备截屏"));
- if (isTipsOpen)
- {
- SetForegroundWindow(isTipsOpen);
- return;
- }
- CString mydll = _T("npScreenShot.dll");
- HMODULE hOcx = GetModuleHandle(mydll);
- wchar_t szPath[1024] = {
- '\0'
- };
- GetModuleFileName(hOcx, szPath, 1024);
- PathRemoveFileSpec(szPath);
- PathAppend(szPath, _T("CatchScreen.dll"));
- //这里苹果浏览器加载c++builder的dll会报错,然而加载c++的dll就不会报错.
- //这里造成的原因是c++builder的dll用到了vcl,而苹果浏览器是不支持vcl的.
- //对于某些电脑的浏览器这里需要放一个MessageBox才可以截屏,否则加载动态库的时候出错.
- //这里若用clr建立界面会出现问题,编译器编译不了,这里需要使用非模态对话框并置顶.
- MSG msg;
- hdicon = LoadIcon(hOcx, MAKEINTRESOURCE(IDI_CATCHSCREENICON));
- HWND hDialog = CreateDialog(hOcx, MAKEINTRESOURCE(IDD_CATCHSCREENTPS), NULL, DlgProc);
- while(GetMessage(&msg, NULL, 0, 0))
- {
- if(hDialog == NULL || !IsDialogMessage(hDialog, &msg))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- }
- if (msg.wParam == 1)
- {
- if(DestroyWindow(hDialog))
- {
- if (InvalidateRect(GetForegroundWindow(), NULL, TRUE))
- {
- if (UpdateWindow(GetForegroundWindow()))
- {
- if (SetForegroundWindow(GetForegroundWindow()))
- {
- SYSTEM_INFO si;
- GetNativeSystemInfo(&si);
- if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 || si.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_IA64)
- {
- Sleep(100);
- }
- HMODULE ShowCatchScreen = LoadLibrary(szPath);
- if (ShowCatchScreen)
- {
- void(WINAPI* CatchScreenPort)(wchar_t* pictureurl, wchar_t* picturename, wchar_t* cookies, wchar_t* actionparater, wchar_t* transmitaddress, wchar_t* port, wchar_t* action);
- CatchScreenPort = (void(WINAPI*)(wchar_t* pictureurl, wchar_t* picturename, wchar_t* cookies, wchar_t* actionparater, wchar_t* transmitaddress, wchar_t* port, wchar_t* action))GetProcAddress(ShowCatchScreen, "OpenCatchScreen");
- CatchScreenPort(pictureurl, picturename, cookies, actionparater, transmitaddress, port, action);
- }
- }
- }
- }
- }
- }
- else if (msg.wParam == 0)
- {
- DestroyWindow(hDialog);
- }
- }
.h文件很好理解,.cpp文件有几个地方需要注意,我依次向下说明:
(我的截屏按钮点击后会出现一个提示框,这时用户可以选择需要截屏的位置,点击开始截屏就好,我的考虑是若一个浏览器有两个标签,其中一个有截屏按钮,而需要我们截屏的区域在第二个标签,若我们点击截屏后就开始截屏,这时我们能截屏的区域只是第一个标签的界面。这个提示界面是win32做的非模态对话框,在我的代码就可以看出来,之所以选择win32而不选择其他,比如clr或者mfc,主要是因为这个工程不支持clr,我们在工程里面建立clr窗体,工程不能建立,若使用clr的dll,工程动态加载,可以实现打开窗口,但是一旦关闭窗口所有浏览器都会重新刷新页面,这个可能跟底层有关,我们不去深究。)
1,以下代码是修正Google浏览器bug的,这个bug是当用户连续点击截屏按钮时虽然当时只弹出一个提示框,但是当这个提示框关闭或者提示框中点击开始截屏按钮就会出现很多个截屏提示框,造成这个原因的是Google等浏览器用的是消息队列,也就是我们的n次点击都在这个队列里面,只有最先的那个结束了才会执行第二个,这样就造成连续点击截屏按钮出现的bug,我这里也只是解决了在提示框里面点击开始截屏弹出很多截屏提示框的问题,当关闭提示框我暂时没解决,有兴趣的朋友可以自己去研究。
- //以下适用于谷歌浏览器等,但不适用于火狐.
- HWND isCatchOpen = FindWindow(_T("TCatchScreenForm"), _T("截屏控件"));
- HWND isTipsOpen = FindWindow(NULL, _T("Live800准备截屏"));
4,以下一句话是加载截屏提示框对话框的标题图标。
- hdicon = LoadIcon(hOcx, MAKEINTRESOURCE(IDI_CATCHSCREENICON));
- HWND hDialog = CreateDialog(hOcx, MAKEINTRESOURCE(IDD_CATCHSCREENTPS), NULL, DlgProc);
很可悲的是当我们建立好后生成时rc文件却出现了问题,按理说不应该,可是还是出现了,原来我们编辑好后的rc文件不能被该工程很好的编译,只要修改rc文件就好,不要去注释掉rc中不能编译的代码,这样可能出现未知的错误。其实解决的办法很简单,这个时候的rc文件里面加载你的对话框资源和图标资源,你可以右键firebreathWin.rc文件,选择打开方式C++源代码编辑器,可以看到这个资源文件的代码,把这个文件的所有代码复制下来保存在一个记事本里面,然后退出vs编译器,把以上备份的rc文件覆盖你编辑的文件,再用C++源代码编辑器打开这个最原始的rc文件,和你复制下来的还有对话框和图标资源的rc文件进行对比,你就会发现问题了,是不是很简单,只要把开头的部分和结束的部分换成最原始rc文件的代码,中间的对话框和图标资源不变,这个问题就解决了。
6,细心的人就会发现我的非模态窗口摧毁是在点击开始截屏按钮或者关闭截屏提示框的时候执行的,而网站上的是在发送退出消息前,这是由于猎豹浏览器造成的,一部分系统的猎豹浏览器在退出时不能把下面的函数放在PostQuitMessage前面。
- DestroyWindow(hDialog);
7,最后一点,也是最重要的一点,要想你做的dll对所有浏览器都支持,那么firebreath做的dll必须np开头,否则不能支持所有浏览器,同时需要regsvr32这个dll。
好了截屏的接口做好了,接下来就是截屏的dll,这里可以用vs的atl,我用的是c++builder的vcl,这里不贴代码,直接上传项目。
链接地址:http://download.csdn.net/detail/kk618172/6788619
值得注意的是,在我的CatchScreenCtrl.cpp文件里面打开截屏窗口用的是myCatchScreenForm->Show(),而不是myCatchScreenForm->ShowModal(),这是由于我如果用第二个则火狐浏览器就会有问题,我不是很明白。大家可以看代码,在关键的地方我注释了的,比如QQ浏览器出现黑屏的情况。
好了截屏操作dll完了,该上传截屏图片到服务器,这里用的vs2010,这个也不贴代码,直接上传项目:
链接地址:http://download.csdn.net/detail/kk618172/6788651
值得注意的是,在代码里面我判断了上传的协议,这里需要截屏的接口传递过来。
好了上传文件dll也完了,最后一个的dll,是一个键盘钩子,也是我用c++builder做的,这里也不贴代码,直接上传项目:
链接地址:http://download.csdn.net/detail/kk618172/6788661
我说一下这个dll的作用,就是用户截屏时候对文字处理时,判断是否超出边界,这时就是否禁用键盘,也可以不要这个dll,若实现这个功能还有其他方式,望大家告诉我。
以下是测试html。
- <html>
- <head>
- <title>截图测试</title>
- </head>
- <body>
- <object id="plugin1" type="application/catchscreen-live800" width="0" height="0"></object>
- <a href="#" onclick="plugin1.getversion();plugin1.ReturnUrl();plugin1.SendAddress('www.live800.com');plugin1.Setport(':8080');plugin1.SetAction('/test1/ChaterServer');plugin1.SendParater('&rid=1232&visitorIDInSession=11');plugin1.CookiesParatar('');plugin1.GetCatchScreenAndRM()" >截屏</a>
- </body>
- </html>