使用多线程多文档界面应用

【勇芳软件工作室】汉化HomePreviousNext

本主题中的示例显示了如何在多文档界面(MDI)进程中使用多个线程。该进程有一个主窗口,但可以有任何数量的子窗口。进程的主线程执行初始化,并通过应用程序定义的MainWndProc和ChildWndProc函数处理所有窗口的消息。

每次创建子窗口时,都会创建一个新的线程。在该示例中,新线程会连续检查全局变量,以查看是否需要终止。

ThreadFunc函数在CreateThread函数中指定为要执行的新线程的代码。与线程关联的子窗口的句柄作为参数传递给ThreadFunc。当将消息分派到子窗口时,子窗口的句柄也是ChildWndProc的参数。子窗口与其对应线程之间的任何通信都需要一个句柄。ThreadFunc和ChildWndProc都使用SetWindowLong函数中的窗口句柄来访问每个窗口结构中为应用程序使用而保留的值。在该示例中,该值是终止标志。当ChildWndProc获取WM_CLOSE消息时,它将设置该标志; ThreadFunc每次通过循环检查标志。

该示例演示了如何使用主线程的正常优先级和其他线程的正常优先级。因为主线程处理主窗口和子窗口的所有消息,因此其较高的相对优先级确保了对用户输入的响应。

当用户通过关闭主窗口终止进程时,主线程设置全局参数以指示工作线程应终止。主线程在继续之前等待每个子线程终止。这是必要的,只有当你想要线程清理,保存文件的更改,或从DLL中断开之前关闭。如果主线程不等待,则没有其他线程将能够执行,因为它们具有较低的优先级。

#include < windows.h >

#include < stdio.h >

#include < stdlib.h >

#define MM_NEWWIN 8001

typedef struct _PTHREADLIST

{

HANDLE hThread;

LPVOID lpvNext;

} THREADLIST, *PTHREADLIST;

HANDLE hModule; //处理这个进程的.EXE文件

HWND hwndMain = NULL; //处理主窗口

BOOL fKillAll = FALSE; //设置TRUE终止所有线程

PTHREADLIST pHead = NULL; //线程信息链接列表

BOOL InitializeApp(VOID);

LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);

LRESULT CALLBACK ChildWndProc(HWND, UINT, WPARAM, LPARAM);

DWORD ThreadFunc(HWND);

VOID AddThreadToList(HANDLE);

VOID ErrorExit(LPSTR);

//主线程:初始化应用程序并发送消息。

int WINAPI WinMain(HINSTANCE hInst,

HINSTANCE hPrevInst,

LPSTR lpszCmdLn,

int nShowCmd)

{

MSG msg;

hModule = GetModuleHandle(NULL);

if(!InitializeApp())

ErrorExit("InitializeApp failure!");

while(GetMessage(& msg,NULL,0,0))

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

return 1;

UNREFERENCED_PARAMETER(hInst);

UNREFERENCED_PARAMETER(hPrevInst);

UNREFERENCED_PARAMETER(lpszCmdLn);

UNREFERENCED_PARAMETER(nShowCmd);

}

//注册窗口类并创建主窗口。

BOOL InitializeApp(VOID)

{

HMENU hmenuMain, hmenuPopup;

WNDCLASS wc;

//为主窗口注册一个窗口类。

wc.style = CS_OWNDC;

wc.lpfnWndProc = MainWndProc;

wc.cbClsExtra = 0;

wc.cbWndExtra = 0;

wc.hInstance = hModule;

wc.hIcon = LoadIcon(NULL,IDI_APPLICATION);

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND+1);

wc.lpszMenuName = NULL;

wc.lpszClassName = "MainWindowClass";

if(!RegisterClass(& wc))

return FALSE;

//为子窗口注册一个窗口类。

wc.lpfnWndProc = ChildWndProc;

wc.lpszClassName = "ThreadWindowClass";

if(!RegisterClass(& wc))

return FALSE;

//为主窗口创建一个菜单。

hmenuMain = CreateMenu();

hmenuPopup = CreateMenu();

if(!AppendMenu(hmenuPopup,MF_STRING,MM_NEWWIN,“& New Window”))

return FALSE;

if(!AppendMenu(hmenuMain,MF_POPUP,(UINT)hmenuPopup,“& Threads”))

return FALSE;

//创建主窗口。

hwndMain = CreateWindow("MainWindowClass", "Primary Window",

WS_OVERLAPPED | WS_CAPTION | WS_BORDER | WS_THICKFRAME |

WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_CLIPCHILDREN |

WS_VISIBLE | WS_SYSMENU, CW_USEDEFAULT, CW_USEDEFAULT,

CW_USEDEFAULT,CW_USEDEFAULT,NULL,hmenuMain,hModule,

NULL);

if (hwndMain == NULL)

return FALSE;

//设置初始焦点。

SetFocus(hwndMain);

return TRUE;

}

//主窗口过程:处理主窗口的消息。

LRESULT CALLBACK MainWndProc(HWND hwnd,UINT uiMessage,

WPARAM wParam,LPARAM lParam)

{

static HWND hwndClient;

static DWORD dwCount = 1;

CLIENTCREATESTRUCT ccsClientCreate;

HWND hwndChildWnd;

DWORD IDThread;

PTHREADLIST pNode;

开关(uiMessage)

{

//创建一个客户端窗口来接收子窗口消息。

case WM_CREATE:

ccsClientCreate.hWindowMenu = NULL;

ccsClientCreate.idFirstChild = 1;

hwndClient = CreateWindow("MDICLIENT", NULL,

WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE, 0, 0, 0, 0,

hwnd, NULL, hModule, (LPVOID)&ccsClientCreate);

return 0L;

//关闭主窗口。首先将fKillAll设置为TRUE

//终止所有线程。然后等待线程退出

//在传递一个关闭消息到默认处理程序之前。如果你

//不要等待线程终止,进程终止

//没有机会进行线程清理。

case WM_CLOSE:

fKillAll = TRUE;

pNode = pHead;

while(pNode)

{

DWORD dwRes;

SetThreadPriority(pNode->hThread,

THREAD_PRIORITY_HIGHEST);

dwRes = WaitForSingleObject(pNode->hThread,

INFINITE);

pNode = (PTHREADLIST) pNode->lpvNext;

}

返回DefFrameProc(hwnd,hwndClient,uiMessage,

wParam, lParam);

//终止进程。

case WM_DESTROY:

PostQuitMessage(0);

return 0L;

//处理菜单命令。

case WM_COMMAND:

开关(LOWORD(wParam))

{

//创建子窗口并启动一个线程。

case MM_NEWWIN:

HANDLE hThrd;

MDICREATESTRUCT mdicCreate;

TCHAR tchTitleBarText[32];

LONG lPrev;

sprintf(tchTitleBarText, "Thread Window %d", dwCount);

mdicCreate.szClass = "ThreadWindowClass";

mdicCreate.szTitle = tchTitleBarText;

mdicCreate.hOwner = hModule;

mdicCreate.x = mdicCreate.y =

mdicCreate.cx = mdicCreate.cy = CW_USEDEFAULT;

mdicCreate.style = mdicCreate.lParam = 0L;

//发送一个“创建子窗口”消息给

//客户端窗口。

hwndChildWnd = (HWND) SendMessage(hwndClient,

WM_MDICREATE, 0L, (LONG)&mdicCreate);

if (hwndChildWnd == NULL)

ErrorExit("Failed in Creating Thread Window!");

//用于传递退出消息的窗口结构

//线程。

lPrev = SetWindowLong(hwndChildWnd, GWL_USERDATA, 0);

//创建一个挂起的线程;改变其优先权

//调用ResumeThread。

hThrd = CreateThread(NULL, //没有安全属性

0, //使用默认堆栈大小

(LPTHREAD_START_ROUTINE)ThreadFunc,

(LPVOID)hwndChildWnd, // param to thread func

CREATE_SUSPENDED, //创建标志

&IDThread); //线程标识符

if (hThrd == NULL)

ErrorExit("CreateThread Failed!");

AddThreadToList(hThrd);

dwCount++;

//将优先级设置为低于主(输入)

//线程,所以进程响应用户

//输入。然后恢复线程。

if(!SetThreadPriority(hThrd,

THREAD_PRIORITY_BELOW_NORMAL))

ErrorExit("SetThreadPriority failed!");

if ((ResumeThread(hThrd)) == -1)

ErrorExit("ResumeThread failed!");

return 0L;

默认:

返回DefFrameProc(hwnd,hwndClient,uiMessage,

wParam, lParam);

}

默认:

返回DefFrameProc(hwnd,hwndClient,uiMessage,

wParam, lParam);

}

}

//处理子窗口的消息。

LRESULT CALLBACK ChildWndProc(HWND hwnd,UINT uiMessage,WPARAM

wParam,LPARAM lParam)

{

LONG lPrevLong;

开关(uiMessage)

{

//使用窗口结构将“关闭”消息传递给线程。

case WM_CLOSE:

lPrevLong = SetWindowLong(hwnd, GWL_USERDATA, 1);

return DefMDIChildProc(hwnd, uiMessage, wParam, lParam);

case WM_DESTROY:

return 0L;

默认:

return DefMDIChildProc(hwnd, uiMessage, wParam, lParam);

}

}

//每个子窗口都有一个可用于执行任务的线程

//与该窗口相关联 - 例如,绘制其内容。

DWORD ThreadFunc(HWND hwnd)

{

LONG lKillMe = 0L;

while(TRUE)

{

lKillMe = GetWindowLong(hwnd, GWL_USERDATA);

if (fKillAll || lKillMe) break;

// 执行任务。

}

//执行线程终止之前需要执行的操作。

return 0;

}

VOID AddThreadToList(HANDLE hThread)

{

PTHREADLIST pNode;

pNode = (PTHREADLIST) LocalAlloc(LPTR, sizeof(PTHREADLIST));

if (pNode == NULL)

ErrorExit("malloc Failed!");

pNode->hThread = hThread;

pNode->lpvNext = (LPVOID) pHead;

pHead = pNode;

}

VOID ErrorExit(LPSTR lpszMessage)

{

MessageBox(hwndMain, lpszMessage, "Error", MB_OK);

ExitProcess(0);

}