本文转自tingsking18。
做界面业务处理的人员,经常会遇到的一个问题,在数据处理过程中需要更新界面。但是这时候如果用户同时在操作界面的话,就会产生莫名其妙的异常了。这就是由于windows用户控件不是线程安全的造成,拥有同样问题的另一个问题是在微软提供的GDI+,在使用IMage对象时,也会经常出现莫名其妙的异常。该异常也是由于GDI+的内核对象不是线程安全所导致。
我们一般的做法都是,在我们的数据线程中向主界面线程中投递消息。让所有对控件的操作都由主线程一个线程执行。这样线程安全问题就被解决了。今天翻看网页突然发现另外一种解决方案,让人虎躯一震。哈哈!转你。。。
1. 定义消息处理函数和SetWindowLong返回值
static LRESULT CALLBACK PluginWinProc(HWND, UINT, WPARAM, LPARAM);
long OldProc;
2. 在Button1的处理函数中创建线程
void CMyDlg::OnButton1()
{
HWND h = m_btn2.m_hWnd;
OldProc = SetWindowLong(h,GWL_WNDPROC,long(PluginWinProc));
_beginthread(ThreadStart1,0,(void *)(&m_btn2));
}
3. 还原系统消息过程
void CMyDlg::OnDestroy()
{
CDialog::OnDestroy();
SetWindowLong(m_btn2.m_hWnd,GWL_WNDPROC,OldProc);
}
4. 自定义消息过程函数
static LRESULT CALLBACK PluginWinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case MYMSG:
{
CWnd *wnd = CWnd::FromHandle(hwnd);
wnd->SetWindowText("aaaaaaa");
}
break;
default:
break;
}
return CallWindowProc((WNDPROC)OldProc,hwnd,msg,wParam,lParam);
}
代码都非常简单。关键的是SetWindowLong这个函数,这个函数的作用是设定窗口属性。GWL_WNDPROC 为窗口过程设置新地址。下面是MSDN中关于SetWindowLong和GWL_WNDPROC的说明:
若使用SetWindowLong函数和GWL_WNDPROC索引替换窗口过程,则给定的窗口过程必须遵循WindowProc回调函数的说明中指定的准则。
使用GWL_WNDPROC索引调用SetWindowLong函数可创建该窗口类的子类(窗口类用来创建窗口)。应用程序不得用另一个过程的窗口产生子类。
当然要在使用完后在用SetWindowLong原来的窗口过程设置回去。使用这种方法可用于修改你无法更改代码的类,可以重写他的消息处理函数。
点评:我对作者的最后一句话很是欣赏。想获取一个任务的句柄并不难,API提供了很多的这样的函数。如果能够将一个任务的消息过程拦截了,将它里面一些关键的消息进行重写。那么修改窗体样式,颜色,等等这样的功能,岂不是很容易实现。