Link to home
Start Free TrialLog in
Avatar of jtbalogh
jtbalogh

asked on

Windows messaging alternatives to Postmessage and Sendmessage

I am looking for an alternative to using PostMessage or SendMessage to notify a thread in my C++ OCX application to respond in more real time. My music application currently uses them to trigger the thread to run, fire and return from an event in a remote Visual Basic executable repeatedly. My application would like to fire the event in real time (less than one millisecond) without being interrupted by delays in the operating system and by other system and thread messaging queues. Works perfectly so far on my 500MHz Pentium III, but not if I start to click on things in any application.

The postmessage or sendmessage is subject to delays in the operating system and can respond slow when windows, objects on windows, and menus in windows are being moved, minimized, clicked, selected with mouse, etc. This is normal for windows messaging when dealing with queued and nonqueued messages. But it has made my real time music application worthless.

Alternatives I have tried:
- I would not be able to fire the event directly because OLE automation error occurs.
- I would not be able to fire the event in another thread with AfxBeginThread() because OLE automation error occurs. Most likely like firing the event directly.
- I would not be able to fire the event with a timer because it can also be delayed.
- I would not be able to fire the event with a timer because I would need less than 1 miillisecond intervals for real-time processing.

If you have a question about the the OLE automation and unhandled error, then go no further. It occurs whenever it reaches simple code in VB that it does not like. Like On Error Goto, Debug.Print, Left() and Trim() string functions, Sleep and other Windows API, Stop, stepping through code in debugger, and many others. No solution as of this date.

I am sure there is more information I can provide, but I can not think of it right now.
Thank you for your help.
Avatar of AndyAinscow
AndyAinscow
Flag of Switzerland image

There are multimedia timers with a higher degree of accuracy than the 'normal' timer.  Could they help?
On NT based OP systems you can manipulate thread priorities and set it as higher priority.
Avatar of AlexFM
AlexFM

Multi-threading + events is native alternative to Windows messages. I think you should ask separate question about automation error, COM has a lot of mysterious multi-threading stuff like thread models, and this problem can be solved, I hope.
In any case, I don't beleive you can get less that millisecond reaction time in the user mode, Windows is not real-time system.
There is something brutal way to increase the current program priority, this can improve it's performance when you are working with another programs.
PostMessage is delayed also when you move mouse quickly over program window, giving a lot of WM_MOUSEMOVE messages. To solve this problem you can overwrite application message loop giving priority to desired messages.

forget it,
take DOS ;-)

(this is only a half joke)
Avatar of jtbalogh

ASKER

Another way to look at it, the delay I am trying to avoid (which can be from <1 milliseconds to as much as two seconds with postmessage) is to speed up moving control from a secondary thread to the main thread. Only then can I continue and fire the event to VB as usual. (Refer to Microsoft article Q196026, Q157437, Dr. GUI #54)

TO: AndyAinscow, I do use multimedia timers and threads to help speed up processing. But the OLE problem occurs because they are on a secondary thread and not allowed to access the single-threaded environment of VB easily. Postmessage and sendmessage helped return control back to the main thread at least, as was very fast, clocked less than 1 msec. But did slow down even to as much as one or two seconds when I clicked on things on the application or waited for a window to redraw meanwhile.

TO: AlexFM, COM is an alternative to switch from the secondary thread to the main thread quickly with CoInitializeEx() or marshalling, but then my app has to rely on DCOM v1.2 or some external component. I do not use an application message loop to try and filter messages because I do not see how I can write code in VB to change default behavior of the form data type. In general though, I would not want to filter too many messages because I would like to be able to click on things in the application or get tooltips while it is running.

Would this be an alternative -- for the main thread to loop and poll for a variable changed by the secondary thread to see if it should fire an event ? During the loop the main thread will release resources so it does not hog up the system as much.
Increasing points ... (to 300)

Another way to look at the problem:

The delay is caused when vb would even redraw the screen first before it relinquishes control back to the message that I posted. I assumed that WM_PAINT messages were processed after all other messages in a message queue and this is not the case here for some reason. The delay is also caused when clicking on a scrollbar in totally different windows application before it relinquishes control back to the message that I posted. Argh...

I am sure its only postmessage or sendmessage causing the delays. For example, I traced and compared the time before triggering sendmessage and once the message map function in the main thread responded. I can trace no other code running in the middle. The time difference ends up usually 1 millisecond, but immediately jumps to 10 miliseconds, 100 milliseconds, 1000 milliseconds, etc. depending on what I click on.

Notice that the program will run fast using postmessage and not waiting for the event to trigger, which is how postmessage works by design. This is nice to solve the delay problem but then most passed data will be lost with each iteration of postmessage until the VB application finally responds. I wrote a routine to store data in a buffer to help, but we still return to the original problem: the delay still exists and the VB app can not use the data until after 100 milliseconds to two seconds in the worst case.

-----------------------------------------------
Please do not focus on the one millisecond response time I used as an example. I even tried my application to just fire events every second. But postmessage can still delay the application an extra 100 milliseconds to two seconds when I start to click on things in the application, or click on other windows program running in the background.

Please do not focus on much code in the VB end of the application because I stripped almost everything in the code and objects on the form except the event method. Which itself contains nothing as well. Quite bare bones.

-----------------------------------------------
GOAL: my OCX object runs very fast even when I activate calculations a thousand times more complicated and complex to be held back by a slow postmessage or sendmessage API function. The OCX object, in its most basic implementation, is only doing one simple thing to update a progress bar. In its most advanced implementation, it can start to do a whole series of processes. For both implementations, the delays are the same and it is annoying.

-----------------------------------------------
TO: AndyAinscow

Thanks for your insight. I also use QueryPerformanceCounter() and QueryPerformanceFrequency() to help verify speed and location of delays. Now that I know where the delays are, people have told me it is not usually worth to optimize and squeeze speed out of every possible piece of code one writes. However it is frustrating when the only piece of code left to optimize is a thousand times slower than the rest of the application.

-----------------------------------------------
TO: AlexFM

Thanks for your insight. I tried to look for some way to interrupt or change the behavior of how messages (like WM_PAINT) can be filtered, monitored, etc. But I do not see any place to put code to handle this between the sendmessage I execute and the message map function in the main thread that responds to it.

-----------------------------------------------
Regarding the alternative - for the main thread to loop and poll

Answer: Never mind

It runs in real time now and eats less than %1 cpu usage, but I seemed to have lost messages like the keyboard input to the forms I have open. Only the mouse seems to work. I am sure if I knew more about C+, I could troubleshoot and get that to work. The trick must have been in how I released resources with peekmessage() to keep main thread polling successfully. However that leads me back to dealing with windows messaging again which I did not want anymore.

-----------------------------------------------
Would this be an alternative?

Fire the event directly from a secondary thread to vb and only execute code that saves passed parameters to vb variables, triggers a multimedia timer (vb timer is inadequate) with interval one millisecond and terminates the event. Most commands would fail in vb but saving to public variables works. The timer, which is now in the main thread of the application, can process the rest of the code that would have normally been executed by the event, and set a property in the OCX object that it has finished and ready for any other events.

-----------------------------------------------
Regarding the alternative - for firing directly.

Answer: Never mind.

It is better than windows messaging and eats less than %1 cpu usage, but requires workaround code in vb that seems awkward against everything I learned about handling events.

-----------------------------------------------
Would this be an alternative?

My application can run with less noticible delays on a faster 1.8 GHz computer with more 256meg memory and faster nvidia video 32meg card. But has more noticible delays on a regular P2-500MHz computer. Unfortunately, requiring my application to run on faster computers is very disappointing ...

-----------------------------------------------
Argh. Seems like trying to get straightforward code to trigger events in more real time from an OCX object to VB is becoming a lost dream ...

Thanks.
I have one more alternative I am trying ... wish me luck.

:')
Haven't heard from anyone lately. If you would like points, please comment.

-----------------------------------------------
Would these be two alternatives?

1. In the OCX, should I use postmessage, not sendmessage, so it does not wait for the event. Then use wait functions like an event object and WaitForSingleObjectEx() to wait for the event to finish, instead of using windows messaging functions like peekmessage() or getmessage() to check if message was processed by the event. Now no additional commands are in the code anymore to be affected by windows messaging except the event itself responding.

2. Can the VB app, since already in a main thread, execute a method in the OCX to fire any pending events manually if postmessage is ever accidently delayed. No need to worry about moving control from a secondary thread to the main thread anymore. No need to use DoEvents and hope that events will run.

Answer: Supposedly Solved.

It runs in real time now (within one millisecond most of the time), eats less than %1 cpu usage, and is barely affected by windows messaging anymore from the application or any other programs in the background on a slow computer. Even WM_PAINT messages do not affect it anymore.

The additional method (item 2) helps fire the events while in a long and fast loop in the VB app without having to use DoEvents or other way to release resources. DoEvents would have been affected and delayed by windows messaging anyway. Note that item 2 is not worth using in VB timer objects because the timers are affected and delayed by windows messaging anyway. Note that item 2 is not worth using in a multimedia timer because those run in a secondary thread and we would still have to find a way to return control back to the main thread anyway.

-----------------------------------------------
TO: AlexFM and AndyAinsco

Your suggestion to increase thread priority of the VB app could help even more if you know how so that even windows messaging with system windows like task manager with ctrl-alt-del do not affect my application. Currently that is the only window left that I have seen that slows the app down. Any other suggestions to verify my assumptions and tests from some of the comments I mentioned would be helpful too.

-----------------------------------------------
Sample MFC code in OCX to initialize public storage:

        #define WM_MYEVENT WM_USER+6;
        HWND hwndQueueOwner;
        HANDLE m_hwndFireWaitEvent;
        BOOL m_isCurrentFire;
        BOOL m_isCurrentFirePending;
        hwndWindowOwner = m_cMyAppWnd.m_hWnd;
        m_hwndFireWaitEvent = 0;
        m_isCurrentFire = FALSE;
        m_isCurrentFirePending = FALSE;
        // set up OnMyEvent() and WM_MYEVENT message map from classwizard, not shown here
        // set up FireMyEvent() event from classwizard, not shown here
        // set up MyEvent() method in VB app, not shown here
        // set up GetMessageEventPending() method from classwizard, not shown here
        // set up SetMessageEventPending() method from classwizard, not shown here

Sample code to initialize event objects:

        BOOL isErr;
        // Kill event objects
        isErr = SetEvent(m_hwndFireWaitEvent); // signaled to terminate wait, if not already
        isErr = CloseHandle(m_hwndFireWaitEvent);
        //if ( isErr == FALSE ) { } // do not stop on errors while closing
        m_hwndFireWaitEvent = 0; // not needed anymore

        // Create event objects
        m_hwndFireWaitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
        // Syntax: no security, manual reset, initial state nonsignaled, no name
        if ( m_hwndFireWaitEvent == NULL ) { } // error

Sample code to fire event from a secondary thread:

        BOOL isErr;
        m_isCurrentFire = TRUE; // one instance of event in progress
        m_isCurrentFirePending = TRUE; // triggering event in progress

        isErr = ResetEvent(m_hwndFireWaitEvent); // nonsignaled to wait
        if( isErr == FALSE ) { } // error

        mErr = ::PostMessage( hwndWindowOwner, WM_MYEVENT, 0, 0 );
        // ::SendMessageCallback(), ::SendNotifyMessage(), alternatives would get more priority
        if( mErr == 0 ) { } // error

        // Wait for previous fired event to process in remote program before continuing.
        while( 1 ) {
            wErr = WaitForSingleObjectEx(m_hwndFireWaitEvent, 1, FALSE);

            // Time out after a short time because another procedure might have fired
            // the event first and postmessage was ignored. Otherwise it would never
            // return.

            // Time out after a short time because postmessage might be so fast
            // since it was ignored before that it already finished firing event
            // before WaitForSingleObjectEx() even had a chance to even run.
            // Otherwise it would never return.

            if( wErr == WAIT_OBJECT_0 || wErr == WAIT_ABANDONED ) break; // terminated normally
            if( m_isCurrentFire == FALSE ) break; // terminated normally
            //if( m_isCurrentFirePending == FALSE ) break; // not applicable
        }
        //wErr = WaitForSingleObjectEx(m_hwndFireWaitEvent, INFINITE, FALSE); // alternative
        //wErr = WaitForSingleObject(m_hwndFireWaitEvent, INFINITE); // alternative

        m_isCurrentFire = FALSE; // not needed anymore

Sample code in main thread that responds to firing:

        long CMyAppCtrl::OnMyEvent( WPARAM wp, LPARAM lp )
        {
            BOOL isErr;

            if( m_isCurrentFirePending == FALSE ) {
                // Already running
                // either triggered by postmessage or other function
                // whichever arrived first, does not matter

            } else {
                m_isCurrentFirePending = FALSE; // not needed anymore
                FireMyEvent(variable1, variable2);

                isErr = SetEvent(m_hwndFireWaitEvent); // signaled to terminate wait, if not already
                if( isErr == FALSE ) { } // error
            }

            return(0);
        }

Sample code for item 2:

        BOOL CMyAppCtrl::GetMessageEventPending()
        {
            if( m_isCurrentFirePending == TRUE ) {
                isReturnResult = TRUE;
            }
        return isReturnResult;
        }

        BOOL CMyAppCtrl::SetMessageEventPending(BOOL bNewValue)
        {
            // safe to fire directly since already from main thread in VB app
            //if( m_isCurrentFirePending == TRUE ) { // already checked in routine later
            OnMyEvent( (WPARAM)0, (LPARAM)0 );
            return;
        }

I hope thats all of it and no misspelling. I renamed some variables because I used much longer names. If I missed some tricks that might make it work. Oh well. At least I was on the right track ...

Thanks.
Joseph (JTBalogh)
Just following up. Have not heard from anyone if this is a good approach to minimize windows messaging and still be able to communicate between threads.

Also how your suggestion to increase thread priority of the VB app would work so that any windows messaging can occur with less delays.

ASKER CERTIFIED SOLUTION
Avatar of jtbalogh
jtbalogh

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
Followup:

Found the code to help make my multimedia timer with higher thread priority. It collects data and fires the sendnotifymessage in even more real time. E.g.

void FAR PASCAL TimerProcessThreadOneShot( UINT wID, UINT wUser, DWORD dwUser, DWORD dw1, DWORD dw2 )
{
    SetThreadPriority(GetCurrentThread(), 15); // priority critical, scale -15,-2,-1,0,1,2,15
    SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); // alternative
    //SetThreadPriority(GetCurrentThread(), 0); // default, has delays occasionally
    //SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); // alternative
    // Safe since, does not inherit the same priority as the executable app is using,
    // and is independent, and retains the priority as long as program is running.

    // Testing priority levels
    long ggg = GetThreadPriority(GetCurrentThread());
    //long ggg2 = GetPriorityClass(GetCurrentThread()); // not recommended
    _asm int 3 // breakpoint and stop

    // Now that this procedure has a high priority, safety features were added (not shown here)
    // so that the timer releases background resources and not hog it all up sometimes,
    // so that the timer releases background resources at the same time as other timers could sometimes (synchronized)

    etc.

I am a happy camper ...