Goz Goz - 1 month ago 22
C# Question

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!

Goz Goz
Answer

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 ) );
        }