Link to home
Start Free TrialLog in
Avatar of ahalya
ahalyaFlag for Canada

asked on

SendMessage vs PostMessage (and lost messages..)

I have a thread communicating with a window via messages.

I want to use PostMessage, but for some reason I seem to lose many of the messages sent via postmessage.  This happens occasionally, and seems to be unpredictable.  

If I use SendMessage, I receive every message, but am struck if SendMessage is called while I'm waiting inside a WaitForSingleObject. (I use a semaphore and WaitForSingleObject function to determine whether the thread is active or not).

Post Message, I lose many, and it appears the MC_ThreadEnd message is lost most of the time This is the last message sent just before the thread terminates).

I have attached a sample log created from these communications to show how I track the messages. They aretimed tightly (nearest micro second in the field following :: symbol in the file)

comments, Solutions, Alternatives please.  (MY OS is XP SP2 with all patches/updates applied. Compiler Delphi 7 with all patches applied).
///////////////////////////////////////////////////////////////////
//  These two procedures process WM_Debug, & WM_Thread messages  //
///////////////////////////////////////////////////////////////////
procedure TMainForm.DebugMessages(var M: TMessage);
 
var ErrMsg: array[0..255] of char;
    StrSize: word;
 
begin;
  StrSize := GlobalGetAtomName(M.LParam, ErrMsg, 256);
  GlobalDeleteAtom(M.LParam);
  if StrSize > 0 then
  begin;
    case M.WParam of
    MC_ErrMsg:    LogInfo(nil, string(ErrMsg));
    MC_DebugMsg : LogInfo(nil, string(ErrMsg));
    MC_MutexMsg : LogInfo(nil, string(ErrMsg));
  end;
else
    TimedMessageDlg('Error message not found !', mtError,[mbOk], 0, 10, mrOk);
end;
 
 
procedure TMainForm.ThreadMessages(var M: TMessage);
 
begin;
  case M.WParam of
    MC_MotorStart : LogInfo(nil, Format('%s stepper motor started', [FeedbackChannelNames[M.LParam - md_ref+countAO]]));
    MC_MotorStop  : LogInfo(nil, Format('%s stepper motor stopped', [FeedbackChannelNames[M.LParam - md_ref+countAO]]));
 
    MC_ThreadCreate: LogInfo(nil, Format('%s. New Thread started', [GetAtomMsg]));
    MC_ThreadQuit  : LogInfo(nil, Format('%s. Thread Quit Request', [GetAtomMsg]));
    MC_ThreadProc  : LogInfo(nil, Format('%s. ThreadProc activated', [GetAtomMsg]));
    MC_ThreadInfo  : LogInfo(nil, Format('%s', [GetAtomMsg]));
    MC_ThreadEnd:
      begin;
        LogInfo(nil, Format('%s. Thread Ended', [GetAtomMsg]));
        dec(ThreadCount);
        IsMultiThread := (ThreadCount > 1);
        CloseHandle(hEPTThread);
        hEPTThread := 0;
      end;
  end;
 
  Caption := Format('HCT DAQ [%d threads active]', [ThreadCount]);
  if (M.WParam = MC_MotorStop) then DAQModule.SetCWButtonState(M.LParam, false);
  if (M.WParam = MC_MotorStart) then DAQModule.SetCWButtonState(M.LParam, true);
end;
 
/////////////////////////////////////////////////////////////////
//   Messages are sent from a separate thread. Example below   //
////////////////////////////////////////////////////////////////
 
<part of the Thread function>
 
    loop := 0; steps := 10;
    while (not TEPTTargets(Ptr^).QuitThread) and (loop < steps) do
    begin;
      updateVariables(steps - loop);//Update if EPTs active
      for EPTransducer := ep_Vert to ep_IntP do ApplyOutput(EPTransducer);
 
      PostMessage(hMainform, WM_Debug, MC_EPTMsg,  AddGlobalAtomFmt('%s <- Calling Sleep', [getPCTAsString]));
      //Break the Sleep command into 4 individual sleeps to avoid a long hang-up
      if not TEPTTargets(Ptr^).QuitThread then sleep(TEPTTargets(Ptr^).TimeDelay div 4);
      if not TEPTTargets(Ptr^).QuitThread then sleep(TEPTTargets(Ptr^).TimeDelay div 4);
      if not TEPTTargets(Ptr^).QuitThread then sleep(TEPTTargets(Ptr^).TimeDelay div 4);
      if not TEPTTargets(Ptr^).QuitThread then sleep(TEPTTargets(Ptr^).TimeDelay - (3 * TEPTTargets(Ptr^).TimeDelay div 4));
      PostMessage(hMainform, WM_Debug, MC_EPTMsg,  AddGlobalAtomFmt('%s -> Done Sleep', [getPCTAsString]));
 
      inc(loop);
    end;
  except on e:exception do
    PostMessage(hMainform, WM_Debug, MC_ErrMsg, GlobalAddAtom(pchar(E.Message)));
    end;
 
  if TEPTTargets(Ptr^).QuitThread then
     PostMessage(hMainform, WM_Thread, MC_ThreadQuit, AddGlobalAtomFmt('%s <EPT>', [getPCTAsString]));
  PostMessage(hMainform, WM_Thread, MC_ThreadEnd, AddGlobalAtomFmt('%s', [getPCTAsString]));

Open in new window

hct.execution.log
Avatar of bernani
bernani
Flag of Belgium image

Maybe not relevant, but remember that even if SendMessage and PostMessage are used in a similar way, they return values are different.

SendMessage returns the RESULT VALUE of the message being processed
PostMessage returns only a BOOL indicating wether the message was placed in the target window's queue.

SendMessage (like Peform) sends the message directly and wait until the message is processed before returning
PostMessage sends the message to the message queue and returns immediately.

That's maybe the reason why you receive all the messages with SendMessage and loose many with PostMessage.

Avatar of ahalya

ASKER

I am familiar with the differences between SendMessage & PostMessage.

The only reason why PostMessage would fail is if the message queue if full, isn't it. ?  Are the any other reasons why a PostMessage would fail ?

An interesting thread ;)

Found this:
A message posted with PostMessage() is put into the target thread's message
queue and is not processed right away.  
- If the target thread does not pump its queue regularly, or does not have a handler for the message, then the message can go unprocessed.  
- If you post to an HWND, and the HWND is destroyed before the message is removed from the queue, the message will go unprocessed.  
- If the queue is full, PostMessage() will fail and the message will not be posted at all.

from a discussion about 'PostMessage or Synchronize' :
http://newsgroups.cryer.info/borland/public.delphi.nativeapi.win32/200707/0707308648.html


Avatar of ahalya

ASKER

My code is fairly through (well, it is difficult to be sure when I have 20,000+ lines of source in this project, but I am fairly certain about the message loops & processing of messages).

I have extensive checks to ensure none of the above conditions would occur. (except the last one about message queue being full. How likely will it be ?   Within that loop, I am posting about half a dozen messages in two seconds.)

There is one uncertainty in that if any other process (completely unknown to me) keeps injecting messages into my message queue (with broadcast messages) then the queue could be made to become full.

Scary thought though isn't it ? If that can be done, anyone can easily bring any windows program to its knees just by pumping messages in to it.
Avatar of ahalya

ASKER

Just to add:
I attached a log file (created with SendMessage) to show how well I log what is happening in the system. I keep track of when the messages are sent, and when they are processed. you can review it, if you like.

An interesting think I found by creating this log file (with micro second timer) was that when messages are post from one thread to another, the  receiving thread doesn't receive them in the same order in which they are posted.   Don't know why ?.
ASKER CERTIFIED SOLUTION
Avatar of bernani
bernani
Flag of Belgium image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of MerijnB
from MSDN help on PostMessage():

Return Value

    If the function succeeds, the return value is nonzero.

    If the function fails, the return value is zero. To get extended error information, call GetLastError.

Did you check the return values of the call (and GetLastError()) when the message does not arrive?
Avatar of ahalya

ASKER

Yes, I have and there are instances when PostMessage returns true, but the message isn't processed by the application.

there is also PostThreadMessage which can post a message to the thread you want.
With this you can post messages to the main thread.
You can use GetCurrentThreadId in the main thread to get that id

from mainthread
MainThreadId := GetCurrentThreadId;

give this to other threads so they know what thread to post back to.

unless your staying the same thread off course

Avatar of ahalya

ASKER

There is no real solution to the problem yet, but still thank you letting me know, it`s not just my code (and others have seen this before). I was going crazy trying to debug the code thinking I had a bug :-)
Avatar of ahalya

ASKER

This question seems to have been reviewed many times, so I want to provide an update.

The mishap has significantly been reduced (and almost eliminated ?) when the I called PostMessage in a loop until it returned true. (of course you want to have a way out, so I timed my loop and continue trying for s few seconds). Bernani's comments seem to have indicated the problem.

See below for the function I am using now.


function MyPostMsg(h:HWND; MsgID: UINT; wParam: WPARAM; lParam: LPARAM): BOOL; stdcall;
 
var StartTick, Delta: int64;
    c : integer;
 
begin;
  StartTick := GetPerformanceCounterTick;
  repeat
    result := PostMessage(h, MsgID, wParam, lParam);
    if not result then
    begin;
     c := GetLastError;
     if c = ERROR_INVALID_WINDOW_HANDLE {1400} then break;
     {$IFOPT D+}
       Windows.MessageBox(0, PChar(Format('%s [Code: %x]', [SysErrorMessage(c), c])), 'Post Message Error. Will try again', MB_OK);
     {$ENDIF}
    end;
    Delta :=  GetPerformanceCounterTick - StartTick;
  until result or (Delta > PostMessageMaxDelay);
  if not result then //didn't post message.
  begin;
    c := GetLastError;
    Windows.MessageBox(0, PChar(Format('%s [Code: %x]', [SysErrorMessage(c), c])), 'Post Message Error', MB_OK);
  end;
end;

Open in new window