Tuesday, 5 November 2013

USB Detector - C#

In this post i am going to explain some more interesting thing, USB Detector so this article deals with create a application for detect the USB when inserted and removed from computer and make a notification to user on removal

USB DetectorClick Here To Download



Launch the application after the download, Then plug in the USB device in the computer now you will see a device add in the application and select a Notify checkbox to notify the user when it is try to remove





Now we see how to do this application.

USBDetector:

public class USBDetector : IDisposable
    {
     
     
        public event USBDetectorEventHandler DeviceArrived;
        public event USBDetectorEventHandler DeviceRemoved;
        public event USBDetectorEventHandler QueryRemove;

        public USBDetector()
        {
            DriveForm frm = new DriveForm(this);
            frm.Show();
            Init(frm, null);
        }

        public USBDetector(Control control)
        {
            Init(control, null);
        }

       
        public USBDetector(Control control, string FileToOpen)
        {
            Init(control, FileToOpen);
        }

       
        private void Init(Control control, string fileToOpen)
        {
            mFileToOpen = fileToOpen;
            mFileOnFlash = null;
            mDeviceNotifyHandle = IntPtr.Zero;
            mRecipientHandle = control.Handle;
            mDirHandle = IntPtr.Zero;  
            mCurrentDrive = "";
        }

       
        public bool IsQueryHooked
        {
            get
            {
                if (mDeviceNotifyHandle == IntPtr.Zero)
                    return false;
                else
                    return true;
            }
        }

      
        public string HookedDrive
        {
            get
            {
                return mCurrentDrive;
            }
        }

       
        public FileStream OpenedFile
        {
            get
            {
                return mFileOnFlash;
            }
        }

       
        public bool EnableQueryRemove(string fileOnDrive)
        {
            if (fileOnDrive == null || fileOnDrive.Length == 0)
                throw new ArgumentException("Drive path must be supplied to register for Query remove.");

            if (fileOnDrive.Length == 2 && fileOnDrive[1] == ':')
                fileOnDrive += '\\';        // append "\\" if only drive letter with ":" was passed in.

            if (mDeviceNotifyHandle != IntPtr.Zero)
            {
                // Unregister first...
                RegisterForDeviceChange(false, null);
            }

            if (Path.GetFileName(fileOnDrive).Length == 0 || !File.Exists(fileOnDrive))
                mFileToOpen = null;     // use root directory...
            else
                mFileToOpen = fileOnDrive;

            RegisterQuery(Path.GetPathRoot(fileOnDrive));
            if (mDeviceNotifyHandle == IntPtr.Zero)
                return false;   // failed to register

            return true;
        }

      
        public void DisableQueryRemove()
        {
            if (mDeviceNotifyHandle != IntPtr.Zero)
            {
                RegisterForDeviceChange(false, null);
            }
        }

     
        public void Dispose()
        {
            RegisterForDeviceChange(false, null);
        }


        #region WindowProc
      
        public void WndProc(ref Message m)
        {
            int devType;
            char c;

            if (m.Msg == WM_DEVICECHANGE)
            {
               
                switch (m.WParam.ToInt32())
                {

                  
                    case DBT_DEVICEARRIVAL:

                        devType = Marshal.ReadInt32(m.LParam, 4);
                        if (devType == DBT_DEVTYP_VOLUME)
                        {
                            DEV_BROADCAST_VOLUME vol;
                            vol = (DEV_BROADCAST_VOLUME)
                                Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_VOLUME));
                            
                            c = DriveMaskToLetter(vol.dbcv_unitmask);                         
                            USBDetectorEventHandler tempDeviceArrived = DeviceArrived;
                            if (tempDeviceArrived != null)
                            {
                                USBDetectorEventArgs e = new USBDetectorEventArgs();
                                e.Drive = c + ":\\";
                                tempDeviceArrived(this, e);

                                // Register for query remove if requested
                                if (e.HookQueryRemove)
                                {
                                    // If something is already hooked, unhook it now
                                    if (mDeviceNotifyHandle != IntPtr.Zero)
                                    {
                                        RegisterForDeviceChange(false, null);
                                    }

                                    RegisterQuery(c + ":\\");
                                }
                            }     // if  has event handler


                        }
                        break;

                  
                    case DBT_DEVICEQUERYREMOVE:

                        devType = Marshal.ReadInt32(m.LParam, 4);
                        if (devType == DBT_DEVTYP_HANDLE)
                        {                         
                            USBDetectorEventHandler tempQuery = QueryRemove;
                            if (tempQuery != null)
                            {
                                USBDetectorEventArgs e = new USBDetectorEventArgs();
                                e.Drive = mCurrentDrive;        // drive which is hooked
                                tempQuery(this, e);

                                // If the client wants to cancel, let Windows know
                                if (e.Cancel)
                                {
                                    m.Result = (IntPtr)BROADCAST_QUERY_DENY;
                                }
                                else
                                {                                   
                                    RegisterForDeviceChange(false, null);   // will also close the mFileOnFlash
                                }

                            }
                        }
                        break;


                    //
                    // Device has been removed
                    //
                    case DBT_DEVICEREMOVECOMPLETE:

                        devType = Marshal.ReadInt32(m.LParam, 4);
                        if (devType == DBT_DEVTYP_VOLUME)
                        {
                            devType = Marshal.ReadInt32(m.LParam, 4);
                            if (devType == DBT_DEVTYP_VOLUME)
                            {
                                DEV_BROADCAST_VOLUME vol;
                                vol = (DEV_BROADCAST_VOLUME)
                                    Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_VOLUME));
                                c = DriveMaskToLetter(vol.dbcv_unitmask);

                                //
                                // Call the client event handler
                                //
                                USBDetectorEventHandler tempDeviceRemoved = DeviceRemoved;
                                if (tempDeviceRemoved != null)
                                {
                                    USBDetectorEventArgs e = new USBDetectorEventArgs();
                                    e.Drive = c + ":\\";
                                    tempDeviceRemoved(this, e);
                                }
                               
                            }
                        }
                        break;
                }

            }

        }
        #endregion

        #region  Private Area

     
        private IntPtr mDirHandle = IntPtr.Zero;
     
        private FileStream mFileOnFlash = null;

        private string mFileToOpen;
       
        private IntPtr mDeviceNotifyHandle;

        private IntPtr mRecipientHandle;

        private string mCurrentDrive;

        private const int DBT_DEVTYP_DEVICEINTERFACE = 5;
        private const int DBT_DEVTYP_HANDLE = 6;
        private const int BROADCAST_QUERY_DENY = 0x424D5144;
        private const int WM_DEVICECHANGE = 0x0219;
        private const int DBT_DEVICEARRIVAL = 0x8000; // system detected a new device
        private const int DBT_DEVICEQUERYREMOVE = 0x8001;   // Preparing to remove (any program can disable the removal)
        private const int DBT_DEVICEREMOVECOMPLETE = 0x8004; // removed
        private const int DBT_DEVTYP_VOLUME = 0x00000002; // drive type is logical volume

        private void RegisterQuery(string drive)
        {
            bool register = true;

            if (mFileToOpen == null)
            {
               
            }
            else
            {
              
                if (mFileToOpen.Contains(":"))
                {
                    string tmp = mFileToOpen.Substring(3);
                    string root = Path.GetPathRoot(drive);
                    mFileToOpen = Path.Combine(root, tmp);
                }
                else
                    mFileToOpen = Path.Combine(drive, mFileToOpen);
            }


            try
            {               
                if (mFileToOpen == null// open root directory
                    mFileOnFlash = null;
                else
                    mFileOnFlash = new FileStream(mFileToOpen, FileMode.Open);
            }
            catch (Exception)
            {
                // just do not register if the file could not be opened
                register = false;
            }


            if (register)
            {
              
                if (mFileOnFlash == null)
                    RegisterForDeviceChange(drive);
                else
                    // old version
                    RegisterForDeviceChange(true, mFileOnFlash.SafeFileHandle);

                mCurrentDrive = drive;
            }


        }


        private void RegisterForDeviceChange(string dirPath)
        {
            IntPtr handle = Native.OpenDirectory(dirPath);
            if (handle == IntPtr.Zero)
            {
                mDeviceNotifyHandle = IntPtr.Zero;
                return;
            }
            else
                mDirHandle = handle;    // save handle for closing it when unregistering

            // Register for handle
            DEV_BROADCAST_HANDLE data = new DEV_BROADCAST_HANDLE();
            data.dbch_devicetype = DBT_DEVTYP_HANDLE;
            data.dbch_reserved = 0;
            data.dbch_nameoffset = 0;
            //data.dbch_data = null;
            //data.dbch_eventguid = 0;
            data.dbch_handle = handle;
            data.dbch_hdevnotify = (IntPtr)0;
            int size = Marshal.SizeOf(data);
            data.dbch_size = size;
            IntPtr buffer = Marshal.AllocHGlobal(size);
            Marshal.StructureToPtr(data, buffer, true);

            mDeviceNotifyHandle = Native.RegisterDeviceNotification(mRecipientHandle, buffer, 0);

        }

      
        private void RegisterForDeviceChange(bool register, SafeFileHandle fileHandle)
        {
            if (register)
            {
                // Register for handle
                DEV_BROADCAST_HANDLE data = new DEV_BROADCAST_HANDLE();
                data.dbch_devicetype = DBT_DEVTYP_HANDLE;
                data.dbch_reserved = 0;
                data.dbch_nameoffset = 0;
                //data.dbch_data = null;
                //data.dbch_eventguid = 0;
                data.dbch_handle = fileHandle.DangerousGetHandle(); //Marshal. fileHandle;
                data.dbch_hdevnotify = (IntPtr)0;
                int size = Marshal.SizeOf(data);
                data.dbch_size = size;
                IntPtr buffer = Marshal.AllocHGlobal(size);
                Marshal.StructureToPtr(data, buffer, true);

                mDeviceNotifyHandle = Native.RegisterDeviceNotification(mRecipientHandle, buffer, 0);
            }
            else
            {
                // close the directory handle
                if (mDirHandle != IntPtr.Zero)
                {
                    Native.CloseDirectoryHandle(mDirHandle);
                    //    string er = Marshal.GetLastWin32Error().ToString();
                }

                // unregister
                if (mDeviceNotifyHandle != IntPtr.Zero)
                {
                    Native.UnregisterDeviceNotification(mDeviceNotifyHandle);
                }


                mDeviceNotifyHandle = IntPtr.Zero;
                mDirHandle = IntPtr.Zero;

                mCurrentDrive = "";
                if (mFileOnFlash != null)
                {
                    mFileOnFlash.Close();
                    mFileOnFlash = null;
                }
            }

        }

        private static char DriveMaskToLetter(int mask)
        {
            char letter;
            string drives = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            // 1 = A
            // 2 = B
            // 4 = C...
            int cnt = 0;
            int pom = mask / 2;
            while (pom != 0)
            {
                // while there is any bit set in the mask
                // shift it to the righ...               
                pom = pom / 2;
                cnt++;
            }

            if (cnt < drives.Length)
                letter = drives[cnt];
            else
                letter = '?';

            return letter;
        }

        #endregion

        #region Native Win32 API
        /// <summary>
        /// WinAPI functions
        /// </summary>       
        private class Native
        {
            //   HDEVNOTIFY RegisterDeviceNotification(HANDLE hRecipient,LPVOID NotificationFilter,DWORD Flags);
            [DllImport("user32.dll", CharSet = CharSet.Auto)]
            public static extern IntPtr RegisterDeviceNotification(IntPtr hRecipient, IntPtr NotificationFilter, uint Flags);

            [DllImport("user32.dll", CharSet = CharSet.Auto)]
            public static extern uint UnregisterDeviceNotification(IntPtr hHandle);

            //
            // CreateFile  - MSDN
            const uint GENERIC_READ = 0x80000000;
            const uint OPEN_EXISTING = 3;
            const uint FILE_SHARE_READ = 0x00000001;
            const uint FILE_SHARE_WRITE = 0x00000002;
            const uint FILE_ATTRIBUTE_NORMAL = 128;
            const uint FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
            static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);


            // should be "static extern unsafe"
            [DllImport("kernel32", SetLastError = true)]
            static extern IntPtr CreateFile(
                  string FileName,                    // file name
                  uint DesiredAccess,                 // access mode
                  uint ShareMode,                     // share mode
                  uint SecurityAttributes,            // Security Attributes
                  uint CreationDisposition,           // how to create
                  uint FlagsAndAttributes,            // file attributes
                  int hTemplateFile                   // handle to template file
                  );


            [DllImport("kernel32", SetLastError = true)]
            static extern bool CloseHandle(
                  IntPtr hObject   // handle to object
                  );

            /// <summary>
            /// Opens a directory, returns it's handle or zero.
            /// </summary>
            /// <param name="dirPath">path to the directory, e.g. "C:\\dir"</param>
            /// <returns>handle to the directory. Close it with CloseHandle().</returns>
            static public IntPtr OpenDirectory(string dirPath)
            {
                // open the existing file for reading         
                IntPtr handle = CreateFile(
                      dirPath,
                      GENERIC_READ,
                      FILE_SHARE_READ | FILE_SHARE_WRITE,
                      0,
                      OPEN_EXISTING,
                      FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL,
                      0);

                if (handle == INVALID_HANDLE_VALUE)
                    return IntPtr.Zero;
                else
                    return handle;
            }


            public static bool CloseDirectoryHandle(IntPtr handle)
            {
                return CloseHandle(handle);
            }
        }


        // Structure with information for RegisterDeviceNotification.
        [StructLayout(LayoutKind.Sequential)]
        public struct DEV_BROADCAST_HANDLE
        {
            public int dbch_size;
            public int dbch_devicetype;
            public int dbch_reserved;
            public IntPtr dbch_handle;
            public IntPtr dbch_hdevnotify;
            public Guid dbch_eventguid;
            public long dbch_nameoffset;
            //public byte[] dbch_data[1]; // = new byte[1];
            public byte dbch_data;
            public byte dbch_data1;
        }

        // Struct for parameters of the WM_DEVICECHANGE message
        [StructLayout(LayoutKind.Sequential)]
        public struct DEV_BROADCAST_VOLUME
        {
            public int dbcv_size;
            public int dbcv_devicetype;
            public int dbcv_reserved;
            public int dbcv_unitmask;
        }
        #endregion

    }



UsbDetectorEventArgs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace USB_Detector
{
    // Delegate for event handler to handle the device events
    public delegate void USBDetectorEventHandler(Object sender, USBDetectorEventArgs e);

    /// <summary>
    /// Our class for passing in custom arguments to our event handlers
    ///
    /// </summary>
    public class USBDetectorEventArgs : EventArgs
    {


        public USBDetectorEventArgs()
        {
            Cancel = false;
            Drive = "";
            HookQueryRemove = false;
        }

        /// <summary>
        /// Get/Set the value indicating that the event should be cancelled
        /// Only in QueryRemove handler.
        /// </summary>
        public bool Cancel;

        /// <summary>
        /// Drive letter for the device which caused this event
        /// </summary>
        public string Drive;

        /// <summary>
        /// Set to true in your DeviceArrived event handler if you wish to receive the
        /// QueryRemove event for this drive.
        /// </summary>
        public bool HookQueryRemove;

    }

}


Now create a Form which is invisble at runtime because we need a WndProc to get the Notification from the Native OS.

public partial class DriveForm : Form
    {
        public DriveForm()
        {
           
        }


         private Label label1;
        private USBDetector mDetector = null;

        /// <summary>
        /// Set up the hidden form.
        /// </summary>
        /// <param name="detector">DriveDetector object which will receive notification about USB drives, see WndProc</param>
        public DriveForm(USBDetector detector)
        {

            mDetector = detector;
            this.MinimizeBox = false;
            this.MaximizeBox = false;
            this.ShowInTaskbar = false;
            this.ShowIcon = false;
            this.FormBorderStyle = FormBorderStyle.None;
            this.Load += new System.EventHandler(this.Load_Form);
            this.Activated += new EventHandler(this.Form_Activated);
        }



        /// <summary>
        /// This function receives all the windows messages for this window (form).
        /// We call the DriveDetector from here so that is can pick up the messages about
        /// drives arrived and removed.
        /// </summary>
        protected override void WndProc(ref Message m)
        {
            base.WndProc(ref m);

            if (mDetector != null)
            {
                mDetector.WndProc(ref m);
            }
        }

        private void Load_Form(object sender, EventArgs e)
        {
            InitializeComponent();
            this.Size = new System.Drawing.Size(5, 5);
        }

        private void Form_Activated(object sender, EventArgs e)
        {
            this.Visible = false;
        }

       
    }


From this article you will get some interesting tool to get notify for portable devices plug in computer.



No comments:

Post a Comment