Problem implementing a message loop

G

Guest

I am trying to write an event-driven application with no main window that
runs "forever". It waits on a named event and then displays a window
depending on data pased in a memory-mapped file. I started with a standard
console application and it all works fine, except that it does not terminate
tidily when the user logs off/shuts down. So, I figured I needed a message
loop to pick up WM_QUIT. I studied the code in "Waiting in a message loop"
(http://msdn.microsoft.com/library/d...us/dllproc/base/waiting_in_a_message_loop.asp) and implemented it in my Main(), using
MsgWaitForMultipleObjects(1, handles, false, INFINITE, QS_POSTMESSAGE +
QS_SENDMESSAGE);
to wait for either my named event (in 'handles') or a windows message. The
event fires fine, but no windows messages ever arrive. I am obviously doing
something wrong, Can anyone advise.
 
W

Willy Denoyette [MVP]

|I am trying to write an event-driven application with no main window that
| runs "forever". It waits on a named event and then displays a window
| depending on data pased in a memory-mapped file. I started with a standard
| console application and it all works fine, except that it does not
terminate
| tidily when the user logs off/shuts down. So, I figured I needed a message
| loop to pick up WM_QUIT. I studied the code in "Waiting in a message loop"
|
(http://msdn.microsoft.com/library/d...us/dllproc/base/waiting_in_a_message_loop.asp)
and implemented it in my Main(), using
| MsgWaitForMultipleObjects(1, handles, false, INFINITE, QS_POSTMESSAGE +
| QS_SENDMESSAGE);
| to wait for either my named event (in 'handles') or a windows message. The
| event fires fine, but no windows messages ever arrive. I am obviously
doing
| something wrong, Can anyone advise.
| --
| Dave

This will not work, you need a main windows for the system to post/send
messages to, you just need to handle the SessionEnded event on the
Microsoft.Win32.SystemEvents class.

Willy.
 
G

Guest

Thanks Willy, but if I implement it as a Windows app with a main window,
execution then disappears into the main window (ie the main window's message
loop) so how do I then wait for my named event?
 
W

Willy Denoyette [MVP]

| Thanks Willy, but if I implement it as a Windows app with a main window,
| execution then disappears into the main window (ie the main window's
message
| loop) so how do I then wait for my named event?
| --
| Dave
|
|
| "Willy Denoyette [MVP]" wrote:
|

Sorry if I wasn't clear, point is that you don't need a main windows (a Form
instance), all you need is a message pump to handle this.
You can start a separate thread to handle your main task before you start
the message loop by calling Application.Run().

Something like this should do:


[STAThread]
static void Main()
{
SystemEvents.SessionEnded += new
SessionEndedEventHandler(SessionEnd);
// Start a new backgound thread to handle your main task here.
// Start a message pump.
Application.Run();
}
static void SessionEnd(object s, SessionEndedEventArgs e)
{
...
}

Willy.
 
G

Guest

Hi Willy
I was confused there as your original reply said "you need a main windows".
I have implemented the event (I used SessionEnding rather than SessionEnded)
but it still doesn't work - when I log off the SessionEnding event is never
fired, but I get a window ".NET-Broadcast Event Window 1.0.5 - This program
is not responding" etc. I would guess that this is because I am not calling
Application.Run because I need to run my own loop waiting for my named event.
Any further suggestions gratefully received. (Maybe I need to make it a
Windows application and override WndProc?)

BTW there seem to be a lot of problems with the newsgroups at the moment.
The "read the response" links in the notification emails haven't worked for
weeks, and 9 times out of 10 I get "page does not exist" when I try to access
a newsgroup. Can you pass the message on to MS.
 
B

Barry Kelly

Dave said:
I was confused there as your original reply said "you need a main windows".
I have implemented the event (I used SessionEnding rather than SessionEnded)
but it still doesn't work - when I log off the SessionEnding event is never
fired, but I get a window ".NET-Broadcast Event Window 1.0.5 - This program
is not responding" etc. I would guess that this is because I am not calling
Application.Run because I need to run my own loop waiting for my named event.
Any further suggestions gratefully received. (Maybe I need to make it a
Windows application and override WndProc?)

I've tested this application and it works for me:

---8<---
using System;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using Microsoft.Win32;

class App
{
static void Main()
{
EventWaitHandle closing = new ManualResetEvent(false);
bool running = true;

SystemEvents.SessionEnded += delegate
{
running = false;
closing.Set();
File.WriteAllText("\\Log.txt", "Session ended");
};

Thread worker = new Thread((ThreadStart)delegate
{
for (;;)
{
WaitHandle.WaitAny(new WaitHandle[] { closing
/* , X, etc. */ });
if (!running)
break;

// Do work because of event X or whatever
}
File.WriteAllText("\\Closed.txt", "Success!");
});
worker.Start();

Application.Run();
}
}
--->8---

Perhaps you can adapt your application along these lines?

-- Barry
 
B

Barry Kelly

Dave said:
I would guess that this is because I am not calling
Application.Run because I need to run my own loop waiting for my named event.
Any further suggestions gratefully received.

I should point out that each thread is separate as far as message loops
are concerned. You can call Application.Run() on the main thread so that
the session ended event occurs, and create your own message loop (or
call Application.Run() again) on a different thread, without problems.

-- Barry
 
W

Willy Denoyette [MVP]

Are you sure you compiled as a Windows application? Console applications
cannot receive Session ending messages.

Consider following sample to get you started.

public class Program
{
static int keepRunning;
[STAThread]
static void Main()
{
Program prog = new Program();
// Optionally install a tray icon and attached context memu
prog.InitializeTray();
Interlocked.Exchange(ref keepRunning, 1);
Thread t = new Thread(new ThreadStart(prog.ThreadJob));
t.Start();
// Register handler
SystemEvents.SessionEnding += new
SessionEndingEventHandler(prog.SessionEnd);
// Start the message loop, this one creates a parking window and
associated message queue,
// windows messages will be posted to this queue.
Application.Run();
}
void ThreadJob()
{
// Initialize thread invariants like synchronization handles
// ...
// keep running the loop until requested to terminate
while (Interlocked.Exchange(ref keepRunning, 1) == 1)
{
// this is your task loop, make sure you don't start blocking
operations in this loop, start another thread when needed.
}
// Clean-up thread invariants here
}
void InitializeTray()
{
// Initialize context menu
MenuItem cntxtMenuItem = new MenuItem();
cntxtMenuItem.Index = 0;
cntxtMenuItem.Text = "E&xit";
cntxtMenuItem.Click += new System.EventHandler(this.cntxtMenu_Click);
ContextMenu cntxtMenu = new ContextMenu();
cntxtMenu.MenuItems.AddRange(
new MenuItem[] {cntxtMenuItem});
// Attach context menu to notifyIcon
NotifyIcon notifyIcon;
notifyIcon = new NotifyIcon();
notifyIcon.Icon = new System.Drawing.Icon(@"whatever.ico");
notifyIcon.Visible = true;
notifyIcon.Text = "My program";
notifyIcon.ContextMenu = cntxtMenu;
}
void cntxtMenu_Click(object Sender, EventArgs e) {
Interlocked.Exchange(ref keepRunning, 0); // Ask threads to terminate
Application.Exit(); // exit application
}

void SessionEnd(object s, SessionEndingEventArgs e)
{
// signal auxiliary thread(s) to stop
Interlocked.Exchange(ref keepRunning, 0);
// cleanup program invariants when needed (constrained by session
end time-out)
//...
// and accept session to end
e.Cancel = true;
}
}
}
}

Willy.
PS. Can't help you with your newsreader issue, apparently you aren't using a
NNTP newsreader (and posting host), I suggest you try another posting host
and/or reader.

| Hi Willy
| I was confused there as your original reply said "you need a main
windows".
| I have implemented the event (I used SessionEnding rather than
SessionEnded)
| but it still doesn't work - when I log off the SessionEnding event is
never
| fired, but I get a window ".NET-Broadcast Event Window 1.0.5 - This
program
| is not responding" etc. I would guess that this is because I am not
calling
| Application.Run because I need to run my own loop waiting for my named
event.
| Any further suggestions gratefully received. (Maybe I need to make it a
| Windows application and override WndProc?)
|
| BTW there seem to be a lot of problems with the newsgroups at the moment.
| The "read the response" links in the notification emails haven't worked
for
| weeks, and 9 times out of 10 I get "page does not exist" when I try to
access
| a newsgroup. Can you pass the message on to MS.
| --
| Dave
|
|
| "Willy Denoyette [MVP]" wrote:
|
| >
| > | > | Thanks Willy, but if I implement it as a Windows app with a main
window,
| > | execution then disappears into the main window (ie the main window's
| > message
| > | loop) so how do I then wait for my named event?
| > | --
| > | Dave
| > |
| > |
| > | "Willy Denoyette [MVP]" wrote:
| > |
| >
| > Sorry if I wasn't clear, point is that you don't need a main windows (a
Form
| > instance), all you need is a message pump to handle this.
| > You can start a separate thread to handle your main task before you
start
| > the message loop by calling Application.Run().
| >
| > Something like this should do:
| >
| >
| > [STAThread]
| > static void Main()
| > {
| > SystemEvents.SessionEnded += new
| > SessionEndedEventHandler(SessionEnd);
| > // Start a new backgound thread to handle your main task here.
| > // Start a message pump.
| > Application.Run();
| > }
| > static void SessionEnd(object s, SessionEndedEventArgs e)
| > {
| > ...
| > }
| >
| > Willy.
| >
| >
| >
 
G

Guest

Brilliant. Thanks Barry, that gave me the clue I needed. All works fine now.
--
Dave


Barry Kelly said:
Dave said:
I was confused there as your original reply said "you need a main windows".
I have implemented the event (I used SessionEnding rather than SessionEnded)
but it still doesn't work - when I log off the SessionEnding event is never
fired, but I get a window ".NET-Broadcast Event Window 1.0.5 - This program
is not responding" etc. I would guess that this is because I am not calling
Application.Run because I need to run my own loop waiting for my named event.
Any further suggestions gratefully received. (Maybe I need to make it a
Windows application and override WndProc?)

I've tested this application and it works for me:

---8<---
using System;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using Microsoft.Win32;

class App
{
static void Main()
{
EventWaitHandle closing = new ManualResetEvent(false);
bool running = true;

SystemEvents.SessionEnded += delegate
{
running = false;
closing.Set();
File.WriteAllText("\\Log.txt", "Session ended");
};

Thread worker = new Thread((ThreadStart)delegate
{
for (;;)
{
WaitHandle.WaitAny(new WaitHandle[] { closing
/* , X, etc. */ });
if (!running)
break;

// Do work because of event X or whatever
}
File.WriteAllText("\\Closed.txt", "Success!");
});
worker.Start();

Application.Run();
}
}
--->8---

Perhaps you can adapt your application along these lines?

-- Barry
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top