首先我们必须了解wxWidgets框架有自己的消息处理体系,WTL也有自己的消息处理体系,现在我们是wxWidgets wrap wtl,主消息循环是wxwidgets的,我们可以通过一个线程启动wtl消息循环,我们看下atlapp.h里面CAppModule一个函数的代码就会了解:
BOOL AddMessageLoop(CMessageLoop* pMsgLoop)
{
CStaticDataInitCriticalSectionLock lock;
if(FAILED(lock.Lock()))
{
ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::AddMessageLoop.\n"));
ATLASSERT(FALSE);
return FALSE;
}
ATLASSERT(pMsgLoop != NULL);
ATLASSERT(m_pMsgLoopMap->Lookup(::GetCurrentThreadId()) == NULL); // not in map yet
BOOL bRet = m_pMsgLoopMap->Add(::GetCurrentThreadId(), pMsgLoop);
lock.Unlock();
return bRet;
}
再看WTL SDK sample中例子工程TabBrowser里面的启动函数:
int Run(LPTSTR /*lpstrCmdLine*/ = NULL, int nCmdShow = SW_SHOWDEFAULT)
{
CMessageLoop theLoop;
_Module.AddMessageLoop(&theLoop);
CMainFrame wndMain;
if(wndMain.CreateEx() == NULL)
{
ATLTRACE(_T("Main window creation failed!\n"));
return 0;
}
wndMain.ShowWindow(nCmdShow);
int nRet = theLoop.Run();
_Module.RemoveMessageLoop();
return nRet;
}
标注红色的第一行代码表示wtl的消息循环是线程安全的,也就是说我们不能从wxwidgets主线程中直接使用wtl体系里面的UI。另外又由于CAppModule派生于ATL::CComModule,我们把有关WTL的功能封装成一个独立的DLL。
下面我们介绍wrap wtl browser的详细步骤:
我们先新建一个DLL工程,然后把WTL SDK sample中例子工程TabBrowser中的两个文件TabBrowser.cpp和BrowserView.h拷贝过来使用,注意,我们要继续使用CBrowserView来实现browser的视图展现。
1、设计dll接口
1)启动wtl消息循环接口
该接口实现是创建一个线程,通过这个线程启动wtl消息循环,线程函数实现很简单,我们只要稍微修改一下TabBrowser.cpp里面的_tWinMain入口函数就可以了,首先,把函数名改掉如改为InitWTL,参数只保留第一个参数,如下:
int InitWTL(HINSTANCE hInstance)
{
HRESULT hRes = ::CoInitialize(NULL);
ATLASSERT(SUCCEEDED(hRes));
AtlInitCommonControls(ICC_COOL_CLASSES | ICC_BAR_CLASSES | ICC_USEREX_CLASSES);
hRes = _Module.Init(NULL, hInstance);
ATLASSERT(SUCCEEDED(hRes));
AtlAxWinInit();
int nRet = Run();
_Module.Term();
::CoUninitialize();
return nRet;
}
当然。Run函数也要修改一下,如下:
static CBrowserView* G_View= NULL;
int Run(LPTSTR /*lpstrCmdLine*/ = NULL, int nCmdShow = SW_SHOWDEFAULT)
{
CMessageLoop theLoop;
_Module.AddMessageLoop(&theLoop);
G_View = new CBrowserView;
G_View->Create(NULL, rcDefault, NULL, WS_DISABLED);
int nRet = theLoop.Run();
_Module.RemoveMessageLoop();
return nRet;
}
注意红色字体的部分,我们通过这个隐藏的window来实现与wxwidgets交互的消息。
另外要注意CBrowserView要添加一个基类CMessageFilter,OnCreate函数里面添加如下代码:
// register object for message filtering and idle updates
CMessageLoop* pLoop = _Module.GetMessageLoop();
ATLASSERT(pLoop != NULL);
pLoop->AddMessageFilter(this);
OnDestroy函数里面添加如下代码:
CMessageLoop* pLoop = _Module.GetMessageLoop();
ATLASSERT(pLoop != NULL);
pLoop->RemoveMessageFilter(this);
该接口调用可以放在dll attach的时候,也可以在wxwidgets程序里面调用。
2)创建browser的接口
首先该接口要有一个参数就是HWND类型的,作为我们new CBrowserView 的父窗口句柄;第二个参数就是RECT类型的,视图的位置大小;第三个参数就是URL,我们要打开的网页的地址。有这三个参数就足够了,当然你也可以添加其他参数如果必要。
该函数的实现要注意了,由于我们是要new一个CBrowserView ,该函数要有wxwidgets程序调用,所以我们不能直接在该函数里面New,要通过发送windows的消息,首先我们在BrowserView.h里面自定义一个windows消息(先前已经有两个自定义消息了):
#define WM_BROWSERSCREATE (WM_APP + 3)
接口实现如下:
void CreateWtlBrowser(HWND parent, RECT& rect, LPCTSTR strURL)
{
if (G_View && G_View->IsWindow())
{
WTLPARAMINFO *info = new WTLPARAMINFO;//我们定义的一个传递参数的结构体,使用后不要忘记delete
info->m_hWnd = parent;
info->rect = rect;
info->m_url = strURL;
::PostMessage(G_View->m_hWnd, WM_BROWSERSCREATE, (WPARAM)info ,0);//注意,此处只能用Post,不能用send,否则等于是直接调用了
}
}
然后我们在BrowserView.h里来添加实现WM_BROWSERSCREATE消息的映射函数就可以了,如:
LRESULT OnBrowserCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
WTLPARAMINFO *info = (WTLPARAMINFO *)wParam;
if (info)
{
CBrowserView* pView = new CBrowserView;
pView->Create(info->m_hWnd, info->rect, info->m_url, WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL);
}
bHandled=FALSE;
return 1;
}
实际上,在wxwidgets程序里面,我们仅仅知道一个父窗口的句柄,其他都封装在了一个DLL里面,wxwidgets里面对browser的所有操作只能通过这个窗口句柄,所以我们要在DLL里面保持一个hash表,用来可以通过这个消息句柄找到对应的CBrowserView实例。
OK了,其他诸如重新刷新窗口大小,前进,后退,刷新,停止等接口照着这个接口实现就可以了。
2、消息接口设计
这个消息接口指的是从dll里面反映到wxwidgets程序里面的消息,例如浏览器状态,标题变化及NewWindow事件等的通知。当然,这个实现我们也通过自定义windows消息,通过在wxWidgets里面接收消息就OK了。
介绍结束,只要注意关键的几个点就不会有难度了!