【转载】Delphi7从子线程中发送消息到主线程触发事件执行 在对数据库的操作时,有时要用一个子线程来进行后台的数据操作。比如说数据备份,转档什么的。在主窗口还能同是进行其它操作。而有时后台每处理一个数据文件,要向主窗口发送消息,让主窗口实时显示处理进度在窗口上(可视),同时进行日志处理等。我用的是下面的方法: [1]用到的API函数: RegisterWindowsMessage ---------------------- 函数功能:该函数定义一个新的窗口消息,该消息确保在系统中是唯一的。返回的消息值可在调用函数SendMessage或PostMessage时使用。 function RegisterWindowMessage(lpString: PChar): UINT; stdcall; SendNotifyMessage ---------------------- 函数功能:该函数将指定的消息发送到一个窗口。 如果该窗口是由调用线程创建的;此函数为该窗口调用窗口程序, 并等待窗口程序处理完消息后再返回。 如果该窗口是由不同的线程创建的,此函数将消息传给该窗口程序, 并立即返回,不等待窗口程序处理完消息。 SendNotifyMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM IParam); BroadcastSystemMessage ---------------------- 函数功能:该函数发送消息给指定的接受者。 接受者可以是一个应用程序、安装驱动器、网络驱动器、系统级设备驱动器 或这些系统组件的组合。 [2]过程: type TForm1 = class(TForm) ............... ............... private Msg: Cardinal; protected procedure WndProc(var Message: TMessage); override; public ............... ............... end; var Form1: TForm1; MsgStrList: TStringList; MsgStrLock : TCriticalSection; implementation uses ThreadCommunication_Unit; {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); begin Msg := RegisterWindowMessage('wm_threadmsg'); MsgStrList := TStringList.Create; end; procedure TForm1.WndProc(var Message: TMessage); begin if Message.Msg = Msg then begin MsgStrLock.Enter; if MsgStrList.Count > 0 then begin Caption := MsgStrList.Strings[0]; MsgStrList.Delete(0); end; MsgStrLock.Leave; ShowMessage('收到消息了'+ inttostr(Message.Msg)); end else begin inherited; end; end; procedure TForm1.Button1Click(Sender: TObject); begin TThreadCommunication.Create(Msg,Memo1); end; ............... ............... initialization MsgStrLock := TCriticalSection.Create; finalization MsgStrLock.Free; end. 一个子线程类的单元: unit ThreadCommunication_Unit; interface uses Classes,StdCtrls; type TThreadCommunicaiton = class(TThread) private FMsg : Cardinal; FMemo: TMemo; protected procedure Execute; override; procedure SendMsg; public constructor Create(aMsg:Cardinal;am:TMemo);virtual; end; implementation uses Messages,Windows, Dialogs,SysUtils, ThreadMsg; { TThreadCommunicaiton } constructor TThreadCommunicaiton.Create(aMsg: Cardinal; am:TMemo); begin inherited Create(True); FMsg := aMsg; FMemo:= am; FreeOnTerminate :=True; Resume; end; procedure TThreadCommunicaiton.Execute; begin Synchronize(SendMsg); end; procedure TThreadCommunicaiton.SendMsg; var M: TMessage; B: DWord; d: integer; begin { Place thread code here } sleep(50); M.Msg := FMsg; B := BSM_ALLCOMPONENTS; MsgStrLock.Enter; MsgStrList.Add('子线程子柄:'+inttostr(ThreadID)+ ' 用BroadcastSystemMessage发送'); d := MsgStrList.Count; MsgStrLock.Leave; BroadcastSystemMessage(BSF_POSTMESSAGE, @B , M.Msg, M.WParam, M.LParam ); FMemo.Lines.Add('子线程子柄:'+inttostr(ThreadID)+ ' 用BroadcastSystemMessage发送'+inttostr(d)); end; end. 我在窗口上放有一Memo控件,可以显示一些信息。 同时我定义了一个全局的TStringList的变量,用于存在要从子线程传出的一些值。用BroadcaseSystemMessage发送消息,而消息号由创建子线程时传入。而消息号在FormCreate中用RegisterWindowsMessage定义,并获得一个消息号。 而消息触发后的事件处理写在WndProc中。 这里将子线程传出的字符串写入窗口的标题。 而TStringList的变量作为临界区使用, 因为当两个线程访问全局量时,为防止它们同时执行,需要使用线程同步。 用TCriticalSection进行操作。 Enter,进入临界区 Leave,离开临界区 这样可以正确的处理从子线程发来的消息。 如果是用SendNotifyMessage函数发送消息的话。 用法如下: M.Msg := FMsg; SendNotifyMessage(HWND_BROADCAST,M.Msg , M.WParam, M.LParam); 参数为HWND_BROADCAST,则消息将被发送到系统中所有顶层窗口,包括无效或不可见的非自身拥有的窗口、被覆盖的窗口和弹出式窗口,但消息不被发送到子窗口。 由于是用SendNotifyMessage将消息发送到主窗口,而主窗口所在线程与调用线程是同一个线程,所以要等待窗口程序处理完消息后再返回。才会执行子线程中的: FMemo.Lines.Add('子线程子柄:'+inttostr(ThreadID)+ ' 用SendNotifyMessage发送'); 还可以用 function PostMessage(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): BOOL; stdcall; hWnd是窗口句柄,你可以将消息发送到主窗口。 而SendNotifyMessage是将消息发送到所有的顶层窗口。就是说如果你在系统中启动了两个实例运行。 一个中发出的消息两个实例都会收到。而PostMessage由于是对句柄发消息。只会在本身这个实例中产生作用。