在delphi线程中实现消息循环

http://delphi.cjcsoft.net//viewthread.php?tid=635

在delphi线程中实现消息循环

Delphi的TThread类使用很方便,但是有时候我们需要在线程类中使用消息循环,delphi没有提供.

花了两天的事件研究了一下win32的消息系统,写了一个线程内消息循环的测试.

但是没有具体应用过,贴出来给有这方面需求的DFW参考一下.希望大家和我讨论.

{-----------------------------------------------------------------------------
Unit Name: uMsgThread
Author:    xwing
eMail :    [email protected] ; MSN : [email protected]
Purpose:   Thread with message Loop
History:

2003-6-19, add function to Send Thread Message.            ver 1.0
            use Event List and waitforsingleObject
            your can use WindowMessage or ThreadMessage
2003-6-18, Change to create a window to Recving message
2003-6-17, Begin.
-----------------------------------------------------------------------------}
unit uMsgThread;

interface
{$WARN SYMBOL_DEPRECATED OFF}
{$DEFINE USE_WINDOW_MESSAGE}
uses
    Classes, windows, messages, forms, sysutils;

type
    TMsgThread = class(TThread)
    private
        {$IFDEF USE_WINDOW_MESSAGE}
        FWinName    : string;
        FMSGWin     : HWND;
        {$ELSE}
        FEventList  : TList;
        FCtlSect    : TRTLCriticalSection;
        {$ENDIF}
        FException  : Exception;
        fDoLoop     : Boolean;
        FWaitHandle : THandle;
        {$IFDEF USE_WINDOW_MESSAGE}
        procedure MSGWinProc(var Message: TMessage);
        {$ELSE}
        procedure ClearSendMsgEvent;
        {$ENDIF}
        procedure SetDoLoop(const Value: Boolean);
        procedure WaitTerminate;

    protected
        Msg         :tagMSG;

        procedure Execute; override;
        procedure HandleException;
        procedure DoHandleException;virtual;
        //Inherited the Method to process your own Message
        procedure DoProcessMsg(var Msg:TMessage);virtual;
        //if DoLoop = true then loop this procedure
        //Your can use the method to do some work needed loop.
        procedure DoMsgLoop;virtual;
        //Initialize Thread before begin message loop
        procedure DoInit;virtual;
        procedure DoUnInit;virtual;

        procedure PostMsg(Msg:Cardinal;wParam:Integer;lParam:Integer);
        //When Use SendMsg method Must not use Synchronize Method in ThreadLoop !!!
        //otherwise will caurse DeadLock
        procedure SendMsg(Msg:Cardinal;wParam:Integer;lParam:Integer);

    public
        constructor Create(Loop:Boolean=False;ThreadName: string=‘‘);
        destructor destroy;override;
        procedure AfterConstruction;override;

        //postMessage to Quit,and Free(if FreeOnTerminater = true)
        //can call this in thread loop, don‘t use terminate property.
        procedure QuitThread;
        //PostMessage to Quit and Wait, only call in MAIN THREAD
        procedure QuitThreadWait;
        //just like Application.processmessage.
        procedure ProcessMessage;
        //enable thread loop, no waitfor message
        property DoLoop: Boolean read fDoLoop Write SetDoLoop;

    end;

implementation

{ TMsgThread }
{//////////////////////////////////////////////////////////////////////////////}
constructor TMsgThread.Create(Loop:Boolean;ThreadName:string);
begin
    {$IFDEF USE_WINDOW_MESSAGE}
    if ThreadName <> ‘‘ then
        FWinName := ThreadName
    else
        FWinName := ‘Thread Window‘;
    {$ELSE}
    FEventList := TList.Create;
    InitializeCriticalSection(fCtlSect);
    {$ENDIF}

    FWaitHandle := CreateEvent(nil, True, False, nil);

    FDoLoop := Loop;            //default disable thread loop
    inherited Create(False);    //Create thread
    FreeOnTerminate := True;    //Thread quit and free object

    //Call resume Method in Constructor Method
    Resume;
    //Wait until thread Message Loop started
    WaitForSingleObject(FWaitHandle,INFINITE);
end;

{------------------------------------------------------------------------------}
procedure TMsgThread.AfterConstruction;
begin
end;

{------------------------------------------------------------------------------}
destructor TMsgThread.destroy;
begin
    {$IFDEF USE_WINDOW_MESSAGE}
    {$ELSE}
    FEventList.Free;
    DeleteCriticalSection(FCtlSect);
    {$ENDIF}

    inherited;
end;

{//////////////////////////////////////////////////////////////////////////////}
procedure TMsgThread.Execute;
var
    mRet:Boolean;
    aRet:Boolean;
    {$IFNDEF USE_WINDOW_MESSAGE}
    uMsg:TMessage;
    {$ENDIF}
begin
{$IFDEF USE_WINDOW_MESSAGE}
    FMSGWin := CreateWindow(‘STATIC‘,PChar(FWinName),WS_POPUP,0,0,0,0,0,0,hInstance,nil);
    SetWindowLong(FMSGWin, GWL_WNDPROC, Longint(MakeObjectInstance(MSGWinProc)));
{$ELSE}
    PeekMessage(Msg,0,WM_USER,WM_USER,PM_NOREMOVE); //Force system alloc a msgQueue
{$ENDIF}

    //notify Conctructor can returen.
    SetEvent(FWaitHandle);
    CloseHandle(FWaitHandle);

    mRet := True;
    try
        DoInit;
        while mRet do   //Message Loop
        begin
            if fDoLoop then
            begin
                aRet := PeekMessage(Msg,0,0,0,PM_REMOVE);
                if aRet and (Msg.message <> WM_QUIT) then
                begin
                    {$IFDEF USE_WINDOW_MESSAGE}
                    TranslateMessage(Msg);
                    DispatchMessage(Msg);
                    {$ELSE}
                    uMsg.Msg := Msg.message;
                    uMsg.wParam := Msg.wParam;
                    uMsg.lParam := Msg.lParam;
                    DoProcessMsg(uMsg);
                    {$ENDIF}

                    if Msg.message = WM_QUIT then
                        mRet := False;
                end;
                {$IFNDEF USE_WINDOW_MESSAGE}
                ClearSendMsgEvent;      //Clear SendMessage Event
                {$ENDIF}
                DoMsgLoop;
            end
            else begin
                mRet := GetMessage(Msg,0,0,0);
                if mRet then
                begin
                    {$IFDEF USE_WINDOW_MESSAGE}
                    TranslateMessage(Msg);
                    DispatchMessage(Msg);
                    {$ELSE}
                    uMsg.Msg := Msg.message;
                    uMsg.wParam := Msg.wParam;
                    uMsg.lParam := Msg.lParam;
                    DoProcessMsg(uMsg);
                    ClearSendMsgEvent;      //Clear SendMessage Event
                    {$ENDIF}
                end;
            end;
        end;
        DoUnInit;
        {$IFDEF USE_WINDOW_MESSAGE}
        DestroyWindow(FMSGWin);
        FreeObjectInstance(Pointer(GetWindowLong(FMSGWin, GWL_WNDPROC)));
        {$ENDIF}
    except
        HandleException;
    end;
end;

{------------------------------------------------------------------------------}
{$IFNDEF USE_WINDOW_MESSAGE}
procedure TMsgThread.ClearSendMsgEvent;
var
    aEvent:PHandle;
begin
    EnterCriticalSection(FCtlSect);
    try
        if FEventList.Count <> 0 then
        begin
            aEvent := FEventList.Items[0];
            if aEvent <> nil then
            begin
                SetEvent(aEvent^);
                CloseHandle(aEvent^);
                Dispose(aEvent);
            end;
            FEventList.Delete(0);
        end;
    finally
        LeaveCriticalSection(FCtlSect);
    end;
end;
{$ENDIF}

{------------------------------------------------------------------------------}
procedure TMsgThread.HandleException;
begin
    FException := Exception(ExceptObject);  //Get Current Exception object
    try
        if not (FException is EAbort) then
            inherited Synchronize(DoHandleException);
    finally
        FException := nil;
    end;
end;

{------------------------------------------------------------------------------}
procedure TMsgThread.DoHandleException;
begin
    if FException is Exception then
        Application.ShowException(FException)
    else
        SysUtils.ShowException(FException, nil);
end;

{//////////////////////////////////////////////////////////////////////////////}
{$IFDEF USE_WINDOW_MESSAGE}
procedure TMsgThread.MSGWinProc(var Message: TMessage);
begin
    DoProcessMsg(Message);
    with Message do
        Result:=DefWindowProc(FMSGWin,Msg,wParam,lParam);
end;
{$ENDIF}

{------------------------------------------------------------------------------}
procedure TMsgThread.DoProcessMsg(var Msg:TMessage);
begin
end;

{------------------------------------------------------------------------------}
procedure TMsgThread.ProcessMessage;
{$IFNDEF USE_WINDOW_MESSAGE}
var
    uMsg:TMessage;
{$ENDIF}
begin
    while PeekMessage(Msg,0,0,0,PM_REMOVE) do
    if Msg.message <> WM_QUIT then
    begin
        {$IFDEF USE_WINDOW_MESSAGE}
        TranslateMessage(Msg);
        DispatchMessage(msg);
        {$ELSE}
        uMsg.Msg := Msg.message;
        uMsg.wParam := Msg.wParam;
        uMsg.lParam := Msg.lParam;
        DoProcessMsg(uMsg);
        {$ENDIF}
    end;
end;

{//////////////////////////////////////////////////////////////////////////////}
procedure TMsgThread.DoInit;
begin
end;

procedure TMsgThread.DoUnInit;
begin
end;

procedure TMsgThread.DoMsgLoop;
begin
    Sleep(1);
end;

{//////////////////////////////////////////////////////////////////////////////}
procedure TMsgThread.QuitThread;
begin
    {$IFDEF USE_WINDOW_MESSAGE}
    PostMessage(FMSGWin,WM_QUIT,0,0);
    {$ELSE}
    PostThreadMessage(ThreadID,WM_QUIT,0,0);
    {$ENDIF}
end;

{------------------------------------------------------------------------------}
procedure TMsgThread.QuitThreadWait;
begin
    QuitThread;
    WaitTerminate;
end;

{------------------------------------------------------------------------------}
procedure TMsgThread.SetDoLoop(const Value: Boolean);
begin
    if Value = fDoLoop then Exit;
    fDoLoop := Value;
    if fDoLoop then
        PostMsg(WM_USER,0,0);
end;

{------------------------------------------------------------------------------}
//Can only call this method in MAIN Thread!!
procedure TMsgThread.WaitTerminate;
var
    xStart:Cardinal;
begin
    xStart:=GetTickCount;
    try
        //EnableWindow(Application.Handle,False);
        while WaitForSingleObject(Handle, 10) = WAIT_TIMEOUT do
        begin
            Application.ProcessMessages;
            if GetTickCount > (xStart + 4000) then
            begin
                TerminateThread(Handle, 0);
                Beep;
                Break;
            end;
        end;
    finally
        //EnableWindow(Application.Handle,True);
    end;
end;

{------------------------------------------------------------------------------}
procedure TMsgThread.PostMsg(Msg: Cardinal; wParam, lParam: Integer);
begin
    {$IFDEF USE_WINDOW_MESSAGE}
    postMessage(FMSGWin,Msg,wParam,lParam);
    {$ELSE}
    EnterCriticalSection(FCtlSect);
    try
        FEventList.Add(nil);
        PostThreadMessage(ThreadID,Msg,wParam,lParam);
    finally
        LeaveCriticalSection(FCtlSect);
    end;
    {$ENDIF}
end;

{------------------------------------------------------------------------------}
procedure TMsgThread.SendMsg(Msg: Cardinal; wParam, lParam: Integer);
{$IFNDEF USE_WINDOW_MESSAGE}
var
    aEvent:PHandle;
{$ENDIF}
begin
    {$IFDEF USE_WINDOW_MESSAGE}
    SendMessage(FMSGWin,Msg,wParam,lParam);
    {$ELSE}
    EnterCriticalSection(FCtlSect);
    try
        New(aEvent);
        aEvent^ := CreateEvent(nil, True, False, nil);
        FEventList.Add(aEvent);
        PostThreadMessage(ThreadID,Msg,wParam,lParam);
    finally
        LeaveCriticalSection(FCtlSect);
    end;
    WaitForSingleObject(aEvent^,INFINITE);
    {$ENDIF}
end;

end. 

我参考了一下msdn,还有windows核心编程. 写了一个类来封装这个功能,不知道对不对.

里面使用了两个方法,一个使用一个隐含窗体来处理消息

还有一个是直接使用thread的消息队列来处理,但是这个时候sendmessage无法工作,

所以我自己设想了一个方法,虽然不完全达到了要求但是我简单测试了一下,好像还能工作.

切换两种工作方式要修改编译条件

{$DEFINE USE_WINDOW_MESSAGE} 使用隐含窗体来处理消息

{-$DEFINE USE_WINDOW_MESSAGE} 使用线程消息队列来处理消息

还有我想要等待线程开始进行消息循环的时候create函数才返回.

但是现在好像还没有这样(用一个事件来处理).只是开始进入了threadexecute函数,线程的create就返回了.可能会出问题.

通过设置 DoLoop属性可以设定线程是否循环(不阻塞等待消息),这样派生类线程在循环做一些其他事情的同时还可以接受消息. 例如:

派生类里面循环发送缓冲区的数据,还可以响应其他线程发送过来的消息(如停止,启动,退出,等等)

重新修改了一下,现在用起来基本没有问题了。

{ -----------------------------------------------------------------------------
  Unit Name: uMsgThread
  Author:    xwing
  eMail :    [email protected] ; MSN : [email protected]
  Purpose:   Thread with message Loop
  History:

  2003-7-15  Write thread class without use delphi own TThread.
  2003-6-19, add function to Send Thread Message.            ver 1.0
  use Event List and waitforsingleObject
  your can use WindowMessage or ThreadMessage
  2003-6-18, Change to create a window to Recving message
  2003-6-17, Begin.
  ----------------------------------------------------------------------------- }
unit uMsgThread;

interface

{$WARN SYMBOL_DEPRECATED OFF}
{$DEFINE USE_WINDOW_MESSAGE}

uses
  Classes, windows, messages, forms, sysutils;

const
  NM_EXECPROC = $8FFF;

type
  EMsgThreadErr = class( Exception );

  TMsgThreadMethod = procedure of object;

  TMsgThread = class
  private
    SyncWindow : HWND;
    FMethod : TMsgThreadMethod;
    procedure SyncWindowProc( var Message : TMessage );

  private
    m_hThread : THandle;
    threadid : DWORD;

{$IFDEF USE_WINDOW_MESSAGE}
    FWinName : string;
    FMSGWin : HWND;
{$ELSE}
    FEventList : TList;
    FCtlSect : TRTLCriticalSection;
{$ENDIF}
    FException : Exception;
    fDoLoop : Boolean;
    FWaitHandle : THandle;

{$IFDEF USE_WINDOW_MESSAGE}
    procedure MSGWinProc( var Message : TMessage );
{$ELSE}
    procedure ClearSendMsgEvent;
{$ENDIF}
    procedure SetDoLoop( const Value : Boolean );
    procedure Execute;

  protected
    Msg : tagMSG;

{$IFNDEF USE_WINDOW_MESSAGE}
    uMsg : TMessage;
    fSendMsgComp : THandle;
{$ENDIF}
    procedure HandleException;
    procedure DoHandleException; virtual;

    // Inherited the Method to process your own Message
    procedure DoProcessMsg( var Msg : TMessage ); virtual;

    // if DoLoop = true then loop this procedure
    // Your can use the method to do some work needed loop.
    procedure DoMsgLoop; virtual;

    // Initialize Thread before begin message loop
    procedure DoInit; virtual;
    procedure DoUnInit; virtual;

    procedure PostMsg( Msg : Cardinal; wParam : Integer; lParam : Integer );
    // When Use SendMsg method Must not use Synchronize Method in ThreadLoop !!!
    // otherwise will caurse DeadLock
    function SendMsg( Msg : Cardinal; wParam : Integer; lParam : Integer )
      : Integer;

  public
    constructor Create( Loop : Boolean = False; ThreadName : string = ‘‘ );
    destructor destroy; override;

    // Return TRUE if the thread exists. FALSE otherwise
    function ThreadExists : BOOL;

    procedure Synchronize( syncMethod : TMsgThreadMethod );

    function WaitFor : Longword;
    function WaitTimeOut( timeout : DWORD = 4000 ) : Longword;

    // postMessage to Quit,and Free(if FreeOnTerminater = true)
    // can call this in thread loop, don‘t use terminate property.
    procedure QuitThread;

    // just like Application.processmessage.
    procedure ProcessMessage;

    // enable thread loop, no waitfor message
    property DoLoop : Boolean read fDoLoop write SetDoLoop;

  end;

implementation

function msgThdInitialThreadProc( pv : Pointer ) : DWORD; stdcall;
var
  obj : TMsgThread;
begin
  obj := TMsgThread( pv );
  obj.Execute;
  Result := 0;
end;

{ TMsgThread }
{ ////////////////////////////////////////////////////////////////////////////// }
constructor TMsgThread.Create( Loop : Boolean; ThreadName : string );
begin
{$IFDEF USE_WINDOW_MESSAGE}
  if ThreadName <> ‘‘ then
    FWinName := ThreadName
  else
    FWinName := ‘Thread Window‘;
{$ELSE}
  FEventList := TList.Create;
  InitializeCriticalSection( FCtlSect );
  fSendMsgComp := CreateEvent( nil, True, False, nil );
{$ENDIF}
  fDoLoop := Loop; // default disable thread loop

  // Create a Window for sync method
  SyncWindow := CreateWindow( ‘STATIC‘, ‘SyncWindow‘, WS_POPUP, 0, 0, 0, 0, 0,  0, hInstance, nil );
  SetWindowLong( SyncWindow, GWL_WNDPROC,  Longint( MakeObjectInstance( SyncWindowProc ) ) );

  FWaitHandle := CreateEvent( nil, True, False, nil );
  // Create Thread
  m_hThread := CreateThread( nil, 0, @msgThdInitialThreadProc, Self, 0, threadid );
  if m_hThread = 0 then
    raise EMsgThreadErr.Create( ‘不能创建线程。‘ );
  // Wait until thread Message Loop started
  WaitForSingleObject( FWaitHandle, INFINITE );
end;

{ ------------------------------------------------------------------------------ }
destructor TMsgThread.destroy;
begin
  if m_hThread <> 0 then
    QuitThread;
  WaitFor;

  // Free Sync Window
  DestroyWindow( SyncWindow );
  FreeObjectInstance( Pointer( GetWindowLong( SyncWindow, GWL_WNDPROC ) ) );

{$IFDEF USE_WINDOW_MESSAGE}
{$ELSE}
  FEventList.Free;
  DeleteCriticalSection( FCtlSect );
  CloseHandle( fSendMsgComp );
{$ENDIF}

  inherited;
end;

{ ////////////////////////////////////////////////////////////////////////////// }
procedure TMsgThread.Execute;
var
  mRet : Boolean;
  aRet : Boolean;
begin
{$IFDEF USE_WINDOW_MESSAGE}
  FMSGWin := CreateWindow( ‘STATIC‘, PChar( FWinName ), WS_POPUP, 0, 0, 0, 0, 0,  0, hInstance, nil );
  SetWindowLong( FMSGWin, GWL_WNDPROC,  Longint( MakeObjectInstance( MSGWinProc ) ) );
{$ELSE}
  PeekMessage( Msg, 0, WM_USER, WM_USER, PM_NOREMOVE );  // Force system alloc a msgQueue
{$ENDIF}

  mRet := True;
  try
    DoInit;

    // notify Conctructor can returen.
    SetEvent( FWaitHandle );
    CloseHandle( FWaitHandle );

    while mRet do // Message Loop
    begin
      if fDoLoop then
      begin
        aRet := PeekMessage( Msg, 0, 0, 0, PM_REMOVE );
        if aRet and ( Msg.Message <> WM_QUIT ) then
        begin
{$IFDEF USE_WINDOW_MESSAGE}
          TranslateMessage( Msg );
          DispatchMessage( Msg );
{$ELSE}
          uMsg.Msg := Msg.Message;
          uMsg.wParam := Msg.wParam;
          uMsg.lParam := Msg.lParam;
          DoProcessMsg( uMsg );
{$ENDIF}
          if Msg.Message = WM_QUIT then
            mRet := False;
        end;
{$IFNDEF USE_WINDOW_MESSAGE}
        ClearSendMsgEvent; // Clear SendMessage Event
{$ENDIF}
        DoMsgLoop;
      end else begin
        mRet := GetMessage( Msg, 0, 0, 0 );
        if mRet then
        begin
{$IFDEF USE_WINDOW_MESSAGE}
          TranslateMessage( Msg );
          DispatchMessage( Msg );
{$ELSE}
          uMsg.Msg := Msg.Message;
          uMsg.wParam := Msg.wParam;
          uMsg.lParam := Msg.lParam;
          DoProcessMsg( uMsg );
          ClearSendMsgEvent; // Clear SendMessage Event
{$ENDIF}
        end;
      end;
    end;
    DoUnInit;
{$IFDEF USE_WINDOW_MESSAGE}
    DestroyWindow( FMSGWin );
    FreeObjectInstance( Pointer( GetWindowLong( FMSGWin, GWL_WNDPROC ) ) );
{$ENDIF}
  except
    HandleException;
  end;
end;

{ ------------------------------------------------------------------------------ }
{$IFNDEF USE_WINDOW_MESSAGE}

procedure TMsgThread.ClearSendMsgEvent;
var
  aEvent : PHandle;
begin
  EnterCriticalSection( FCtlSect );
  try
    if FEventList.Count <> 0 then
    begin
      aEvent := FEventList.Items[ 0 ];
      if aEvent <> nil then
      begin
        SetEvent( aEvent^ );
        CloseHandle( aEvent^ );
        Dispose( aEvent );
        WaitForSingleObject( fSendMsgComp, INFINITE );
      end;
      FEventList.Delete( 0 );
    end;
  finally
    LeaveCriticalSection( FCtlSect );
  end;
end;
{$ENDIF}

{ ------------------------------------------------------------------------------ }
procedure TMsgThread.HandleException;
begin
  FException := Exception( ExceptObject ); // Get Current Exception object
  try
    if not( FException is EAbort ) then
      Synchronize( DoHandleException );
  finally
    FException := nil;
  end;
end;

{ ------------------------------------------------------------------------------ }
procedure TMsgThread.DoHandleException;
begin
  if FException is Exception then
    Application.ShowException( FException )
  else
    sysutils.ShowException( FException, nil );
end;

{ ////////////////////////////////////////////////////////////////////////////// }
{$IFDEF USE_WINDOW_MESSAGE}

procedure TMsgThread.MSGWinProc( var Message : TMessage );
begin
  DoProcessMsg( message );
  if message.Msg < WM_USER then
    with message do
      Result := DefWindowProc( FMSGWin, Msg, wParam, lParam );
end;
{$ENDIF}

{ ------------------------------------------------------------------------------ }
procedure TMsgThread.DoProcessMsg( var Msg : TMessage );
begin

end;

{ ------------------------------------------------------------------------------ }
procedure TMsgThread.ProcessMessage;
{$IFNDEF USE_WINDOW_MESSAGE}
var
  uMsg : TMessage;
{$ENDIF}
begin
  while PeekMessage( Msg, 0, 0, 0, PM_REMOVE ) do
    if Msg.Message <> WM_QUIT then
    begin
{$IFDEF USE_WINDOW_MESSAGE}
      TranslateMessage( Msg );
      DispatchMessage( Msg );
{$ELSE}
      uMsg.Msg := Msg.Message;
      uMsg.wParam := Msg.wParam;
      uMsg.lParam := Msg.lParam;
      DoProcessMsg( uMsg );
{$ENDIF}
    end;
end;

{ ////////////////////////////////////////////////////////////////////////////// }
procedure TMsgThread.DoInit;
begin
end;

procedure TMsgThread.DoUnInit;
begin
end;

procedure TMsgThread.DoMsgLoop;
begin
  Sleep( 0 );
end;

{ ////////////////////////////////////////////////////////////////////////////// }
function TMsgThread.ThreadExists : BOOL;
begin
  if m_hThread = 0 then
    Result := False
  else
    Result := True;
end;

{ ------------------------------------------------------------------------------ }
procedure TMsgThread.QuitThread;
begin
{$IFDEF USE_WINDOW_MESSAGE}
  PostMessage( FMSGWin, WM_QUIT, 0, 0 );
{$ELSE}
  PostThreadMessage( threadid, WM_QUIT, 0, 0 );
{$ENDIF}
end;

{ ------------------------------------------------------------------------------ }
procedure TMsgThread.SetDoLoop( const Value : Boolean );
begin
  if Value = fDoLoop then
    Exit;
  fDoLoop := Value;
  if fDoLoop then
    PostMsg( WM_USER, 0, 0 );
end;

{ ------------------------------------------------------------------------------ }
function TMsgThread.WaitTimeOut( timeout : DWORD ) : Longword;
var
  xStart : Cardinal;
  H : THandle;
begin
  H := m_hThread;
  xStart := GetTickCount;
  while WaitForSingleObject( H, 10 ) = WAIT_TIMEOUT do
  begin
    Application.ProcessMessages;
    if GetTickCount > ( xStart + timeout ) then
    begin
      TerminateThread( H, 0 );
      Break;
    end;
  end;
  GetExitCodeThread( H, Result );
end;

{ ------------------------------------------------------------------------------ }
function TMsgThread.WaitFor : Longword;
var
  Msg : TMsg;
  H : THandle;
begin
  H := m_hThread;
  if GetCurrentThreadID = MainThreadID then
    while MsgWaitForMultipleObjects( 1, H, False, INFINITE, QS_SENDMESSAGE )
      = WAIT_OBJECT_0 + 1 do
      PeekMessage( Msg, 0, 0, 0, PM_NOREMOVE )
  else
    WaitForSingleObject( H, INFINITE );
  GetExitCodeThread( H, Result );
end;

{ ------------------------------------------------------------------------------ }
procedure TMsgThread.PostMsg( Msg : Cardinal; wParam, lParam : Integer );
begin
{$IFDEF USE_WINDOW_MESSAGE}
  PostMessage( FMSGWin, Msg, wParam, lParam );
{$ELSE}
  EnterCriticalSection( FCtlSect );
  try
    FEventList.Add( nil );
    PostThreadMessage( threadid, Msg, wParam, lParam );
  finally
    LeaveCriticalSection( FCtlSect );
  end;
{$ENDIF}
end;

{ ------------------------------------------------------------------------------ }
function TMsgThread.SendMsg( Msg : Cardinal; wParam, lParam : Integer )
  : Integer;
{$IFNDEF USE_WINDOW_MESSAGE}
var
  aEvent : PHandle;
{$ENDIF}
begin
{$IFDEF USE_WINDOW_MESSAGE}
  Result := SendMessage( FMSGWin, Msg, wParam, lParam );
{$ELSE}
  EnterCriticalSection( FCtlSect );
  try
    New( aEvent );
    aEvent^ := CreateEvent( nil, True, False, nil );
    FEventList.Add( aEvent );
    PostThreadMessage( threadid, Msg, wParam, lParam );
  finally
    LeaveCriticalSection( FCtlSect );
  end;
  WaitForSingleObject( aEvent^, INFINITE );
  Result := uMsg.Result;
  SetEvent( fSendMsgComp );
{$ENDIF}
end;

{ ------------------------------------------------------------------------------ }
procedure TMsgThread.Synchronize( syncMethod : TMsgThreadMethod );
begin
  FMethod := syncMethod;
  SendMessage( SyncWindow, NM_EXECPROC, 0, Longint( Self ) );
end;

{ ------------------------------------------------------------------------------ }
procedure TMsgThread.SyncWindowProc( var Message : TMessage );
begin
  case message.Msg of
    NM_EXECPROC :
      with TMsgThread( message.lParam ) do
      begin
        message.Result := 0;
        try
          FMethod;
        except
          raise EMsgThreadErr.Create( ‘执行同步线程方法错误。‘ );
        end;
      end;
  else
    message.Result := DefWindowProc( SyncWindow, message.Msg, message.wParam,
      message.lParam );
  end;
end;

end.
时间: 2024-10-27 07:15:29

在delphi线程中实现消息循环的相关文章

TMsgThread, TCommThread -- 在delphi线程中实现消息循环(105篇博客,好多研究消息的文章)

在delphi线程中实现消息循环 在delphi线程中实现消息循环 Delphi的TThread类使用很方便,但是有时候我们需要在线程类中使用消息循环,delphi没有提供. 花了两天的事件研究了一下win32的消息系统,写了一个线程内消息循环的测试. 但是没有具体应用过,贴出来给有这方面需求的DFW参考一下.希望大家和我讨论. {----------------------------------------------------------------------------- Unit

Chromium线程模型、消息循环

多线程的麻烦 多线程编程是一件麻烦的事,相信很多人深有体会.执行顺序的不确定性,资源的并发访问一直困扰着众多程序员.解决多线程编程问题的方法分为两类:一是对并发访问的资源直接加锁:二是避免并发访问资源:Chromium采用第二种思想来设计多线程模型,通过在线程之间传递消息来实现跨进程通讯. 设计原则 Chromium希望尽量保持UI处于响应状态.为此遵循如下设计原则: 1 不在UI线程上执行任何阻塞I/O操作,以及其它耗时操作. 2 少用锁和线程安全对象 3 避免阻塞I/O线程 4 线程之间不要

安卓中的消息循环机制Handler及Looper详解

我们知道安卓中的UI线程不是线程安全的,我们不能在UI线程中进行耗时操作,通常我们的做法是开启一个子线程在子线程中处理耗时操作,但是安卓规定不允许在子线程中进行UI的更新操作,通常我们会通过Handler机制来完成该功能,即当子线程中耗时操作完成后,在子线程中通过Handler向主线程发送消息,在主线程中的Handler的handleMessage方法中处理接受到的消息.这就是安卓中的消息机制,安卓中的消息机制主要是指Handler的运行机制,但是Handler的运行需要底层的MessageQu

Delphi7从子线程中发送消息到主线程触发事件执行

[转载]Delphi7从子线程中发送消息到主线程触发事件执行 在对数据库的操作时,有时要用一个子线程来进行后台的数据操作.比如说数据备份,转档什么的.在主窗口还能同是进行其它操作.而有时后台每处理一个数据文件,要向主窗口发送消息,让主窗口实时显示处理进度在窗口上(可视),同时进行日志处理等.我用的是下面的方法: [1]用到的API函数: RegisterWindowsMessage ---------------------- 函数功能:该函数定义一个新的窗口消息,该消息确保在系统中是唯一的.返

iOS中的消息循环

什么是消息循环: 消息循环就是NSRunloop这个类 ,每个线程都有自己的消息循环. 主线程的消息循环默认是开启的(需要去检测事件),子线程默认关闭(通常不需要子线程检测事件). 消息循环的目的: 保证程序不退出.负责处理输入事件(输入源和Timer源).如果没有事件发生则会让程序处于休眠状态. 消息循环的两种运行模式:NSDefaultRunloopModel和 NSRunloopCommonModels 消息循环需要在一定的模式下才能相匹配,当在消息循环中添加了一个定时源时, 消息循环的模

delphi XE7 中的消息

在delphi XE7的程序开发中,消息机制保证进程间的通信. 在程序中,消息来自: 1)系统: 通知你的程序用户输入,涂画以及其他的系统范围的事件: 2)你的程序:不同的程序部分之间的通信信息.   什么时候使用消息?       当你写程序的时候,通常是使用事件而不是消息,控件触发事件让你来处理而其内部则是通过消息机制处理的(对你是透明的),从而实现和其他控件之间的通信以及处理系统信息.       不管咋样,有时你确实需要使用消息. 你的程序必须响应一个系统或者框架没有定义的事件(或者没有

Windows 消息循环(2) - WPF中的消息循环

接上文: Windows 消息循环(1) - 概览 win32/MFC/WinForm/WPF 都依靠消息循环驱动,让程序跑起来. 本文介绍 WPF 中是如何使用消息循环来驱动程序的. 4 消息循环在 WPF 中的应用 4.1 引入 只听说过 Dispatcher ,哪里来的消息循环? 先瞧一眼 WPF 启动运行堆栈: 可以发现 PushFrameImpl 这个方法. 去看其源码,就发现了熟悉的消息循环 : 可以理解为:Dispatcher 对消息循环的操作进行了"封装" . 那,Di

事件循环和线程没有必然关系(就像Windows子线程默认没有消息循环一样),模态对话框和事件循环也没有必然关系(QWidget直接就可以)

周末天冷,索性把电脑抱到床上上网,这几天看了 dbzhang800 博客关于 Qt 事件循环的几篇 Blog,发现自己对 Qt 的事件循环有不少误解.从来只看到现象,这次借 dbzhang800 的博客,就代码论事,因此了解到一些 Qt 深层的实现,虽然是在 Qt 庞大的构架里只算的是冰山的一角,确让人颇为收益. 从 dbzhang800 的博客中转载两篇关于事件循环的文章,放在一起,写作备忘. 再次提到的一点是:事件循环和线程没有必然关系. QThread 的 run() 方法始终是在一个单独

Delphi 线程间发送消息

线程B向线程A发送消息,SendMessage(线程A句柄, msg, wParam参数, lParam参数); 线程A重写WndProc(var Message: TMessage); 1 { 线程B发送消息 } 2 unit ThreadUnit; 3 4 interface 5 6 uses 7 Classes, ActiveX, businessService, Dialogs, Constant, Windows, Messages, SysUtils; 8 9 type 10 TUp