(Scope: C# and VB.NET although code here is in VB.NET.)
My WinForms application is working well until first suspend and resume of the PC. After resume, certain event handlers are ignored.
Logging mechanism logs first suspend and first resume. Similarly, breakpoint in VisualStudio stops in Sub PowerModeChanged()
after first suspend and resume. But they never do it again.
Sub PowerModeChanged()
never gets called again.
Sub FeedRawInput()
never gets called again and keystrokes go to standard WinForms handlers which are otherwise inactive in my app.
I'm not messing with any system tweaks, it is pretty standard WinForms MDI application. Any idea what can be killing some internal bindings so events get never called after resume?
If I restart the application, everything is immediately running fine again... until suspend-resume.
Sub Initialize() 'called from MainForm_Load()
If AreAllSettingsMissing() Then PushAllDefaultSettings() 'needs database connections available
AddHandler Microsoft.Win32.SystemEvents.PowerModeChanged, AddressOf PowerModeChanged
_rawInput = New RawInputProcessor.RawFormsInput(My.Application.OpenForms(0),
RawInputProcessor.RawInputCaptureMode.Foreground)
AddHandler _rawInput.KeyPressed, AddressOf FeedRawInput
End Sub
'my attempt to log suspend/resume and to rebuild USB scanner support
Sub PowerModeChanged(sender As Object, e As Microsoft.Win32.PowerModeChangedEventArgs)
clsLogging.Log(String.Format("{0}:{1}:{2}", sender.ToString(), [Enum].GetName(GetType(Microsoft.Win32.PowerModes), e.Mode), _rawInput.ToString()))
If e.Mode <> Microsoft.Win32.PowerModes.Resume Then Return
'bring all down
If _rawInput Is Nothing Then Return
_rawInput.Dispose()
_rawInput = Nothing
RemoveHandler _rawInput.KeyPressed, AddressOf FeedRawInput 'problem 1 - called too late
'restore all back
_rawInput = New RawInputProcessor.RawFormsInput(My.Application.OpenForms(0),
RawInputProcessor.RawInputCaptureMode.Foreground)
AddHandler _rawInput.KeyPressed, AddressOf FeedRawInput
End Sub
Sub FeedRawInput(sender As Object, e As RawInputProcessor.RawInputEventArgs)
'push raw input key attributes into processing queue where
'each detected USB keyboard has its own async processing queue,
'marshalling complete keystroke or scan back to main thread - standard stuff
End Sub
Regarding RawInput library, I have ported this one (C#->VB) which is a fork of this one. At the moment I don't know if creation of hidden window (what library does for catching messages) can play any role in the problem. (But it handles keyboard, so why also power event is ignored?) When I've tested library in small WPF project, it could survive resume well. WinForms testing of standalone library I didn't do yet.
If you have any quick hints where to check instead of lengthy dismantling of everything, separating pieces of app for easier diagnostics etc. please let me know.
problem 1
was moved before Dispose()
. Debugger is no longer strangely losing tracking.PowerModeChanged()
is always called (no longer ignored) after I removed my multi-threaded USB keyboard decoder. Now it can survive suspend-resume.I didn't find solution (workaround?) other than to terminate all worker threads and remove global handlers on receiving Suspend
event. After computer resume, on receving Resume
event in my application I can re-create all listeners and re-add global handlers. (Listeners were not enough, handlers needed "refresh", too.) After applying this approach it started to work correctly.
Sub PowerModeChanged(sender As Object, e As Microsoft.Win32.PowerModeChangedEventArgs)
clsLogging.Log(String.Format("{0}:{1}:{2}",
sender.ToString(),
[Enum].GetName(GetType(Microsoft.Win32.PowerModes), e.Mode),
Threading.Thread.CurrentThread.GetHashCode()))
Select Case e.Mode
Case Microsoft.Win32.PowerModes.Suspend
My.Application.Scanners.Suspend()
'inside: 1) send termination request to all threads
' 2) clear USB keyboard map and any other references
' 3) remove global raw keyboard event handler
' 4) destroy instance of rawInput engine
Case Microsoft.Win32.PowerModes.Resume
My.Application.Scanners.Resume()
'inside: undo (rebuild) steps 4, 3, 2, 1 seen in Suspend()
End Select
'debug printout: see list of all thread-powered device readers
Dim logEntry As String = ""
For Each key As IntPtr In My.Application.Scanners.Devices.Keys
logEntry &= String.Format("{0} {1} ", key, My.Application.Scanners.Devices(key))
Next
clsLogging.Log(logEntry)
End Sub