通过上一篇文章,已经可以浏览网页了,但有一个问题,就是那最大化对话框后,网页的浏览区域并没有变,如何让浏览区域变化呢?
我参考了两篇文章,一篇是(http://blog.csdn.net/cyloser/article/details/49734559),另一篇是(http://www.heycode.com/a13696.html)。感谢这两篇文章的作者,没有这两篇文章,我估计还在纠结为会么CefSize::Set(…)函数为什么不能改变窗口大小呢!当然如果有人知道如何使用Set()函数,请不吝赐教。
我们知道,窗口大小的改变通常会产生WM_SIZE消息。那么首先应该在对话框中添加OnSize函数。此外,要控制浏览器窗口,必须要获得其句柄或指针,如何获得呢?这就是本文主要讲解的内容。
在(一)中搭建CEF用到了cefsimple示例中的五个文件,这五个文件可以实现最基本的功能。为了探究cefsimple如何完成窗口缩放的功能,我尝试在整个cefsimple中搜索WM_SIZE,然而并没有搜到。由于时间问题 ,我没有去分析cefsimple的源码,而是去cefclient项目的去找答案。在cefclient项目,很容易就在cefclient_win.cpp中搜到了,通过代码分析,将主要代码写在下方:
if (!g_handler.get())
break;
CefRefPtr browser=g_handler->GetBrowser();
if (browser/*g_handler->GetBrowser()*/)
{
// Retrieve the window handle (parent window with off-screen rendering).
CefWindowHandle hwnd =g_handler->GetBrowser()->GetHost()->GetWindowHandle();
if (hwnd)
{
// Resize the window and address bar to match the new frame size.
RECT rect;
GetClientRect(hWnd, &rect);
rect.top += URLBAR_HEIGHT;
......
}
}
上面这段代码说明了什么呢?首先检查g_handler.get()是否为0,为0就直接退出了WM_SIZE的消息处理了。在为非0的情况下,获得浏览器的智能指针(CefRefPtr),并对其进行判断。在browser非零的情况下才能获得浏览窗口的句柄,然后根据客户的界面进行更改。
可能你会问:g_handler是什么变量?对g_handler进行追踪,发现这是一个在cefclient.cpp中定义的全局变量,CefRefPtr g_handler;。所以在完全按照前一篇文章是不行的,应将app,handler的声明为全局变量,并分别在构造函数和OnInitDialog中进行初始化,模板分别为SimpleApp、SimpleHandler。然而就这样还是不能使用,因为g_handler并没有GetBrowser() 这样的成员函数,怎么办?还得回到cefclient项目中寻找答案。
在client_handler.h和client_handler.cpp中找到了GetBrowser()的声明和定义。
.h
// Lock used to protect members accessed on multiple threads. Make it mutable
// so that it can be used from const methods.
mutable base::Lock lock_;
// The child browser window.
CefRefPtr browser_;
CefRefPtr GetBrowser() const;
----------
.cpp
CefRefPtr ClientHandler::GetBrowser() const
{
base::AutoLock lock_scope(lock_);
return browser_;
}
得到了GetBrowser的声明和定义,问题又来了,lock_和browser_是什么鬼?然而就这么用到你的工程中还是不是的,否则就会出现如下错误提示:
通过对cefclient中lock_的类型base::Lock分析,发现这是在include/base/cef_lock.h中定义的,所以在handler.h中务必要加上#include “include/base/cef_lock.h”。请注意,不要不加思考地对代码进行复制!!,ClientApp,ClientHandler这些类名一定要改成自已项目中定义的。
现在完成对OnSize()函数的编写,代码如下:
void CceftestDlg::OnSize(UINT nType, int cx, int cy)
{
CDialogEx::OnSize(nType, cx, cy);
if(g_handler!=NULL)
{
CefRefPtr<CefBrowser> browser=g_handler->GetBrowser();
if(browser)
{
CefWindowHandle hwnd = browser->GetHost()->GetWindowHandle();
::MoveWindow(hwnd,0,0,cx,cy,TRUE); //因为浏览器对于对话框是子窗口,所以浏览器的左上角坐标是相于父窗口的客户区的左上角而言的
}
}
}
编译一下,通过了。运行,发现居然还是老样子。怎么回事?!只好对OnSize进行断点调试,发现if(browser)的判断体始终没有执行。不信邪的我回到cefclient项目进行WM_SIZE处进行断点调试,发现browser_非0。于是我意识到我一定在SimpleHandler.cpp中漏掉了对browser_的一些操作。在cefclient项目的cef_handler.cpp中搜索对browser_的操作,找到了两处,分别是在OnAfterCreated和OnBeforeClose,一个是在创建之后,一个是在关闭之前,刚后形成互补,代码如下:
void ClientHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
......
if (!GetBrowser()) {
base::AutoLock lock_scope(lock_);
// We need to keep the main child window, but not popup windows
browser_ = browser;
browser_id_ = browser->GetIdentifier();
}
......
}
void ClientHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser)
{
......
if (GetBrowserId() == browser->GetIdentifier()) {
{
base::AutoLock lock_scope(lock_);
// Free the browser pointer so that the browser can be destroyed
browser_ = NULL;
}
......
}
由于只有一个浏览器窗口,所以关于窗口ID的操作就可以无视了。简化代码后如下:
void SimpleHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser)
{
......
if (!GetBrowser())
{
base::AutoLock lock_scope(lock_);
// We need to keep the main child window, but not popup windows
browser_ = browser;
}
......
}
void SimpleHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser)
{
......
base::AutoLock lock_scope(lock_); //这一句包括下一句一定要有,否则就会在退出程序时报错,进程无法释放
// Free the browser pointer so that the browser can be destroyed
browser_ = NULL;
......
}
后记:
我自己在处理对browser_的一些补全操作时,第一次只在OnAfterCreated中加了相关代码,没有意识到要在OnBeforeClosed中加,结果也成功达到了目的,但是会出现一个奇怪的问题,就是在退出时会报错。花了好长时间才明白browser_出现在OnAfterCreated和OnBeforeClosed中是有原因的,一个初始化,一个销毁,官方在注释中也说明了这一点。