Logoff/Shutdown event

drmiller32
drmiller32 used Ask the Experts™
on
Recently I have finished rewriting a program in C# that I had created if VB6.  Everything is working perfectly except for one very annoying and disabling feature.  From what I can understand .NET forms are not generated in the same way that VB6 forms were generated. (Here is a link to the information that I found http://www.ftponline.com/archives/premier/mgznarch/vbpj/2001/11nov01/qa0111/qa0111.asp ).  I need a way to detect when the user logs off or windows shutdown.

The program I have written monitors lab activity and for statistical purposes.  But I need to be able to know when my application closes or when the user logs off.  With VB6 I used _QueryUnload to detect this.  I have tried using winproc but the event never catches when my app closes.  I can detect when the user clicks the close icon or selects FILE EXIT and let onclosing catch the event but it does not work if it is hidden from the task bar.  Is the any alternative to this?

Here is a test of the WinProc routine that I tried but the events I am looking for never trigger.
  protected override void WndProc(ref Message m)
  {
       const int WM_QUERYENDSESSION = 0x011;
       const int WM_ENDSESSION = 0x16;
       bool test = false;
       switch(m.Msg)
            {
                    case WM_QUERYENDSESSION:
                            test = RWR.WriteLogout(GD);
                            break;
                    case WM_ENDSESSION:
                            test = RWR.WriteLogout(GD);
                            break;
             }
             base.WndProc(ref m);
   }

Thanks
     Dave.
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Top Expert 2004

Commented:
the WM is still sent you have to trap it as you are above ...

http://www.stevex.org/dottext/articles/155.aspx has an explanation

Commented:
Just having a WndProc() is insufficient - the OS has to be told about it when a window is created.

Here's an alternative to filter WM_* messages:
http://www.codeproject.com/dotnet/devicevolumemonitor.asp

Author

Commented:
I looked at both links and I changed my code accordingly.  But I still do not get the WM_QUERYENDSESSION or the  WM_ENDSESSION events.  I get the WM_Close event.  But I only get that event if the app is visible in the task bar. It seems that I capture about every event that exists except for the two events that I need.   I'm not sure if I am doing what cookre has posted.  I have found other examples from your help.  Here is my last attempt.  But it seems to basically what WndProc() does.


static void Main()
{
      Application.AddMessageFilter(new MyFilter());

      Application.Run(new Form1());
}




class MyFilter:IMessageFilter //gets the left mouse button messages
{
     public bool PreFilterMessage(ref Message m)
     {
           const int WM_QUERYENDSESSION = 0x011;
           const int WM_ENDSESSION = 0x16;
           if (m.Msg==WM_QUERYENDSESSION)
           {
                            do stuff ....................
                            return(true);
           }
           return(false);
}



Become a CompTIA Certified Healthcare IT Tech

This course will help prep you to earn the CompTIA Healthcare IT Technician certification showing that you have the knowledge and skills needed to succeed in installing, managing, and troubleshooting IT systems in medical and clinical settings.

Top Expert 2004

Commented:
only top level windows get the message (as stated in the link I sent)

Commented:
Where did you put the AddMessageFilter()?

Author

Commented:
I have two forms Form1 and Form2.  Form1 is hidden and not desplayed in the task bar,  and is showne and displays a message when the user has used an application that the instructor wished blocked.  Form 2 is shown but is hidden from the task bar.  It displays idle time and lab closing information.  I have tried placing the code in both forms.  

I tried what gregoryyoung suggested but I'm not sure if I am placing it in the proper location.  I have simply cut the code and moved it back in forth testing it in both forms.  

cookre, I have tried it in the main of form1 and I have placed it before Initialize componets in form2.

Thanks
Top Expert 2004
Commented:
take a look here about using setwindowshook to do this ...

http://support.microsoft.com/?id=319524
http://www.codeproject.com/dll/hooks.asp
http://www.codeproject.com/system/hooksys.asp

although you should be able to catch it on form2 in the winproc
Commented:
Here's what I did once to get a full message handler in a c# program:
(I'm cutting and pasting from an old backup - I hope it's all there)



[DllImport("User32.Dll")]
public static extern int RegisterClass(ref WNDCLASS wndcls);

[DllImport("user32.dll")]
public static extern IntPtr CreateWindowEx(int extra
                                         ,string lpClassName
                                         ,string lpWindowName
                                         ,int dwStyle
                                         ,int x
                                         ,int y
                                         ,int nWidth
                                         ,int nHeight
                                         ,int hwndParent
                                         ,HANDLE hMenu
                                         ,HANDLE hInstance
                                         ,IntPtr lpParam);

// Let's create a window
IntPtr hInst=Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly().GetModule("MyEXE.exe"));
WNDCLASS wndclass=new WNDCLASS();

wndclass.style=11; //CS_HREDRAW+CS_VREDRAW+CS_DBLCLKS;
wndclass.lpfnwndproc=new WndProc(MyWndProc);
wndclass.cbClsextra=0;
wndclass.cbWndExtra2=0;
wndclass.hInstance=hInst;
wndclass.hIcon=IntPtr.Zero;
wndclass.hCursor=IntPtr.Zero;
wndclass.hbrBackground=IntPtr.Zero;;
wndclass.lpszMenuName="MyEXE";
wndclass.lpszClassName="MyEXE";
mm=RegisterClass(ref wndclass);
IntPtr MyHwnd=IntPtr.Zero;
MyHwnd=CreateWindowEx(0
                     ,"MyEXE"
                     ,"MyEXE"
                     ,0x00cf0000d
                     ,0
                     ,0
                     ,200
                     ,200
                     ,0
                     ,IntPtr.Zero
                     ,hInst
                     ,IntPtr.Zero);
ShowWindow(MyHwnd,5); //SW_SHOW          <--------------------- make this SW_HIDE

...

int MyWndProc(int hwnd,int message,int wParam,int lParam)
{
...
}

Author

Commented:
Thanks to both of you.  I have learned quite a bit about Window Events over the past week.  In the past hour I learned that there is a major difference in how the debugger in Visual Studio 6 and .Net handle events.  After I tried both solutions with no luck I tried runing the release (with both suggestions) without the debuger.  My app wrote out the information after I chose to shutdown or logout.  So, I guess that .Net debuger is  is traping the WM_ events befoure the app being debuged.  In vb6 my app caught the event before the debuger.

Once again thanks for both your help.

Commented:
Tnx...

In the future, you may want to mention using debug mode earlier.  There's a world of difference between debug and release - so much so that I never use it, and, I suspect, no small number of others avoid it, too.

In this case, it tells me something important.  It's not so much that .NET is trapping the messages - you're just not seeing them because VS would appear to create them as child windows.  As gregoryyoung mentioned, child windows get only those messages aimed at them.  General broadcast messages go only to parent windows.

And that just added another item to my 'get around to it eventually' list - do an EnumWindows() under c# debug mode to verify, or refute, that conclusion.

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial