目前我所知的,能防假死的浏览器有:Chrome,Maxthon,Theworld,搜狗.除了Chrome外,其余的都是IE内核,它们的实现方式都是一样的,页面窗口与主程序窗口分离,每个页面一个线程.事实上,除了Chrome能完美实现防假死,其余都有瑕疵.
瑕疵有3
- 点击主窗口的时候,发现页面失去了输入焦点
- 按alt+tab键切换窗口的时候,发现第一次不能成功,按两次才行
- 弹出的模态窗口并不模态,主窗口仍能动,其他页面也能动
造成这些瑕疵,还是因为页面窗口与主程序窗口分离,问题是为什么不能在同一个窗口呢?实验证明,放在同一个窗口,即使是不同进程,子窗口处于假死状态的话,主窗口也同样假死了.
这一周,我一直在尝试一个事情,子窗口假死的时候,主窗口能否不死,两个窗口是处于不同线程.
测试内容如下:有窗口A和窗口B,A是由主线程创建,然后创建一个线程,再这个线程上创建B,CreateWindow的hWndParent参数是A的hwnd.显然A,B都有各自的消息循环,但当B执行一个死循环的时候,A也死掉,没有反应了,但如果B的hWndParent是NULL的话,当B无响应时,A仍然能活动自如.于是猜测是当两个窗口处于父子关系时,它们的消息循环是否合并了?
但,后来翻遍了资料,都没有讲两个不同线程的子父窗口的消息循环是怎样的.
最后发现在B执行死循环操作的时候,如果添加PeekMessage函数,那么A是可以正常响应的.PeekMessage是什么?就是在消息队列中把消息提出来,可以把消息清除,也可以不清除(使用PM_REMOVE 或 PM_NOREMOVE)标记.但如果不清除,A仍然是不能响应.于是可以猜测,当这两个窗口处于父子关系的时候,主进程会先将它们的消息放在同一队列中,然后再分发给各自的线程,所以当B堵塞的时候,A就无法响应,只有将属于B的消息都清除掉,A的消息才能到达A.
确实是这样吗?找不到资料,不能确定.
如果猜测是正确的,那么就要想办法将B的消息从消息队列中清除掉,而这个操作只能在另一个线程中,但PeekMessage做的只是当前线程的,也没有别的函数可以做到.
看来,目前来说,实验结果是不成功.
其实也是,Maxthon,Theworld,搜狗都不是省油的灯,他们肯定也想过,不得已才采用这个"分离"的办法.但为何Chrome能做到?不要忘了,那可是google的孩子,另外Chrome采用的是开源的核心,在处理这个事情上更方便.
最后附上测试代码,很混乱,见谅.
// MyWin.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include "resource.h"
#include <windows.h>
#define szClassMain "MainWClass"
#define szClassChild "ChildWClass"
// Global variable
HINSTANCE hinst;
HWND hParent;
HWND hChild,hChild2;
HANDLE hTh1;
DWORD hThID1;
HHOOK hMsgHook;
// Function prototypes.
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
InitApplication(HINSTANCE);
InitInstance(HINSTANCE, int);
LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK ChildWndProc(HWND, UINT, WPARAM, LPARAM);
BOOL WINAPI ThreadProc(HWND);
BOOL WINAPI ThreadProc2(HWND);
LRESULT CALLBACK GetMsgProc(int,WPARAM ,LPARAM );
// Application entry point.
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
hinst=hinstance;
if (!InitApplication(hinstance))
return FALSE;
if (!InitInstance(hinstance, nCmdShow))
return FALSE;
DWORD thid;
hTh1=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadProc,hParent,0,&thid);
hThID1=thid;
HANDLE hTh2=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadProc2,hParent,0,&thid);
while (int sc=GetMessage(&msg, (HWND) NULL, 0, 0) != 0 && sc != -1)
{
if(!SendMessageTimeout(hChild,WM_USER+100,0,0,0,200,NULL))
MessageBox(0,"fat","fat",MB_OK);
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
UNREFERENCED_PARAMETER(lpCmdLine);
}
BOOL InitApplication(HINSTANCE hinstance)
{
WNDCLASSEX wcx;
// Fill in the window class structure with parameters
// that describe the main window.
wcx.cbSize = sizeof(WNDCLASSEX); // size of structure
wcx.style = CS_HREDRAW | CS_VREDRAW;// redraw if size changes
wcx.lpfnWndProc = (WNDPROC)MainWndProc; // points to window procedure
wcx.cbClsExtra = 0; // no extra class memory
wcx.cbWndExtra = 0; // no extra window memory
wcx.hInstance = hinstance; // handle to instance
wcx.hIcon =NULL;// LoadIcon(NULL,
//IDI_APPLICATION); // predefined app. icon
wcx.hCursor = NULL; // predefined arrow
wcx.hbrBackground = (HBRUSH)COLOR_WINDOW; // white background brush
wcx.lpszMenuName = NULL;//"MainMenu"; // name of menu resource
wcx.lpszClassName = szClassMain; // name of window class
wcx.hIconSm = NULL;
// Register the window class.
RegisterClassEx(&wcx);
wcx.lpfnWndProc = (WNDPROC)ChildWndProc; // points to window procedure
wcx.lpszClassName = szClassChild;
wcx.lpszMenuName=(LPCSTR)IDR_MENU1;
return RegisterClassEx(&wcx);
}
BOOL InitInstance(HINSTANCE hinstance, int nCmdShow)
{
HWND hwnd;
// Save the application-instance handle.
//hinst = hinstance;
// Create the main window.
hwnd = CreateWindow(
"MainWClass", // name of window class
"Sample", // title-bar string
WS_OVERLAPPEDWINDOW , // top-level window
CW_USEDEFAULT, // default horizontal position
CW_USEDEFAULT, // default vertical position
400, // default width
300, // default height
(HWND) NULL, // no owner window
(HMENU) NULL, // use class menu
hinstance, // handle to application instance
(LPVOID) NULL); // no window-creation data
hParent=hwnd;
if (!hwnd)
return FALSE;
// Show the window and send a WM_PAINT message to the window
// procedure.
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
return TRUE;
}
LRESULT CALLBACK MainWndProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam) // second message parameter
{
switch (uMsg)
{
case WM_DESTROY:
// Clean up window-specific data objects.
PostQuitMessage(0);
break;
//
// Process other messages.
//
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK ChildWndProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam) // second message parameter
{
BOOL fDone=false;
MSG msg;
switch (uMsg)
{
case WM_COMMAND:
if(hwnd==hChild)
{
if(wParam==IDM_Test)
while(!fDone)
{
//while (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE))
//{
/*switch(msg.message)
{
case WM_KEYDOWN:
fDone=true;
}*/
//}
}
}
else if(hwnd==hChild2)
{
switch(wParam)
{
case IDM_Test:
SuspendThread(hTh1);
break;
//hMsgHook=SetWindowsHookEx(WH_GETMESSAGE,GetMsgProc,0,hThID1);
/*if(SendMessageTimeout(hChild,WM_USER+100,0,0,0,1000,NULL))
MessageBox(0,"sus","sus",MB_OK);
else
{
MessageBox(0,"fat","fat",MB_OK);
if(SuspendThread(hTh1))MessageBox(0,"s s","ss",MB_OK);
}*/
case IDM_Resume:
ResumeThread(hTh1);
break;
}
}
break;
case WM_DESTROY:
// Clean up window-specific data objects.
PostQuitMessage(0);
break;
//
// Process other messages.
//
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
BOOL WINAPI ThreadProc(HWND hParent)
{
HWND hwnd;
hwnd = CreateWindow(
szClassChild, // name of window class
"Child", // title-bar string
WS_OVERLAPPEDWINDOW , // top-level window
CW_USEDEFAULT, // default horizontal position
CW_USEDEFAULT, // default vertical position
400, // default width
300, // default height
(HWND) hParent, // no owner window
(HMENU) NULL, // use class menu
hinst, // handle to application instance
(LPVOID) NULL); // no window-creation data
hChild=hwnd;
if (!hwnd)
return FALSE;
// Show the window and send a WM_PAINT message to the window
// procedure.
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
MSG msg;
while (int sc=GetMessage(&msg, (HWND) NULL, 0, 0) != 0 && sc != -1)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return TRUE;
}
BOOL WINAPI ThreadProc2(HWND hParent)
{
HWND hwnd;
hwnd = CreateWindow(
szClassChild, // name of window class
"Child2", // title-bar string
WS_OVERLAPPEDWINDOW , // top-level window
CW_USEDEFAULT, // default horizontal position
CW_USEDEFAULT, // default vertical position
400, // default width
300, // default height
(HWND) NULL, // no owner window
(HMENU) NULL, // use class menu
hinst, // handle to application instance
(LPVOID) NULL); // no window-creation data
hChild2=hwnd;
if (!hwnd)
return FALSE;
// Show the window and send a WM_PAINT message to the window
// procedure.
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
MSG msg;
while (int sc=GetMessage(&msg, (HWND) NULL, 0, 0) != 0 && sc != -1)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return TRUE;
}
LRESULT CALLBACK GetMsgProc(
int code, // hook code
WPARAM wParam, // removal option
LPARAM lParam // message
)
{
MSG *msg=(MSG *)lParam;
if(msg->message==WM_KEYDOWN)
{
int i=1;
}
return CallNextHookEx(hMsgHook,code,wParam,lParam);
}