views:

177

answers:

5

I've got an app working semi-background stuff in the GUI thread. Another thread won't work and a timer won't work. But I want the app to be responsive while I'm going my 'background' work.

I cound use application.doevents - but that gives me 3 bad choices:

  • loop with no sleeps - end peg an entire CPU (my task waits on stuff besides calcs & I/O so this matters)
  • loop with a sleep - then the app slows down
  • Try to make a 'smart sleep' loop that speeds up when lots of messages happen (check for messages in messagefilter function)

In the good ole' days in sdk I can do this:

while(PeekMessage(&msg,0,0,0,PM_REMOVE) && still_working)  
{
       TranslateMessage( &msg );
       DispatchMessage(&msg);
      // do a bit of my background work with each message here, quit the loop when done.
}

Wnd have it be a bit better than DoEvents as it will process multiple messages in a row, but I still have to use 'smart sleep' to save cpu - I'd like to avoid that.

Also is there a way to do a primary message loop?

    MSG msg; 
    while( GetMessage( &msg, NULL, 0, 0 ) )
    {
        TranslateMessage( &msg );
        DispatchMessage( &msg );

    }

Is there a proper .NET equivalent, or do I just have to p/invoke the sdk stuff? Thanks!

EDIT Winforms. For all you answer-ers wondering why I can't use application.run, or threads, what I want to do is handled the OnUnhandledException event, then leave my app open for a while while some winforms stuff closes it down gracefully and tells my watchdog program to restart the app. after allowing the user to finish a critical process, too. Yes, I know people thing that a program getting OnUnhandledException deserves to die (obviously MS), there are 'dangerous' things that could happen; none of them are as dangerous, however, as the wrath of my users.

Edit #2 - Looking for vb.net sourcecode exact translations of the above two functions.

Maybe somebody who's good with p/invoke and declares could score a quick bounty ;-)

+1  A: 

If you are using WinForms then simply use the Application.Run() API to setup a proper message loop.

JaredPar
A: 

Take a look at BackgroundWorker, it's worked well for me in the past...

joshperry
+1  A: 

Another thread won't work

Why? That or BackgroundWorker (ThreadPool usage) is eaxctly what you need.

TomTom
I would also be interested to know why the question states "Another thread won't work and a timer won't work".
louisgab
+2  A: 

You seem to have two reasons for wanting to write your own message loop:

1. To avoid "end pegging an entire CPU".

This is generally a worthy goal, but if you are performing some computation, using the CPU is entirely appropriate. Assuming normal process priority, it will not starve other processes of the CPU. If you deliberately throttle your computation, it will run slower than necessary, annoying the user.

Your "good ole' days" version could probably be duplicated in .NET, but I don't see how it would avoid end pegging the CPU. How do you imagine it is different from calling Application.DoEvents() in a loop while doing your "background" work?

2. To allow handling "unhandled" exceptions.

It seems that you might be wanting to wrap DispatchMessage() (or the whole loop) in a Try ... Catch block. It is not necessary. Just do this:

''// file ApplicationEvents.vb
''// <- the stackoverflow syntax highlighter doesn't know about VB comment syntax
Namespace My
    Partial Friend Class MyApplication
        Private Sub MyApplication_UnhandledException(ByVal sender As Object, ByVal e As Microsoft.VisualBasic.ApplicationServices.UnhandledExceptionEventArgs) Handles Me.UnhandledException
            e.ExitApplication = False
            ''// notify the user or whatever...
        End Sub
    End Class
End Namespace

See the docs: My.Application.UnhandledException Event.

Hugh Allen
+1  A: 

If you are determined to write your own message loop you can.

Application.Run is closed-source so you don't know what it's doing besides GetMessage, TranslateMessage and DispatchMessage, so you'd better at least call DoEvents instead. Since DoEvents only processes pending messages and doesn't wait, you can call WaitMessage to yield the CPU until there are new messages to process.

Declare Function WaitMessage Lib "user32.dll" () As Boolean

Shared Sub Main()
    Dim f As New Form1
    f.Show()
    Do While f.Visible
        Try
            Application.DoEvents()
        Catch e As Exception
            MsgBox(e.Message)
        End Try
        WaitMessage()
    Loop
End Sub

This doesn't end-peg the CPU, but then it doesn't do any background work either.

You haven't said what your background work is exactly, but if it's stuff which completes asynchronously you should replace WaitMessage with MsgWaitForMultipleObjectsEx - although you'll need to get the Win32 handles of the objects you are waiting on.

Hugh Allen
This answer is spot-on 100!! The WaitMessage function is the one I didn't know about. Actually I can just put a this code in a while(true) loop at the end of my AppDomain.UnhandledException handler and it (almost) invisibly spins until I can shutdown gracefully. This is because .NET's app-kill code is after that handler returns. I then stop however many of these loops may have been started by thread errors with the VB End command when the application.run command finishes (ugly I know). Problem Solved!
FastAl