1

I need to detect USB device changed event and update the changes to WPF Window. Here is my code.

    public MainWindow()
    {
        InitializeComponent();
        SourceInitialized += (sender, e) =>
        {
            HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
            source.AddHook(WndProc);
        };
    }

    private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        if (msg == 0x219)  // Device Changed
        {
            UpdateSerialPortDict();
        }
        return IntPtr.Zero;
    }

    private void UpdateSerialPortDict()
    {
        System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex(@"^[\S\s]+\((COM([0-9])+)\)$");
        using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_PnPEntity"))
        {
            foreach (ManagementObject obj in searcher.Get())
            {
                if ((string)obj["PNPClass"] == "Ports")
                {
                    string key = obj["Name"] as string;
                    var mat = reg.Match(key);
                    if (mat.Success)
                    {
                        var val = mat.Groups[1].Value;
                        _comDictionary.Add(key, val);
                    }
                }
            }
        }
    }

The program will crash when calling searcher.Get(). If I use a Button to trigger UpdateSerialPortDict(), it works fine. But I would it update automatically by detecting device changed event.

Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
Hyz Yuzhou
  • 83
  • 1
  • 12
  • 1
    Look [HERE](https://stackoverflow.com/questions/620144/detecting-usb-drive-insertion-and-removal-using-windows-service-and-c-sharp). – jsanalytics Jan 16 '18 at 12:01
  • BTW, in case you weren't able to trap it, this is why your code is crashing: – jsanalytics Jan 16 '18 at 12:12
  • Managed Debugging Assistant 'DisconnectedContext' Message=Managed Debugging Assistant 'DisconnectedContext' : 'Transition into COM context 0x14b4f08 for this RuntimeCallableWrapper failed with the following error: An outgoing call cannot be made since the application is dispatching an input-synchronous call. (Exception from HRESULT: 0x8001010D (RPC_E_CANTCALLOUT_ININPUTSYNCCALL)). This is typically because the COM context 0x14b4f08 where this RuntimeCallableWrapper was created has been disconnected or it is busy doing something else and cannot process the context transition. – jsanalytics Jan 16 '18 at 12:14
  • No proxy will be used to service the request on the COM component and calls will be made to the COM component directly. This may cause corruption or data loss. To avoid this problem, please ensure that all COM contexts/apartments/threads stay alive and are available for context transition, until the application is completely done with the RuntimeCallableWrappers that represents COM components that live inside them.' – jsanalytics Jan 16 '18 at 12:14
  • 1
    worthy of an answer @jsanalytics even though it's a dup mostly – kenny Jan 16 '18 at 14:50
  • @Kenny yes, I agree it's mostly a dup. But the two lengthy comments are not my own, they are part of the error message thrown by VS. Besides, mm8 post does the job and should be marked as the answer. – jsanalytics Jan 16 '18 at 18:46

1 Answers1

2

Try to use the dispatcher:

private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    if (msg == 0x219)  // Device Changed
    {
        Dispatcher.BeginInvoke(new Action(() => UpdateSerialPortDict()),
            System.Windows.Threading.DispatcherPriority.Background);
    }
    return IntPtr.Zero;
}
mm8
  • 163,881
  • 10
  • 57
  • 88