Process Windows Messages in your WPF application

 

I have some code that attaches and injects code into a target application (like Visual Studio or IE) to examine its memory use. In order to do that, my code freezes the target.

 

I wanted my application to respond to WM_QUERYENDSESSION so that when the user is shutting down, it unfreezes the target, so that the Force Shutdown dialog doesn’t show.

 

The #define constants for the WM_xxx messages can be found In the file c:\program files (x86)\microsoft sdks\windows\v7.0a\include\winuser.rh

For example,

#define WM_QUERYENDSESSION 0x0011

#define WM_QUERYOPEN 0x0013

#define WM_ENDSESSION 0x0016

 

You can multi-edit these 3 lines: Cut and paste those into the Visual Studio Editor.

Put the cursor at the beginning of the first “#”, then hold the Alt-Shift down while you hit the down arrow 3 times. This causes a column selection of zero width. You can also move right to make the column width non-zero. Then you can type text which will replace the current selection, like “Private Const”. You won’t get intellisense, but you will change multiple lines easily. This trick comes in handy when you want to select columns of text for copy/paste too.

 

The code below adds a Window Procedure to be hooked into the normal processing chain. The code just shows the window messages as they occur in the window, along with a timestamp and parameters.

 

Start VS 2010

File->New->Project->VB->WPF app

Replace the MainWindow.Xaml.Vb code with the code below.

 

Try moving the mouse, typing keys, resizing, moving, etc.

 

Try starting Task Manager (Ctrl-Shift-Esc) and note the windows messages that are logged. What’s happening? See How does Task Manager determine if an Application is Not Responding?

 

<Code>

Imports System.Windows.Interop

Class MainWindow

    'c:\program files (x86)\microsoft sdks\windows\v7.0a\include\winuser.rh

    Enum Msgs

        WM_MOVE = &H3

        WM_SIZE = &H5

        WM_ACTIVATE = &H6

        WM_SETFOCUS = &H7

        WM_KILLFOCUS = &H8

        WM_ENABLE = &HA

        WM_SETREDRAW = &HB

        WM_SETTEXT = &HC

        WM_GETTEXT = &HD

        WM_GETTEXTLENGTH = &HE

       WM_PAINT = &HF

        WM_CLOSE = &H10

        WM_QUERYENDSESSION = &H11

        WM_QUERYOPEN = &H13

        WM_ENDSESSION = &H16

        WM_QUIT = &H12

        WM_ERASEBKGND = &H14

        WM_SYSCOLORCHANGE = &H15

        WM_SHOWWINDOW = &H18

        WM_WININICHANGE = &H1A

        WM_ACTIVATEAPP = &H1C

        WM_SETCURSOR = &H20

        WM_GETMINMAXINFO = &H24

        WM_WINDOWPOSCHANGING = &H46

        WM_WINDOWPOSCHANGED = &H47

        WM_CONTEXTMENU = &H7B

        WM_STYLECHANGING = &H7C

        WM_STYLECHANGED = &H7D

        WM_DISPLAYCHANGE = &H7E

        WM_GETICON = &H7F

        WM_SETICON = &H80

        WM_NCCALCSIZE = &H83

        WM_NCHITTEST = &H84

        WM_NCPAINT = &H85

        WM_NCACTIVATE = &H86

        WM_NCMOUSEMOVE = &HA0

       WM_NCLBUTTONDOWN = &HA1

        WM_NCLBUTTONUP = &HA2

        WM_NCLBUTTONDBLCLK = &HA3

        WM_NCRBUTTONDOWN = &HA4

        WM_NCRBUTTONUP = &HA5

        WM_NCRBUTTONDBLCLK = &HA6

        WM_NCMBUTTONDOWN = &HA7

        WM_NCMBUTTONUP = &HA8

        WM_NCMBUTTONDBLCLK = &HA9

        WM_KEYDOWN = &H100

        WM_KEYUP = &H101

        WM_CHAR = &H102

        WM_DEADCHAR = &H103

        WM_SYSKEYDOWN = &H104

        WM_SYSKEYUP = &H105

        WM_SYSCHAR = &H106

        WM_SYSDEADCHAR = &H107

        WM_MOUSEMOVE = &H200

        WM_LBUTTONDOWN = &H201

        WM_LBUTTONUP = &H202

        WM_LBUTTONDBLCLK = &H203

        WM_RBUTTONDOWN = &H204

        WM_RBUTTONUP = &H205

        WM_RBUTTONDBLCLK = &H206

        WM_MBUTTONDOWN = &H207

        WM_MBUTTONUP = &H208

        WM_MBUTTONDBLCLK = &H209

        WM_SIZING = &H214

        WM_CAPTURECHANGED = &H215

        WM_MOVING = &H216

        WM_ENTERSIZEMOVE = &H231

        WM_EXITSIZEMOVE = &H232

        WM_IME_SETCONTEXT = &H281

        WM_IME_NOTIFY = &H282

        WM_NCMOUSEHOVER = &H2A0

        WM_MOUSEHOVER = &H2A1

        WM_NCMOUSELEAVE = &H2A2

        WM_MOUSELEAVE = &H2A3

    End Enum

 

    Private Class txtStatus

        Inherits TextBox

        Sub New()

            ' MaxLines = 25

            AcceptsReturn = True

            AcceptsTab = True

            IsReadOnly = True

            FontFamily = New FontFamily("Courier New") ' monospace

            VerticalScrollBarVisibility = ScrollBarVisibility.Auto

            Text = String.Empty

        End Sub

        Sub AddStat(ByVal str As String)

            str = String.Format("{0} {1}", DateTime.Now.ToLongTimeString, str)

            AppendText(str + vbCrLf)

            CaretIndex = Text.Length

            ScrollToEnd()

            ' force background rendering thread to render even though we might be at full CPU

            Me.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Render, Function() Nothing)

            ' write to log file, so we can see even in logoff/shutdown case

            System.IO.File.AppendAllText("c:\t.log", str + vbCrLf)

        End Sub

    End Class

    Private _txtStatus As New txtStatus

 

    Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded

        Try

            Dim lamWndProc = Function(hwnd As IntPtr, msg As Integer, wParam As IntPtr, lParam As IntPtr, ByRef handleed As Boolean) As IntPtr

                                 Dim retVal = IntPtr.Zero

 

                                 Try

                                     Select Case msg

                                         Case Msgs.WM_ACTIVATEAPP, Msgs.WM_GETICON, Msgs.WM_ENDSESSION, Msgs.WM_QUERYENDSESSION

                                     End Select

                                     Dim strMsg = CType(msg, Msgs).ToString

                                     If Char.IsDigit(strMsg) Then

                                         strMsg = msg.ToString("x8") ' convert to hex

                                     End If

                                    _txtStatus.AddStat(String.Format("{0,-20} {1:x8} {2:x8}", strMsg, wParam.ToInt32, lParam.ToInt32))

 

                                 Catch ex As Exception

                                 End Try

                                 Return retVal

                             End Function

            Dim hwndSrc = CType(HwndSource.FromVisual(Me), HwndSource)

            hwndSrc.AddHook(lamWndProc)

            Me.Content = _txtStatus

 

            AddHandler Me.Closing, Sub()

                                       hwndSrc.RemoveHook(lamWndProc)

                                   End Sub

        Catch ex As Exception

            Me.Content = ex.ToString

        End Try

    End Sub

End Class

 

</Code>