终于到了期待已久的UI多线程编程了,这个例子的效果很好,从中也学到了一些东西,我曾经就没有想到过将一个窗口分成几个部分,然后分别用不同的线程去控制这些部分的显示,看了这个例子以后就知道了,是不是在重绘窗口的时候也是采用了这个办法呢?也许吧。先来看看这个例子的主界面:
值得一提的是,这是一个win32 application的例子,不过也许在这样的框架下实现这样的功能更加方便吧。从主界面的现实情况可以看出程序的大概流程,很明显,首先将主窗口分平均分为四个子窗口,然后四个子窗口应该分别有自己的窗口过程函数,这样,当主窗口接收到用户的某个消息响应的时候,也会分别给四个子窗口发送相应的消息,这样就完成了“父子同心”,协调工作了,下面还是来看看其中的关键代码所在。
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static char szAppName[] = "MultiThread" ;
HWND hwnd ;
MSG msg ;
WNDCLASSEX wndclass ;
wndclass.cbSize = sizeof (wndclass) ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
wndclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION) ;
RegisterClassEx (&wndclass) ;
hwnd = CreateWindow (szAppName, "多线程例子",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
这是主函数的代码,也是一般的创建一个新窗口的步骤,我觉得最值得一看的还是CreateWindow的各个参数,其中第4个和第5个是窗口显示的坐标位置,第6个和第7个是窗口的宽度和高度。
好了,下面再来看看主窗口的窗口过程函数吧:
// 窗口函数--创建各个子窗口
// -----------------------------------
LRESULT APIENTRY WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
static char *szChildClass[] = { "Child1", "Child2", "Child3", "Child4" } ;
static HWND hwndChild[4] ;
static WNDPROC ChildProc[] = { WndProc1, WndProc2, WndProc3, WndProc4 } ;
HINSTANCE hInstance ;
int i, cxClient, cyClient ;
WNDCLASSEX wndclass ;
switch (iMsg)
{
case WM_CREATE :
hInstance = (HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE) ;
wndclass.cbSize = sizeof (wndclass) ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = NULL ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.hIconSm = NULL ;
for (i = 0 ; i < 1 ; i++)
{
wndclass.lpfnWndProc = ChildProc[i] ;
wndclass.lpszClassName = szChildClass[i] ;
RegisterClassEx (&wndclass) ;
hwndChild[i] = CreateWindow (szChildClass[i], NULL,
WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE,
0, 0, 0, 0, hwnd, (HMENU) i, hInstance, NULL) ;
}
return 0 ;
case WM_SIZE :
cxClient = LOWORD (lParam) ;
cyClient = HIWORD (lParam) ;
for (i = 0 ; i < 4 ; i++)
MoveWindow (hwndChild[i], (i % 2) * cxClient / 2,
(i > 1) * cyClient / 2,
cxClient / 2, cyClient / 2, TRUE) ;
return 0 ;
case WM_CHAR :
if (wParam == ' ')// /x1B
DestroyWindow (hwnd) ;
return 0 ;
case WM_DESTROY :
PostQuitMessage(0) ;
for(int i=0;i<4;i++)
CloseHandle(hThread[i]);
return 0 ;
}
return DefWindowProc (hwnd, iMsg, wParam, lParam) ;
}
主窗口的窗口过程函数无非就是根据用户发送的消息作用于各个子窗口就可以了,比如说WM_CREATE的时候,创建主窗口的以后也需要接着继续创建各个子窗口,还是看看CreateWindow函数的参数,发现起始坐标位置和宽度、高度都指定为0了,为什么呢?答案还在响应WM_SIZE消息,主窗口使用了MoveWindow函数分别指定了各个子窗口应该在什么位置显示,显示的宽和高应该是多少。可以发现创建子窗口的同时也分别指定了子窗口的窗口过程函数,是通过一个函数指针数组指定的,这也是一个编程技巧吧,值得学习。其实后面的代码都已经非常简单了,无非就是在各个子窗口的窗口过程函数中开一个线程,做出相应的显示效果就可以了,来看看显示效果最花哨的第四个子窗口是怎么做的吧:
LRESULT APIENTRY WndProc4 (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
static PARAMS params ;
switch (iMsg)
{
case WM_CREATE :
params.hwnd = hwnd ;
params.cyChar = HIWORD (GetDialogBaseUnits ()) ;
hThread[3] = CreateThread(NULL,0,Thread4,¶ms,0,NULL);
SetThreadPriority(hThread[3],THREAD_PRIORITY_BELOW_NORMAL);
return 0 ;
case WM_SIZE :
params.cxClient = LOWORD (lParam) ;
params.cyClient = HIWORD (lParam) ;
return 0 ;
case WM_DESTROY :
params.bKill = TRUE ;
return 0 ;
}
return DefWindowProc (hwnd, iMsg, wParam, lParam) ;
}
这是窗口过程函数,在处理WM_CREATE消息的时候,开辟了一个新的线程,值得注意的是,由于线程需要在子窗口中进行绘制,但是线程并不知道该在什么地方进行绘制,怎么办呢?办法就是将相应的子窗口的一个位置参数传给线程,也就是代码中的params结构体了,穿过去以后,我们再来看看线程里面是怎么做的吧:
unsigned long __stdcall Thread4 (PVOID pvoid)
{
HDC hdc ;
PPARAMS pparams ;
HBRUSH hBrush ;
int xLeft, xRight, yTop, yBottom, iRed, iGreen, iBlue ;
pparams = (PPARAMS) pvoid ;
while (!pparams->bKill)
{
if (pparams->cxClient != 0 || pparams->cyClient != 0)
{
xLeft = rand () % pparams->cxClient ;
xRight = rand () % pparams->cxClient ;
yTop = rand () % pparams->cyClient ;
yBottom = rand () % pparams->cyClient ;
iRed = rand () & 255 ;
iGreen = rand () & 255 ;
iBlue = rand () & 255 ;
hdc = GetDC (pparams->hwnd) ;
hBrush = CreateSolidBrush (RGB (iRed, iGreen, iBlue)) ;
SelectObject (hdc, hBrush) ;
Rectangle (hdc, min (xLeft, xRight), min (yTop, yBottom),
max (xLeft, xRight), max (yTop, yBottom)) ;
ReleaseDC (pparams->hwnd, hdc) ;
DeleteObject (hBrush) ;
}
}
return 0;
}
产生一个随机的位置,一个随机的长宽,一个随机的颜色,再在子窗口中进行绘制,就ok了,还是一个很简单的事情。
其余三个子窗口的的原理也是一样的,其中第一个是不停循环显示素数;第二个是循环显示复数;第三个是不停的画一些半径随机的圆,下面是实现代码:
int CheckBottom (HWND hwnd, int cyClient, int cyChar, int iLine)
{
if (iLine * cyChar + cyChar > cyClient)
{
InvalidateRect (hwnd, NULL, TRUE) ;
UpdateWindow (hwnd) ;
iLine = 0 ;
}
return iLine ;
}
unsigned long __stdcall Thread1 (PVOID pvoid)
{
char szBuffer[16] ;
int iNum = 1, iLine = 0, i, iSqrt ;
HDC hdc ;
PPARAMS pparams ;
pparams = (PPARAMS) pvoid ;
while (!pparams->bKill)
{
do
{
if (++iNum < 0)
iNum = 0 ;
iSqrt = (int) sqrt (iNum) ;
for (i = 2 ; i <= iSqrt ; i++)
if (iNum % i == 0)
break ;
}
while (i <= iSqrt) ;
iLine = CheckBottom (pparams->hwnd, pparams->cyClient,
pparams->cyChar, iLine) ;
wsprintf (szBuffer, "%d", iNum) ;
hdc = GetDC (pparams->hwnd) ;
TextOut (hdc, 0, iLine * pparams->cyChar,
szBuffer, strlen (szBuffer)) ;
ReleaseDC (pparams->hwnd, hdc) ;
iLine++ ;
}
return 0;
}
// Window 1: 显示逐渐增加的素数
LRESULT APIENTRY WndProc1 (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
static PARAMS params ;
switch (iMsg)
{
case WM_CREATE :
params.hwnd = hwnd ;
params.cyChar = HIWORD (GetDialogBaseUnits ()) ;//系统中基于对话框字体的高度
hThread[0] = CreateThread(NULL,0,Thread1,¶ms,0,NULL);
SetThreadPriority(hThread[0],THREAD_PRIORITY_BELOW_NORMAL);
return 0 ;
case WM_SIZE :
params.cyClient = HIWORD (lParam) ;
return 0 ;
case WM_DESTROY :
params.bKill = TRUE ;
return 0 ;
}
return DefWindowProc (hwnd, iMsg, wParam, lParam) ;
}
// ------------------------------------------------------
unsigned long __stdcall Thread2(PVOID pvoid)
{
char szBuffer[16] ;
int iNum = 0, iNext = 1, iLine = 0, iTemp ;
HDC hdc ;
PPARAMS pparams ;
pparams = (PPARAMS) pvoid ;
while (!pparams->bKill)
{
if (iNum < 0)
{
iNum = 0 ;
iNext = 1 ;
}
iLine = CheckBottom (pparams->hwnd, pparams->cyClient,
pparams->cyChar, iLine) ;
wsprintf (szBuffer, "%d", iNum) ;
hdc = GetDC (pparams->hwnd) ;
TextOut (hdc, 0, iLine * pparams->cyChar,
szBuffer, strlen (szBuffer)) ;
ReleaseDC (pparams->hwnd, hdc) ;
iTemp = iNum ;
iNum = iNext ;
iNext += iTemp ;
iLine++ ;
}
return 0;
}
// Window : 显示逐渐增加的复数
LRESULT APIENTRY WndProc2 (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
static PARAMS params ;
switch (iMsg)
{
case WM_CREATE :
params.hwnd = hwnd ;
params.cyChar = HIWORD (GetDialogBaseUnits ()) ;
hThread[1] = CreateThread(NULL,0,Thread2,¶ms,0,NULL);
SetThreadPriority(hThread[1],THREAD_PRIORITY_BELOW_NORMAL);
return 0 ;
case WM_SIZE :
params.cyClient = HIWORD (lParam) ;
return 0 ;
case WM_DESTROY :
params.bKill = TRUE ;
return 0 ;
}
return DefWindowProc (hwnd, iMsg, wParam, lParam) ;
}
unsigned long __stdcall Thread3 (PVOID pvoid)
{
HDC hdc ;
int iDiameter ;
PPARAMS pparams ;
pparams = (PPARAMS) pvoid ;
while (!pparams->bKill)
{
InvalidateRect (pparams->hwnd, NULL, TRUE) ;
UpdateWindow (pparams->hwnd) ;
iDiameter = rand() % (max (1,
min (pparams->cxClient, pparams->cyClient))) ;
hdc = GetDC (pparams->hwnd) ;
Ellipse (hdc, (pparams->cxClient - iDiameter) / 2,
(pparams->cyClient - iDiameter) / 2,
(pparams->cxClient + iDiameter) / 2,
(pparams->cyClient + iDiameter) / 2) ;
ReleaseDC (pparams->hwnd, hdc) ;
}
return 0;
}
// Window 3: 显示任意半径的圆
LRESULT APIENTRY WndProc3 (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
static PARAMS params ;
switch (iMsg)
{
case WM_CREATE :
params.hwnd = hwnd ;
params.cyChar = HIWORD (GetDialogBaseUnits ()) ;
hThread[2] = CreateThread(NULL,0,Thread3,¶ms,0,NULL);
SetThreadPriority(hThread[2],THREAD_PRIORITY_BELOW_NORMAL);
return 0 ;
case WM_SIZE :
params.cxClient = LOWORD (lParam) ;
params.cyClient = HIWORD (lParam) ;
return 0 ;
case WM_DESTROY :
params.bKill = TRUE ;
return 0 ;
}
return DefWindowProc (hwnd, iMsg, wParam, lParam) ;
}