在平時寫程序時,總是碰到窗體(TForm)與線程(TThread)消息通信問題。令人煩惱的是窗體不能向線程(TThread)發(fā)送消息(線程沒有窗口句柄)。經(jīng)過幾天的折騰,想出二種解決方案,拿出來跟大家探討探討。
第一。我們知道VC++ 中的MFC類庫是自已封裝了消息處理(BEGINMESSAGE, ENDMESSAGE),在MFC中對消息的處理是通過建立一張消息映射表,而把方法(function)或過程(PRocedure)的地址保存到映射表里(消息處理實(shí)質(zhì)上是方法或過程的調(diào)用),再加上一個消息分發(fā)機(jī)制,來實(shí)現(xiàn)消息的接收發(fā)送 <詳見VC++技術(shù)內(nèi)幕>。所以我們只要為線程里建立一張消息映射表,并建立相應(yīng)的消息分發(fā)機(jī)制。這樣就可以處理窗體發(fā)送到線程的消息。以下代碼是實(shí)現(xiàn)消息映射表和消息分發(fā)的類(詳見 <../消息處理設(shè)計(jì)(線程)1/MessageHandle.pas> 中 )
unit MessageHandle;
interface
uses messages,Classes,SysUtils,Dialogs;
const PMSG_BASE = $BE00; //自定義消息基址;
PMSG_NUM = 200; //消息表大小;
{**自定義消息處理類
*;功能 = 建立自定義消息表,處理線程之間
* 以及與主窗體之間的自定義消息(宏觀)
*}
//消息處理句柄
TMessageHandle = procedure(var Message: TMessage) of Object;
TPDispatcher = class(TObject)
private
//消息對應(yīng)表(消息ID為數(shù)組下標(biāo));
MessageHandles: array of TMessageHandle;
//從消息ID得到數(shù)組ID
function GetIndexFromMsgID(const aMessageID: cardinal): Integer;
public
constructor Create;
destructor Destroy;
//發(fā)送消息
procedure SendMessage(var Message: TMessage); overload;
//添加自定義消息到消息對應(yīng)表;
procedure AddHandle(const aMessageID: cardinal; aMessageHandle: TMessageHandle);
end;
//
implementation
{ TPDispatcher }
constructor TPDispatcher.Create;
var i: Integer;
begin
SetLength(MessageHandles,PMSG_NUM); //200個消息的消息對應(yīng)表
//初始化消息隊(duì)列;
for i := 0 to Pred(PMSG_NUM) do
MessageHandles[i] := nil;
end;
destructor TPDispatcher.Destroy;
begin
{釋放消息對應(yīng)表}
FreeAndNil(MessageHandles);
end;
procedure TPDispatcher.AddHandle(const aMessageID: cardinal;
aMessageHandle: TMessageHandle);
var tID: Integer;
begin
tID := GetIndexFromMsgID(aMessageID);
Assert((tID > 0) or (tID < Pred(PMSG_NUM)) );
Assert(Assigned(aMessageHandle));
MessageHandles[tID] := aMessageHandle;
end;
function TPDispatcher.GetIndexFromMsgID(const aMessageID: cardinal): Integer;
begin
Result := aMessageID - PMSG_BASE;
end;
procedure TPDispatcher.SendMessage(var Message: TMessage);
var tID: Integer;
tMsgHandle: TMessageHandle;
begin
tID := GetIndexFromMsgID(Message.Msg);
Assert((tID > 0) or (tID < Pred(PMSG_NUM)));
tMsgHandle := MessageHandles[tID];
if Assigned(tMsgHandle) then
tMsgHandle(Message);
end;
現(xiàn)在我們只需要注冊一下自定義的消息,然后通過消息分發(fā)類(TPDispatcher),實(shí)現(xiàn)對線程消息的處理。代碼如下<詳見../消息處理設(shè)計(jì)(線程)1/test/unit1.pas>:
Unit unit1
const
{自定久線程消息}
MY_MESSAGE2 = PMSG_BASE + 02;
type
TForm1 = class(TForm)
AddMsgList: TButton;
SendThead: TButton;
sendForm: TButton;
sendOther: TButton;
procedure SendTheadClick(Sender: TObject); //發(fā)送消息
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
Fdispatcher: TPDispatcher; 消息映射表類
Fhandle: TPHandler;
FThread: TPTHread; 自定義線程類
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.SendTheadClick(Sender: TObject);
var aMessage: TMessage;begin
aMessage.Msg := MY_MESSAGE2;
aMessage.WParam := 1;
Fdispatcher.SendMessage(aMessage);
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
{創(chuàng)建消息映射表類}
Fdispatcher := TPDispatcher.Create;
Fhandle := TPHandler.Create;
{創(chuàng)建線程}
FThread := TPThread.Create(false);
{向映射表中增加消息}
Fdispatcher.AddHandle(MY_MESSAGE2,FThread.DoMessage);
end;
procedure TForm1.FormDestroy(Sender: TObject);
var i: Integer;
begin
FreeAndNil(Fdispatcher);
FreeAndNil(Fhandle);
for i:= 0 to 3 do
FreeAndNil(FThread[i]);
end;
第二。窗口可以處理消息是因?yàn)樗写翱诰浔榱耸咕€程也能處理消息,我們可以通過為線程加上一個相應(yīng)窗口類的窗口名柄。(源碼在 <../消息處理設(shè)計(jì)(線程)2 / pThread.pas >中)
unit pThread;
interface
uses classes,sysutils,Windows,Messages,Dialogs;
const MY_MESSAGE1 = $BD00 + 01;
Type
{** 消息處理線程類
*;功能 = 添加線程處理消息能力,
*}
TPMsgThread = class(TThread)
private
//窗口句柄
FWndHandle: HWND;
//窗口數(shù)據(jù)信息
FWndClass: WNDCLASS;
//指向窗口回調(diào)函數(shù)的指針
FObjectInstance: Pointer;
//初始化窗口數(shù)據(jù)
procedure InitWnd;
//創(chuàng)建隱藏窗口
procedure CreateWnd;
//注冊隱藏窗口
procedure RegistWnd;
procedure DestroyWnd;
//窗口回調(diào)函數(shù)
procedure pWndProc(var Message: TMessage); virtual;
protected
procedure Execute; override;
procedure DoTerminate; override;
public
constructor Create(CreateSuspended: Boolean); virtual;
property WndHandle: HWND read FWndHandle write FWndHandle;
end;
implementation
const WND_NAME = 'PY20';
{ TPMsgThread }
constructor TPMsgThread.Create(CreateSuspended: Boolean);
begin
inherited Create(CreateSuspended);
FWndHandle := Integer(nil);
InitWnd;
RegistWnd;
CreateWnd;
end;
procedure TPMsgThread.CreateWnd;
begin
if(WndHandle = Integer(nil)) then
WndHandle := CreateWindow(FWndClass.lpszClassName, FWndClass.lpszClassName,
WS_POPUP or WS_CAPTION or WS_CLipSIBLINGS or WS_SYSMENU
or WS_MINIMIZEBOX,
GetSystemMetrics(SM_CXSCREEN) div 2,
GetSystemMetrics(SM_CYSCREEN) div 2,
0, 0, 0, 0, FWndClass.hInstance, nil);
//置換窗口回調(diào)函數(shù)
SetWindowLong(WndHandle, GWL_WNDPROC, Longint(FObjectInstance));
end;
procedure TPMsgThread.DestroyWnd;
begin
UnregisterClass(FWndClass.lpszClassName,FWndClass.hInstance);
DestroyWindow(WndHandle);
end;
procedure TPMsgThread.DoTerminate;
begin
inherited;
DestroyWnd;
end;
procedure TPMsgThread.Execute;
begin
end;
procedure TPMsgThread.InitWnd;
begin
FwndClass.lpszClassName := PChar(WND_NAME);
FWndClass.hInstance := Handle;
FWndClass.lpfnWndProc := @DefWindowProc;
end;
procedure TPMsgThread.pWndProc(var Message: TMessage);
begin
end;
procedure TPMsgThread.RegistWnd;
begin
FObjectInstance := Classes.MakeObjectInstance(pWndProc);
if(FWndClass.hInstance <> Integer(nil)) then
RegisterClass(FWndClass);
end;
新聞熱點(diǎn)
疑難解答
圖片精選