La necessità è quella di intercettare il plug/unplug di un qualsiasi device USB. Il problema è che il sistema operativo sparapacchia la notifica nella massage pump e il subclassing in Wpf è concetto inesistente…

Quindi?

E’ un po’ contorto ma alla fine con una manciata di righe di codice ci si arriva:

Public Interface IHardwareMonitor
    Event DeviceAttached As EventHandler
    Event DeviceRemoved As EventHandler
End Interface

definiamo quello che vogliamo ottenere: un semplice observer.

The Trick

A questo punto lo possiamo implementare, ad esempio, in questo modo:

Public NotInheritable Class WpfHardwareMonitor
    Implements IHardwareMonitor

    Public Event DeviceAttached(ByVal sender As Object, ByVal e As System.EventArgs) Implements Plc.IHardwareMonitor.DeviceAttached
    Private Sub OnDeviceAttached()
        RaiseEvent DeviceAttached(Me, EventArgs.Empty)
    End Sub

    Public Event DeviceRemoved(ByVal sender As Object, ByVal e As System.EventArgs) Implements Plc.IHardwareMonitor.DeviceRemoved
    Private Sub OnDeviceRemoved()
        RaiseEvent DeviceRemoved(Me, EventArgs.Empty)
    End Sub

    Dim source As HwndSource

    Public Sub New()

        AddHandler Application.Current.Exit, AddressOf OnAppExit
        AddHandler Application.Current.MainWindow.Loaded, AddressOf OnLoaded

    End Sub

    Sub OnAppExit(ByVal sender As Object, ByVal e As ExitEventArgs)
        source.RemoveHook(New HwndSourceHook(AddressOf WndProc))
        RemoveHandler Application.Current.Exit, AddressOf OnAppExit
    End Sub

    Sub OnLoaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
        source = HwndSource.FromHwnd(New WindowInteropHelper(Application.Current.MainWindow).Handle)
        source.AddHook(New HwndSourceHook(AddressOf WndProc))

        RemoveHandler Application.Current.MainWindow.Loaded, AddressOf OnLoaded
    End Sub

    Const WM_DEVICECHANGE As Integer = &H219
    Const DBT_DEVICEARRIVAL As Integer = &H8000
    Const DBT_DEVICEREMOVECOMPLETE As Integer = &H8004

    Function WndProc(ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr, ByRef handled As Boolean) As IntPtr
        If msg = WM_DEVICECHANGE Then

            If wParam.ToInt32() = DBT_DEVICEARRIVAL Then
                Me.OnDeviceAttached()
            ElseIf wParam.ToInt32() = DBT_DEVICEREMOVECOMPLETE Then
                Me.OnDeviceRemoved()
            End If
        End If
    End Function

End Class

Il giochetto di fondo è quello di risuscire a ricavare l’handle di una finestra, top level, che in Wpf non serve:

source = HwndSource.FromHwnd(New WindowInteropHelper(Application.Current.MainWindow).Handle)
source.AddHook(New HwndSourceHook(AddressOf WndProc))

Il resto è normale codice per la gestione dei messaggi.

ndr
E’ VB.Net :-), mamma mia che fatica! non so se è perchè lo uso veramente di rado ma lo trovo decisamente ostico, decisamente. E’ evidente che dal punto di vista delle potenzialità non c’è differenza ma alcune cose mi laciano molto perplesso, molto.

.m