Search code examples
c#performancewmiwmi-querywmi-service

WMI Event Watcher Performance Problems


I've written some WMI code that works really well on most machines. However on some machines (nearly all SSD based macs) this code causes a HUGE performance problem. It causes the WMIPrvSe process to keep hitting I/O Other. If you run another app doing the same watching then it slows to a crawl to the point where the software becomes unusable.

            System.Management.WqlEventQuery     queryIn;
            System.Management.WqlEventQuery     queryOut;
            System.Management.ManagementScope   scope   = new System.Management.ManagementScope( "root\\CIMV2" );
            scope.Options.EnablePrivileges      = true;

            try
            {
                queryIn = new System.Management.WqlEventQuery();
                queryIn.EventClassName          = "__InstanceCreationEvent";
                queryIn.WithinInterval          = new TimeSpan( 0, 0, 1 );
                //queryIn.GroupWithinInterval       = new TimeSpan( 0, 0, 0 );
                queryIn.Condition               = @"TargetInstance ISA 'Win32_DiskDrive' AND TargetInstance.InterfaceType = 'USB'";
                mUSBWatcherIn = new System.Management.ManagementEventWatcher( scope, queryIn );

                //adds event handler that’s is fired when the insertion event occurs
                mUSBWatcherIn.EventArrived += new System.Management.EventArrivedEventHandler( USBInserted );

                queryOut = new System.Management.WqlEventQuery();
                queryOut.EventClassName         = "__InstanceDeletionEvent";
                queryOut.WithinInterval         = new TimeSpan( 0, 0, 1 );
                //queryOut.GroupWithinInterval  = new TimeSpan( 0, 0, 0 );
                queryOut.Condition              = @"TargetInstance ISA 'Win32_DiskDrive' AND TargetInstance.InterfaceType = 'USB'";
                mUSBWatcherOut = new System.Management.ManagementEventWatcher( scope, queryOut );

                //adds event handler that’s is fired when the insertion event occurs
                mUSBWatcherOut.EventArrived += new System.Management.EventArrivedEventHandler( USBRemoved );

                mUSBWatcherIn.Start();//run the watcher
                mUSBWatcherOut.Start();
            }
            catch (Exception e)
            {
                System.Windows.Forms.MessageBox.Show( e.Message );
                StopUSBWatcher();
            }

Does anyone have any idea what could be going on here? If I remove this code then it works perfectly. On other machines, it works perfectly. Its very strange. Any ideas hugely appreciated!


Solution

  • so I've not worked out what was causing the problem but I have now implemented identical functionality using WM_DEVICECHANGE.

    Here is my interop setup:

        [System.Runtime.InteropServices.DllImport( "user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, CallingConvention = System.Runtime.InteropServices.CallingConvention.StdCall )]
        private static extern IntPtr RegisterDeviceNotification(IntPtr recipient, IntPtr notificationFilter, int flags);
    
        [System.Runtime.InteropServices.DllImport( "user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, CallingConvention = System.Runtime.InteropServices.CallingConvention.StdCall )] 
        private static extern bool UnregisterDeviceNotification(IntPtr handle);
    
        [System.Runtime.InteropServices.DllImport( "user32.dll", SetLastError = true )]
        static extern IntPtr GetWindowLongPtr( IntPtr hWnd, int nIndex );
    
        [System.Runtime.InteropServices.DllImport( "user32.dll", SetLastError = true )]
        static extern int GetWindowLong( IntPtr hWnd, int nIndex );
    
        [System.Runtime.InteropServices.DllImport( "user32.dll", SetLastError = true )]
        static extern IntPtr SetWindowLongPtr( IntPtr hWnd, int nIndex, IntPtr newLong );
    
        [System.Runtime.InteropServices.DllImport( "user32.dll", SetLastError = true )]
        static extern int SetWindowLong( IntPtr hWnd, int nIndex, int newLong );
    
    
        delegate IntPtr WndProcDelegate( IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam );
    
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        static extern IntPtr CallWindowProc( IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam );
    
        public const    int             GWLP_WNDPROC    = -4;
    
        [System.Runtime.InteropServices.StructLayout( System.Runtime.InteropServices.LayoutKind.Sequential )]
        private struct DevBroadcastDeviceinterface
        {
            internal int Size;
            internal int DeviceType;
            internal int Reserved;
            internal Guid ClassGuid;
            internal short Name;
        }
    
        const int DBT_DEVTYPVOLUME = 0x00000002;  
        const int DBT_DEVTYPDEVICEINTERFACE = 5;
    
        static readonly Guid GuidDevinterfaceUSBDevice = new Guid( "A5DCBF10-6530-11D2-901F-00C04FB951ED" );
    

    And the actual setup code is as follows:

            DevBroadcastDeviceinterface dbi = new DevBroadcastDeviceinterface
               {
                   DeviceType = DBT_DEVTYPDEVICEINTERFACE,//DBT_DEVTYPVOLUME,
                   Reserved = 0,
                   ClassGuid = GuidDevinterfaceUSBDevice,
                   Name = 0
               };
    
            dbi.Size = System.Runtime.InteropServices.Marshal.SizeOf( dbi );
            IntPtr buffer = System.Runtime.InteropServices.Marshal.AllocHGlobal( dbi.Size );
            System.Runtime.InteropServices.Marshal.StructureToPtr( dbi, buffer, true );
    
            IntPtr i = RegisterDeviceNotification( Handle, buffer, 0 );
    
            System.Runtime.InteropServices.Marshal.FreeHGlobal( buffer );
    
            if ( IntPtr.Size == 4 )
            {
                oldWndProc  = new IntPtr( SetWindowLong( Handle, GWLP_WNDPROC, System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate( newWndProc ).ToInt32() ) );
            }
            else
            { 
                oldWndProc  = SetWindowLongPtr( Handle, GWLP_WNDPROC, System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate( newWndProc ) );
            }