Delphi中使用Win32 API创建内建消息循环的线程函数
1.线程创建
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, // pointer to thread security attributes
DWORD dwStackSize, // initial thread stack size, in bytes
LPTHREAD_START_ROUTINE lpStartAddress, // pointer to thread function
LPVOID lpParameter, // argument for new thread
DWORD dwCreationFlags, // creation flags
LPDWORD lpThreadId // pointer to returned thread identifier
var
hMyThread: THandle; //线程句柄
iThreadID: DWORD = 0; //线程ID
begin
hMyThread := CreateThread(nil, 0, @ThreadFun, nil, 0, iThreadID);
..
end;
其中,ThreadFun是你定义的线程函数或过程,注意,因为这里是使用Win32 API创建线程,所以这个ThreadFun函数必须设置成stdcall;
如: procedure ThreadFun; stdcall;
2.向子线程发消息
UINT Msg, // message to post
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
);
举例:
const
WM_MyMsg = WM_USER + 100; //用户自定义消息
PostThreadMessage(iThreadID, WM_MyMsg, 0, 0);
3. 销毁子线程
1. PostThreadMessage(iThreadID, WM_QUIT, 0, 0);
2.CloseHandle(hMyThread);
BOOL CloseHandle(
HANDLE hObject // handle to object to close
);
如果iThreadID子线程内建了消息循环,哪么当线程中的GetMessage收到这个WM_QUIT消息后,iThreadID子线程将销毁。
4.子线程内建消息循环。
需要用到下面几个API:
a. 取得消息: GetMessage() 或 PeekMessage()
BOOL GetMessage(
LPMSG lpMsg, // address of structure with message
HWND hWnd, // handle of window
UINT wMsgFilterMin, // first message
UINT wMsgFilterMax // last message
);
BOOL PeekMessage(
LPMSG lpMsg, // pointer to structure for message
HWND hWnd, // handle to window
UINT wMsgFilterMin, // first message
UINT wMsgFilterMax, // last message
UINT wRemoveMsg // removal flags
);
这2个取得消息函数还是有点不同的。
GetMessage()函数在线程消息队列中取消息的时候,如果这时候消息队列中没有消息,该线程将冻结,直到别的线程向该线程发送一条消息后才会重新激活。
PeekMessage()函数在线程消息队列中取消息的时候,如果这时候消息队列中没有消息,操作系统还是让该线程执行一段时间。换句话说,它的作用只是查看一下线程的消息队列中有没有消息。个人觉得,这个函数要比GetMessage()函数用处更大一点。当然,GetMessage()函数在有些情况下,也是另有妙用。
b.翻译和派遣消息。
TranslateMessage(msg); //翻译消息
DispatchMessage(msg); //派遣消息
BOOL TranslateMessage(
CONST MSG *lpMsg // address of structure with message
);
LONG DispatchMessage(
CONST MSG *lpmsg // pointer to structure with message
);
上面这2个函数是消息循环的核心,它们和GetMessage() 或 PeekMessage()一齐构建消息循环---程序的心脏
举例:
var
msg: TMSG;
fQuit: Boolean;
begin
while not fQuit do
begin
while PeekMessage(msg, 0, 0, 0, PM_REMOVE) do
//while GetMessage(msg, 0, 0, 0) do
begin
if msg.message = WM_QUIT then
begin
fQuit := True;
end
else
if msg.message = WM_MyMsg then
begin
..
end
else
begin
TranslateMessage(msg);
DispatchMessage(msg);
end;
end;
end;
1.线程创建
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, // pointer to thread security attributes
DWORD dwStackSize, // initial thread stack size, in bytes
LPTHREAD_START_ROUTINE lpStartAddress, // pointer to thread function
LPVOID lpParameter, // argument for new thread
DWORD dwCreationFlags, // creation flags
LPDWORD lpThreadId // pointer to returned thread identifier
);
举例:var
hMyThread: THandle; //线程句柄
iThreadID: DWORD = 0; //线程ID
begin
hMyThread := CreateThread(nil, 0, @ThreadFun, nil, 0, iThreadID);
..
end;
其中,ThreadFun是你定义的线程函数或过程,注意,因为这里是使用Win32 API创建线程,所以这个ThreadFun函数必须设置成stdcall;
如: procedure ThreadFun; stdcall;
2.向子线程发消息
BOOL PostThreadMessage(
DWORD idThread, // thread identifierUINT Msg, // message to post
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
);
举例:
const
WM_MyMsg = WM_USER + 100; //用户自定义消息
PostThreadMessage(iThreadID, WM_MyMsg, 0, 0);
上面这句是主线程向iThreadID子线程发送WM_MyMsg消息。不过,在第1次向子线程发消息的时候,应该循环发,直到发送成功。这样:
while not PostThreadMessage(iThreadID, WM_MyMsg, 0, 0) do
Sleep(100);
3. 销毁子线程
1. PostThreadMessage(iThreadID, WM_QUIT, 0, 0);
2.CloseHandle(hMyThread);
BOOL CloseHandle(
HANDLE hObject // handle to object to close
);
如果iThreadID子线程内建了消息循环,哪么当线程中的GetMessage收到这个WM_QUIT消息后,iThreadID子线程将销毁。
4.子线程内建消息循环。
需要用到下面几个API:
a. 取得消息: GetMessage() 或 PeekMessage()
BOOL GetMessage(
LPMSG lpMsg, // address of structure with message
HWND hWnd, // handle of window
UINT wMsgFilterMin, // first message
UINT wMsgFilterMax // last message
);
BOOL PeekMessage(
LPMSG lpMsg, // pointer to structure for message
HWND hWnd, // handle to window
UINT wMsgFilterMin, // first message
UINT wMsgFilterMax, // last message
UINT wRemoveMsg // removal flags
);
这2个取得消息函数还是有点不同的。
GetMessage()函数在线程消息队列中取消息的时候,如果这时候消息队列中没有消息,该线程将冻结,直到别的线程向该线程发送一条消息后才会重新激活。
PeekMessage()函数在线程消息队列中取消息的时候,如果这时候消息队列中没有消息,操作系统还是让该线程执行一段时间。换句话说,它的作用只是查看一下线程的消息队列中有没有消息。个人觉得,这个函数要比GetMessage()函数用处更大一点。当然,GetMessage()函数在有些情况下,也是另有妙用。
b.翻译和派遣消息。
TranslateMessage(msg); //翻译消息
DispatchMessage(msg); //派遣消息
BOOL TranslateMessage(
CONST MSG *lpMsg // address of structure with message
);
LONG DispatchMessage(
CONST MSG *lpmsg // pointer to structure with message
);
上面这2个函数是消息循环的核心,它们和GetMessage() 或 PeekMessage()一齐构建消息循环---程序的心脏
举例:
var
msg: TMSG;
fQuit: Boolean;
begin
while not fQuit do
begin
while PeekMessage(msg, 0, 0, 0, PM_REMOVE) do
//while GetMessage(msg, 0, 0, 0) do
begin
if msg.message = WM_QUIT then
begin
fQuit := True;
end
else
if msg.message = WM_MyMsg then
begin
..
end
else
begin
TranslateMessage(msg);
DispatchMessage(msg);
end;
end;
end;
end;
上面的例子我为了写着省事,把PeekMessage()和 GetMessage() 放在一齐了。当然,你要明白,如果实际用的是GetMessage() 循环,if msg.message = WM_QUIT then是无用的,它只是为了上面的PeekMessage()而存在。因为当线程GetMessage() 收到WM_QUIT时,GetMessage()循环就退出了。