通過上一篇文章,已經可以浏覽網頁了,但有一個問題,就是那最大化對話框後,網頁的浏覽區域并沒有變,如何讓浏覽區域變化呢?
我參考了兩篇文章,一篇是(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<CefBrowser> 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<CefBrowser> browser_;
CefRefPtr<CefBrowser> GetBrowser() const;
----------
.cpp
CefRefPtr<CefBrowser> 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,,,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中是有原因的,一個初始化,一個銷毀,官方在注釋中也說明了這一點。