Skip to main content
GameDev.net gamedev.net
🔒 Locked

C# main loop?

Started by romer Feb 13, 2004 at 7:47 PM 13 replies 38.1k views
Original Post
romer
romer
Hmm, after getting to work on my level editor that I''m writing in C#, it became apparent to me that I had no clue how to set up a ''main loop'' in the application. I know in C/C++ Win32 you can do this:
while ( true )
{
  if ( PeekMessage( /* blah */ ) )
  {
    /* blah */
  }
  /* more blah */
}
 
And in Java when working with applets, I could just implement the run method from the Runnable interface. I''ve tried looking for an equivalent for either in C#, but haven''t had much luck. Can someone help me on how I would go about setting up a main loop in a C# windows application?
Arild Fines
Arild Fines
Using Application.DoEvents is evil according to Tom Miller: http://blogs.msdn.com/tmiller/archive/2003/11/07/57524.aspx.

His recommended alternatives: http://blogs.msdn.com/tmiller/archive/2003/11/24/57532.aspx

--
AnkhSVN - A Visual Studio .NET Addin for the Subversion version control system.
[Project site] [Blog] [RSS] [Browse the source] [IRC channel]
--AnkhSVN - A Visual Studio .NET Addin for the Subversion version control system.[Project site] [IRC channel] [Blog]
VizOne
VizOne
Thanks for the links. Interesting facts!

Regards
Andre (VizOne) Loker
romer
romer
Yes, thank you for those links, especially the two blog postings regarding the alternatives to DoEvents. I was thinking of going about calling PeekMessage/TranslateMessage/DispatchMessage directly, but I'm kind of uncertain how you exactly go about this, and I have a few questions regarding it:

1. If I decide calling PeekMessage/TranslateMessage/DispatchMessage directly from my C# application, does it just use the default message pump that a C# windows application would use anyways? Am I still able to set up event handlers like I normally would? (ex: myForm.Paint += new PaintEventHandler( myForm.MyPaintEventHandler ) )

2. I'm not too keen on using Win32 API functions in a C# program, but I did find this link. After looking over it, I think I have an idea of how to go about coding it, but I'm wondering if someone with more knowledge would be able to tell if I'm on the right track or not..

/* put necessary using statements here */

namespace MyApp
{
public class MyMainForm : Form
{
struct POINTAPI
{
public Int32 x;
public Int32 y;
}

struct MSG
{
public Int32 hwnd;
public Int32 message;
public Int32 wParam;
public Int32 lParam;
public Int32 time;
public POINTAPI pt;
}

[DllImport( "Coredll.dll", SetLastError = true )]
private static extern bool PeekMessage(ref MSG lpMsg,
Int32 hwnd,
Int32 wMsgFilterMin,
Int32 wMsgFilterMax,
Int32 wRemoveMsg);

[DllImport( "Coredll.dll", SetLastError = true )]
private static extern Int32 DispatchMessage(ref MSG lpMsg);

[DllImport( "Coredll.dll", SetLastError = true )]
private static extern Int32 TranslateMessage(ref MSG lpMsg);

public MyMainForm()
{
/* initialize the window form here with all the widgets */
}

public void Run()
{
MSG msg = new MSG();

while ( true )
{
if ( PeekMessage( ref msg, 0, 0, 0, PM_REMOVE ) )
{
if ( msg.message == WM_QUIT )
break;

TranslateMessage( ref msg );
DispatchMessage( ref msg );
}
/* main loop stuff */
}
}

public static void Main(string[] args)
{
MyMainForm mainForm = new MyMainForm();
mainForm.Run();
}
}


3. I know I can't access the WM_QUIT and the PM_REMOVE enumerations just like that in C#, and I'm really not sure how to. Does someone know how?

Sorry for the lengthy post, put thank you so much for all those who have been helping me out, and much thanks for anyone who continues to help!

[edited by - MRom on February 14, 2004 1:19:19 PM]
romer
romer
After messing around a bit, I've finally managed to get a message loop working with PeekMessage/TranslateMessage/DispatchMessage. The loop works fine and everything, but now I have a problem with properly closing the application. When you click the close button on the form, it causes the application to go away, leaving you to think that it's been properly shutdown. But the process is still running under the processes tab in Task Manager, leading me to believe that the default internal message pump isn't receiving the WM_QUIT message, which would cause the main loop to exit properly. Here's the general jist of the code I'm working with:

using System;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace GSLevEd
{
public class GSLevEdMainWin : Form
{
///////////////////////////////////////

// Constructor

//

public GSLevEdMainWin()
{
/* removed some methods for brevity */
SetEventHandlers();

// display this form

this.Show();
}

///////////////////////////////////////

// SetEventHandlers - sets the event handlers

// used by this form

//

private void SetEventHandlers()
{
/* removed some statements for brevity */
this.Paint += new PaintEventHandler( this.OnPaintEvent );
this.Closing += new CancelEventHandler( this.OnClosingEvent );
}

///////////////////////////////////////

// Drawing methods

///////////////////////////////////////

// OnPaintEvent - handler for Paint event

//

private void OnPaintEvent(object sender, PaintEventArgs e)
{
/* screen drawing code */
}

///////////////////////////////////////

// Draw - draws the backbuffer

//

private void Draw()
{
/* drawing code here */
}

///////////////////////////////////////

// Closing handler

///////////////////////////////////////

// OnClosingEvent

//

private void OnClosingEvent(object sender, CancelEventArgs e)
{
this.Dispose();
}

///////////////////////////////////////

// Main Loop stuff

///////////////////////////////////////

// Necessary structs and imports

//

struct POINTAPI
{
public Int32 x;
public Int32 y;
}

struct MSG
{
public Int32 hwmd;
public Int32 message;
public Int32 wParam;
public Int32 lParam;
public Int32 time;
public POINTAPI pt;
}

[ DllImport( "user32.dll", SetLastError = true ) ]
private static extern bool PeekMessage(
ref MSG lpMsg,
Int32 hwnd,
Int32 wMsgFilterMin,
Int32 wMsgFilterMax,
PeekMessageOption wRemoveMsg);

[ DllImport( "user32.dll", SetLastError = true ) ]
private static extern bool TranslateMessage(ref MSG lpMsg);

[ DllImport( "user32.dll", SetLastError = true ) ]
private static extern Int32 DispatchMessage(ref MSG lpMsg);

private enum PeekMessageOption
{
PM_NOREMOVE = 0,
PM_REMOVE
}

private static Int32 WM_QUIT = 0x12;

public void Run()
{
MSG msg = new MSG();

while ( true )
{
if ( PeekMessage( ref msg, 0, 0, 0, PeekMessageOption.PM_REMOVE ) )
{
if ( msg.message == WM_QUIT )
break;

TranslateMessage( ref msg );
DispatchMessage( ref msg );
}

Draw();

// sleep a little

System.Threading.Thread.Sleep( 10 );
}
}
}
}

--------------------

// main entry point

using System;

namespace GSLevEd
{
public class GSLevEdApp
{
///////////////////////////////////////

// Main

//

public static int Main(string[] args)
{
GSLevEdMainWin mainWin = new GSLevEdMainWin();
mainWin.Run();
return ( 0 );
}
}
}


The main loop and importing of Win32 functions is towards the end of the code segment. To be honest, I'm not sure what shuts the application down completely, so I was more or less shooting in the dark with the this.Dispose() method call in the closing event handler. Any help with regards to getting the application to shut down properly would be greatly appreciated.

Edit: added a missing code segment

[edited by - MRom on February 14, 2004 2:48:51 PM]
romer
romer
bump...anyone able to help me with the app not closing problem?
antareus
antareus
You got your message loop all wrong, which is a common mistake.

http://www.winprog.org/tutorial/message_loop.html

Edit: why aren't you using Windows Forms, anyway? They do all of this for you.

[edited by - antareus on February 15, 2004 12:12:16 AM]
--God has paid us the intolerable compliment of loving us, in the deepest, most tragic, most inexorable sense.- C.S. Lewis
romer
romer
Sorry if I didn''t make this clear, but I''m using C# rather than plain old Win32 API. I understand how to set up a message loop in Win32. And I understand that Windows forms will handle the message loop all on its own. But from what I can tell, there''s nothing like an Idle event that you can just attach an event handler to so you can do some background processing (in my case, I''m using a double buffering scheme, so I''m drawing stuff to the backbuffer, then displaying it when a Paint event occurs). So the only way I know of being able to do background processing every ''frame'' is by coding out the message loop itself. But I can''t use GetMessage because I can''t have the app just sitting around while I''m waiting for a message... if there''s no message to handle, then it outta go ahead and do background processing. So, that''s why I use PeekMessage instead (structured similar to how a game loop would be set up). And as the loop stands so far, it''s seems to working correctly, for the most part; I mean, my Paint event handler and my Click and Select event handlers for my menu items (which aren''t shown in the posted source just to keep irrelevant code out) all work fine. It''s when I go to close the application, it closes the form, but it doesn''t kill the app. That''s why it''s leading me to believe that for some reason it''s not getting a WM_QUIT message or something similar that triggers the application to be shutdown completely.
tweety_04_01
tweety_04_01
Have you tried calling Application.DoEvents(); when you receive a WM_QUIT message?
Also, don't use Sleep. Not even GetTickCount/Environment.Tick. Use the high-performance timers.

=== Tweety

[edited by - tweety_04_01 on February 17, 2004 10:11:49 PM]
=== Tweety
antareus
antareus
Are you using PostQuitMessage()?
--God has paid us the intolerable compliment of loving us, in the deepest, most tragic, most inexorable sense.- C.S. Lewis
romer
romer
antareus: After working with it a little bit more today, I was finally able to get it working. The way I did it didn''t involve having to use PostQuitMessage, but now that you mention it, that''s what I was originally missing. The form closed properly, but I forgot to post the quit message (most likely would done that in a Closed event handler), hence the reason the app never shut down completely. I want to thank you, and everyone else, however for posting various suggestions and helping me solve this rather annoying problem. Seeing how there''s very little information on this subject, I in the process of writing a small article explaining how I did it, hoping gamedev will host it in one of its article sections.

tweety_04_01: Just wanted to say that Sleep is not used for any timing mechanism. It''s just used to prevent the CPU usage from shooting up to 100% since that loop will be executing constantly until the app is closed.
antareus
antareus
Glad to see you got it working.
--God has paid us the intolerable compliment of loving us, in the deepest, most tragic, most inexorable sense.- C.S. Lewis

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.