Subversion Repositories Code-Repo

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
28 Kevin 1
using System;
2
using System.Collections.Generic;
3
using System.Text;
4
using System.Windows.Forms;             // required for Message
5
using System.Runtime.InteropServices;   // required for Marshal
6
using System.IO;
7
using Microsoft.Win32.SafeHandles;     
8
// DriveDetector - rev. 1, Oct. 31 2007
9
 
10
namespace Dolinay
11
{
12
    /// <summary>
13
    /// Hidden Form which we use to receive Windows messages about flash drives
14
    /// </summary>
15
    internal class DetectorForm : Form
16
    {
17
        private Label label1;
18
        private DriveDetector mDetector = null;
19
 
20
        /// <summary>
21
        /// Set up the hidden form. 
22
        /// </summary>
23
        /// <param name="detector">DriveDetector object which will receive notification about USB drives, see WndProc</param>
24
        public DetectorForm(DriveDetector detector)
25
        {
26
            mDetector = detector;
27
            this.MinimizeBox = false;
28
            this.MaximizeBox = false;
29
            this.ShowInTaskbar = false;
30
            this.ShowIcon = false;
31
            this.FormBorderStyle = FormBorderStyle.None;
32
            this.Load += new System.EventHandler(this.Load_Form);
33
            this.Activated += new EventHandler(this.Form_Activated);
34
        }
35
 
36
        private void Load_Form(object sender, EventArgs e)
37
        {
38
            // We don't really need this, just to display the label in designer ...
39
            InitializeComponent();
40
 
41
            // Create really small form, invisible anyway.
42
            this.Size = new System.Drawing.Size(5, 5);
43
        }
44
 
45
        private void Form_Activated(object sender, EventArgs e)
46
        {
47
            this.Visible = false;
48
        }
49
 
50
        /// <summary>
51
        /// This function receives all the windows messages for this window (form).
52
        /// We call the DriveDetector from here so that is can pick up the messages about
53
        /// drives arrived and removed.
54
        /// </summary>
55
        protected override void WndProc(ref Message m)
56
        {
57
            base.WndProc(ref m);
58
 
59
            if (mDetector != null)
60
            {
61
                mDetector.WndProc(ref m);
62
            }
63
        }
64
 
65
        private void InitializeComponent()
66
        {
67
            this.label1 = new System.Windows.Forms.Label();
68
            this.SuspendLayout();
69
            // 
70
            // label1
71
            // 
72
            this.label1.AutoSize = true;
73
            this.label1.Location = new System.Drawing.Point(13, 30);
74
            this.label1.Name = "label1";
75
            this.label1.Size = new System.Drawing.Size(314, 13);
76
            this.label1.TabIndex = 0;
77
            this.label1.Text = "This is invisible form. To see DriveDetector code click View Code";
78
            // 
79
            // DetectorForm
80
            // 
81
            this.ClientSize = new System.Drawing.Size(360, 80);
82
            this.Controls.Add(this.label1);
83
            this.Name = "DetectorForm";
84
            this.ResumeLayout(false);
85
            this.PerformLayout();
86
 
87
        }
88
    }   // class DetectorForm
89
 
90
 
91
    // Delegate for event handler to handle the device events 
92
    public delegate void DriveDetectorEventHandler(Object sender, DriveDetectorEventArgs e);
93
 
94
    /// <summary>
95
    /// Our class for passing in custom arguments to our event handlers 
96
    /// 
97
    /// </summary>
98
    public class DriveDetectorEventArgs : EventArgs 
99
    {
100
 
101
 
102
        public DriveDetectorEventArgs()
103
        {
104
            Cancel = false;
105
            Drive = "";
106
            HookQueryRemove = false;
107
        }
108
 
109
        /// <summary>
110
        /// Get/Set the value indicating that the event should be cancelled 
111
        /// Only in QueryRemove handler.
112
        /// </summary>
113
        public bool Cancel;
114
 
115
        /// <summary>
116
        /// Drive letter for the device which caused this event 
117
        /// </summary>
118
        public string Drive;
119
 
120
        /// <summary>
121
        /// Set to true in your DeviceArrived event handler if you wish to receive the 
122
        /// QueryRemove event for this drive. 
123
        /// </summary>
124
        public bool HookQueryRemove;
125
 
126
    }
127
 
128
 
129
    /// <summary>
130
    /// Detects insertion or removal of removable drives.
131
    /// Use it in 1 or 2 steps:
132
    /// 1) Create instance of this class in your project and add handlers for the
133
    /// DeviceArrived, DeviceRemoved and QueryRemove events.
134
    /// AND (if you do not want drive detector to creaate a hidden form))
135
    /// 2) Override WndProc in your form and call DriveDetector's WndProc from there. 
136
    /// If you do not want to do step 2, just use the DriveDetector constructor without arguments and
137
    /// it will create its own invisible form to receive messages from Windows.
138
    /// </summary>
139
    class DriveDetector : IDisposable 
140
    {
141
        /// <summary>
142
        /// Events signalized to the client app.
143
        /// Add handlers for these events in your form to be notified of removable device events 
144
        /// </summary>
145
        public event DriveDetectorEventHandler DeviceArrived;
146
        public event DriveDetectorEventHandler DeviceRemoved;
147
        public event DriveDetectorEventHandler QueryRemove;
148
 
149
        /// <summary>
150
        /// The easiest way to use DriveDetector. 
151
        /// It will create hidden form for processing Windows messages about USB drives
152
        /// You do not need to override WndProc in your form.
153
        /// </summary>
154
        public DriveDetector()
155
        {
156
            DetectorForm  frm = new DetectorForm(this);
157
            frm.Show(); // will be hidden immediatelly
158
            Init(frm, null);
159
        }
160
 
161
        /// <summary>
162
        /// Alternate constructor.
163
        /// Pass in your Form and DriveDetector will not create hidden form.
164
        /// </summary>
165
        /// <param name="control">object which will receive Windows messages. 
166
        /// Pass "this" as this argument from your form class.</param>
167
        public DriveDetector(Control control)
168
        {
169
            Init(control, null);
170
        }
171
 
172
        /// <summary>
173
        /// Consructs DriveDetector object setting also path to file which should be opened
174
        /// when registering for query remove.  
175
        /// </summary>
176
        ///<param name="control">object which will receive Windows messages. 
177
        /// Pass "this" as this argument from your form class.</param>
178
        /// <param name="FileToOpen">Optional. Name of a file on the removable drive which should be opened. 
179
        /// If null, root directory of the drive will be opened. Opening a file is needed for us 
180
        /// to be able to register for the query remove message. TIP: For files use relative path without drive letter.
181
        /// e.g. "SomeFolder\file_on_flash.txt"</param>
182
        public DriveDetector(Control control, string FileToOpen)
183
        {
184
            Init(control, FileToOpen);
185
        }
186
 
187
        /// <summary>
188
        /// init the DriveDetector object
189
        /// </summary>
190
        /// <param name="intPtr"></param>
191
        private void Init(Control control, string fileToOpen)
192
        {
193
            mFileToOpen = fileToOpen;
194
            mFileOnFlash = null;
195
            mDeviceNotifyHandle = IntPtr.Zero;
196
            mRecipientHandle = control.Handle;
197
            mDirHandle = IntPtr.Zero;   // handle to the root directory of the flash drive which we open 
198
            mCurrentDrive = "";
199
        }
200
 
201
        /// <summary>
202
        /// Gets the value indicating whether the query remove event will be fired.
203
        /// </summary>
204
	    public bool IsQueryHooked
205
	    {
206
		    get
207
            {
208
                if (mDeviceNotifyHandle == IntPtr.Zero)
209
                    return false;
210
                else
211
                    return true;
212
            }
213
	    }
214
 
215
        /// <summary>
216
        /// Gets letter of drive which is currently hooked. Empty string if none.
217
        /// See also IsQueryHooked.
218
        /// </summary>
219
        public string HookedDrive
220
        {
221
            get
222
            {
223
                return mCurrentDrive;
224
            }
225
        }
226
 
227
        /// <summary>
228
        /// Gets the file stream for file which this class opened on a drive to be notified
229
        /// about it's removal. 
230
        /// This will be null unless you specified a file to open (DriveDetector opens root directory of the flash drive) 
231
        /// </summary>
232
        public FileStream OpenedFile
233
        {
234
            get
235
            {
236
                return mFileOnFlash;
237
            }
238
        }
239
 
240
        /// <summary>
241
        /// Hooks specified drive to receive a message when it is being removed.  
242
        /// This can be achieved also by setting e.HookQueryRemove to true in your 
243
        /// DeviceArrived event handler. 
244
        /// By default DriveDetector will open the root directory of the flash drive to obtain notification handle
245
        /// from Windows (to learn when the drive is about to be removed). 
246
        /// </summary>
247
        /// <param name="fileOnDrive">Drive letter or relative path to a file on the drive which should be 
248
        /// used to get a handle - required for registering to receive query remove messages.
249
        /// If only drive letter is specified (e.g. "D:\\", root directory of the drive will be opened.</param>
250
        /// <returns>true if hooked ok, false otherwise</returns>
251
        public bool EnableQueryRemove(string fileOnDrive)
252
        {
253
            if (fileOnDrive == null || fileOnDrive.Length == 0)
254
                throw new ArgumentException("Drive path must be supplied to register for Query remove.");
255
 
256
            if ( fileOnDrive.Length == 2 && fileOnDrive[1] == ':' )
257
                fileOnDrive += '\\';        // append "\\" if only drive letter with ":" was passed in.
258
 
259
            if (mDeviceNotifyHandle != IntPtr.Zero)
260
            {
261
                // Unregister first...
262
                RegisterForDeviceChange(false, null);
263
            }
264
 
265
            if (Path.GetFileName(fileOnDrive).Length == 0 ||!File.Exists(fileOnDrive))
266
                mFileToOpen = null;     // use root directory...
267
            else
268
                mFileToOpen = fileOnDrive;
269
 
270
            RegisterQuery(Path.GetPathRoot(fileOnDrive));
271
            if (mDeviceNotifyHandle == IntPtr.Zero)
272
                return false;   // failed to register
273
 
274
            return true;
275
        }
276
 
277
        /// <summary>
278
        /// Unhooks any currently hooked drive so that the query remove 
279
        /// message is not generated for it.
280
        /// </summary>
281
        public void DisableQueryRemove()
282
        {
283
            if (mDeviceNotifyHandle != IntPtr.Zero)
284
            {
285
                RegisterForDeviceChange(false, null);
286
            }
287
        }
288
 
289
 
290
        /// <summary>
291
        /// Unregister and close the file we may have opened on the removable drive. 
292
        /// Garbage collector will call this method.
293
        /// </summary>
294
        public void Dispose()
295
        {
296
            RegisterForDeviceChange(false, null);
297
        }
298
 
299
 
300
        #region WindowProc
301
        /// <summary>
302
        /// Message handler which must be called from client form.
303
        /// Processes Windows messages and calls event handlers. 
304
        /// </summary>
305
        /// <param name="m"></param>
306
        public void WndProc(ref Message m)
307
        {
308
            int devType;
309
            char c;
310
 
311
            if (m.Msg == WM_DEVICECHANGE)
312
            {
313
                // WM_DEVICECHANGE can have several meanings depending on the WParam value...
314
                switch (m.WParam.ToInt32())
315
                {
316
 
317
                        //
318
                        // New device has just arrived
319
                        //
320
                    case DBT_DEVICEARRIVAL:
321
 
322
                        devType = Marshal.ReadInt32(m.LParam, 4);
323
                        if (devType == DBT_DEVTYP_VOLUME)
324
                        {
325
                            DEV_BROADCAST_VOLUME vol;
326
                            vol = (DEV_BROADCAST_VOLUME)
327
                                Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_VOLUME));
328
 
329
                            // Get the drive letter 
330
                            c = DriveMaskToLetter(vol.dbcv_unitmask);
331
 
332
 
333
                            //
334
                            // Call the client event handler
335
                            //
336
                            // We should create copy of the event before testing it and
337
                            // calling the delegate - if any
338
                            DriveDetectorEventHandler tempDeviceArrived = DeviceArrived;
339
                            if ( tempDeviceArrived != null )
340
                            {
341
                                DriveDetectorEventArgs e = new DriveDetectorEventArgs();
342
                                e.Drive = c + ":\\";
343
                                tempDeviceArrived(this, e);
344
 
345
                                // Register for query remove if requested
346
                                if (e.HookQueryRemove)
347
                                {
348
                                    // If something is already hooked, unhook it now
349
                                    if (mDeviceNotifyHandle != IntPtr.Zero)
350
                                    {
351
                                        RegisterForDeviceChange(false, null);
352
                                    }
353
 
354
                                   RegisterQuery(c + ":\\");
355
                                }
356
                            }     // if  has event handler
357
 
358
 
359
                        }
360
                        break;
361
 
362
 
363
 
364
                        //
365
                        // Device is about to be removed
366
                        // Any application can cancel the removal
367
                        //
368
                    case DBT_DEVICEQUERYREMOVE:
369
 
370
                        devType = Marshal.ReadInt32(m.LParam, 4);
371
                        if (devType == DBT_DEVTYP_HANDLE)
372
                        {
373
                            // TODO: we could get the handle for which this message is sent 
374
                            // from vol.dbch_handle and compare it against a list of handles for 
375
                            // which we have registered the query remove message (?)                                                 
376
                            //DEV_BROADCAST_HANDLE vol;
377
                            //vol = (DEV_BROADCAST_HANDLE)
378
                            //   Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_HANDLE));
379
                            // if ( vol.dbch_handle ....
380
 
381
 
382
                            //
383
                            // Call the event handler in client
384
                            //
385
                            DriveDetectorEventHandler tempQuery = QueryRemove;
386
                            if (tempQuery != null)
387
                            {
388
                                DriveDetectorEventArgs e = new DriveDetectorEventArgs();
389
                                e.Drive = mCurrentDrive;        // drive which is hooked
390
                                tempQuery(this, e);
391
 
392
                                // If the client wants to cancel, let Windows know
393
                                if (e.Cancel)
394
                                {                                    
395
                                    m.Result = (IntPtr)BROADCAST_QUERY_DENY;
396
                                }
397
                                else
398
                                {
399
                                    // Change 28.10.2007: Unregister the notification, this will
400
                                    // close the handle to file or root directory also. 
401
                                    // We have to close it anyway to allow the removal so
402
                                    // even if some other app cancels the removal we would not know about it...                                    
403
                                    RegisterForDeviceChange(false, null);   // will also close the mFileOnFlash
404
                                }
405
 
406
                           }                          
407
                        }
408
                        break;
409
 
410
 
411
                        //
412
                        // Device has been removed
413
                        //
414
                    case DBT_DEVICEREMOVECOMPLETE:
415
 
416
                        devType = Marshal.ReadInt32(m.LParam, 4);
417
                        if (devType == DBT_DEVTYP_VOLUME)
418
                        {
419
                            devType = Marshal.ReadInt32(m.LParam, 4);
420
                            if (devType == DBT_DEVTYP_VOLUME)
421
                            {
422
                                DEV_BROADCAST_VOLUME vol;
423
                                vol = (DEV_BROADCAST_VOLUME)
424
                                    Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_VOLUME));
425
                                c = DriveMaskToLetter(vol.dbcv_unitmask);
426
 
427
                                //
428
                                // Call the client event handler
429
                                //
430
                                DriveDetectorEventHandler tempDeviceRemoved = DeviceRemoved;
431
                                if (tempDeviceRemoved != null)
432
                                {
433
                                    DriveDetectorEventArgs e = new DriveDetectorEventArgs();
434
                                    e.Drive = c + ":\\";
435
                                    tempDeviceRemoved(this, e);
436
                                }
437
 
438
                                // TODO: we could unregister the notify handle here if we knew it is the
439
                                // right drive which has been just removed
440
                                //RegisterForDeviceChange(false, null);
441
                            }
442
                        }
443
                        break;
444
                }
445
 
446
            }
447
 
448
        }
449
        #endregion
450
 
451
 
452
 
453
        #region  Private Area
454
 
455
        /// <summary>
456
        /// New: 28.10.2007 - handle to root directory of flash drive which is opened
457
        /// for device notification
458
        /// </summary>
459
        private IntPtr mDirHandle = IntPtr.Zero;
460
 
461
        /// <summary>
462
        /// Class which contains also handle to the file opened on the flash drive
463
        /// </summary>
464
        private FileStream mFileOnFlash = null;
465
 
466
        /// <summary>
467
        /// Name of the file to try to open on the removable drive for query remove registration
468
        /// </summary>
469
        private string mFileToOpen;
470
 
471
        /// <summary>
472
        /// Handle to file which we keep opened on the drive if query remove message is required by the client
473
        /// </summary>       
474
        private IntPtr mDeviceNotifyHandle;
475
 
476
        /// <summary>
477
        /// Handle of the window which receives messages from Windows. This will be a form.
478
        /// </summary>
479
        private IntPtr mRecipientHandle;
480
 
481
        /// <summary>
482
        /// Drive which is currently hooked for query remove
483
        /// </summary>
484
        private string mCurrentDrive;   
485
 
486
 
487
        // Win32 constants
488
        private const int DBT_DEVTYP_DEVICEINTERFACE = 5;
489
        private const int DBT_DEVTYP_HANDLE = 6;
490
        private const int BROADCAST_QUERY_DENY = 0x424D5144;
491
        private const int WM_DEVICECHANGE = 0x0219;
492
        private const int DBT_DEVICEARRIVAL = 0x8000; // system detected a new device
493
        private const int DBT_DEVICEQUERYREMOVE = 0x8001;   // Preparing to remove (any program can disable the removal)
494
        private const int DBT_DEVICEREMOVECOMPLETE = 0x8004; // removed 
495
        private const int DBT_DEVTYP_VOLUME = 0x00000002; // drive type is logical volume
496
 
497
        /// <summary>
498
        /// Registers for receiving the query remove message for a given drive.
499
        /// We need to open a handle on that drive and register with this handle. 
500
        /// Client can specify this file in mFileToOpen or we will open root directory of the drive
501
        /// </summary>
502
        /// <param name="drive">drive for which to register. </param>
503
        private void RegisterQuery(string drive)
504
        {
505
            bool register = true;
506
 
507
            if (mFileToOpen == null)
508
            {
509
                // Change 28.10.2007 - Open the root directory if no file specified - leave mFileToOpen null 
510
                // If client gave us no file, let's pick one on the drive... 
511
                //mFileToOpen = GetAnyFile(drive);
512
                //if (mFileToOpen.Length == 0)
513
                //    return;     // no file found on the flash drive                
514
            }
515
            else
516
            {
517
                // Make sure the path in mFileToOpen contains valid drive
518
                // If there is a drive letter in the path, it may be different from the  actual
519
                // letter assigned to the drive now. We will cut it off and merge the actual drive 
520
                // with the rest of the path.
521
                if (mFileToOpen.Contains(":"))
522
                {
523
                    string tmp = mFileToOpen.Substring(3);
524
                    string root = Path.GetPathRoot(drive);
525
                    mFileToOpen = Path.Combine(root, tmp);
526
                }
527
                else
528
                    mFileToOpen = Path.Combine(drive, mFileToOpen);
529
            }
530
 
531
 
532
            try
533
            {
534
                //mFileOnFlash = new FileStream(mFileToOpen, FileMode.Open);
535
                // Change 28.10.2007 - Open the root directory 
536
                if (mFileToOpen == null)  // open root directory
537
                    mFileOnFlash = null;
538
                else
539
                    mFileOnFlash = new FileStream(mFileToOpen, FileMode.Open);
540
            }
541
            catch (Exception)
542
            {
543
                // just do not register if the file could not be opened
544
                register = false;
545
            }
546
 
547
 
548
            if (register)
549
            {
550
                //RegisterForDeviceChange(true, mFileOnFlash.SafeFileHandle);
551
                //mCurrentDrive = drive;
552
                // Change 28.10.2007 - Open the root directory 
553
                if (mFileOnFlash == null)
554
                    RegisterForDeviceChange(drive);
555
                else
556
                    // old version
557
                    RegisterForDeviceChange(true, mFileOnFlash.SafeFileHandle);
558
 
559
                mCurrentDrive = drive;
560
            }
561
 
562
 
563
        }
564
 
565
 
566
        /// <summary>
567
        /// New version which gets the handle automatically for specified directory
568
        /// Only for registering! Unregister with the old version of this function...
569
        /// </summary>
570
        /// <param name="register"></param>
571
        /// <param name="dirPath">e.g. C:\\dir</param>
572
        private void RegisterForDeviceChange(string dirPath)
573
        {
574
            IntPtr handle = Native.OpenDirectory(dirPath);
575
            if (handle == IntPtr.Zero)
576
            {
577
                mDeviceNotifyHandle = IntPtr.Zero;
578
                return;
579
            }
580
            else
581
                mDirHandle = handle;    // save handle for closing it when unregistering
582
 
583
            // Register for handle
584
            DEV_BROADCAST_HANDLE data = new DEV_BROADCAST_HANDLE();
585
            data.dbch_devicetype = DBT_DEVTYP_HANDLE;
586
            data.dbch_reserved = 0;
587
            data.dbch_nameoffset = 0;
588
            //data.dbch_data = null;
589
            //data.dbch_eventguid = 0;
590
            data.dbch_handle = handle;
591
            data.dbch_hdevnotify = (IntPtr)0;
592
            int size = Marshal.SizeOf(data);
593
            data.dbch_size = size;
594
            IntPtr buffer = Marshal.AllocHGlobal(size);
595
            Marshal.StructureToPtr(data, buffer, true);
596
 
597
            mDeviceNotifyHandle = Native.RegisterDeviceNotification(mRecipientHandle, buffer, 0);
598
 
599
        }
600
 
601
        /// <summary>
602
        /// Registers to be notified when the volume is about to be removed
603
        /// This is requierd if you want to get the QUERY REMOVE messages
604
        /// </summary>
605
        /// <param name="register">true to register, false to unregister</param>
606
        /// <param name="fileHandle">handle of a file opened on the removable drive</param>
607
        private void RegisterForDeviceChange(bool register, SafeFileHandle fileHandle)
608
        {
609
            if (register)
610
            {
611
                // Register for handle
612
                DEV_BROADCAST_HANDLE data = new DEV_BROADCAST_HANDLE();
613
                data.dbch_devicetype = DBT_DEVTYP_HANDLE;
614
                data.dbch_reserved = 0;
615
                data.dbch_nameoffset = 0;
616
                //data.dbch_data = null;
617
                //data.dbch_eventguid = 0;
618
                data.dbch_handle = fileHandle.DangerousGetHandle(); //Marshal. fileHandle; 
619
                data.dbch_hdevnotify = (IntPtr)0;
620
                int size = Marshal.SizeOf(data);
621
                data.dbch_size = size;
622
                IntPtr buffer = Marshal.AllocHGlobal(size);
623
                Marshal.StructureToPtr(data, buffer, true);
624
 
625
                mDeviceNotifyHandle = Native.RegisterDeviceNotification(mRecipientHandle, buffer, 0);
626
            }
627
            else
628
            {
629
                // close the directory handle
630
                if (mDirHandle != IntPtr.Zero)
631
                {
632
                    Native.CloseDirectoryHandle(mDirHandle);
633
                    //    string er = Marshal.GetLastWin32Error().ToString();
634
                }
635
 
636
                // unregister
637
                if (mDeviceNotifyHandle != IntPtr.Zero)
638
                {
639
                    Native.UnregisterDeviceNotification(mDeviceNotifyHandle);                                        
640
                }
641
 
642
 
643
                mDeviceNotifyHandle = IntPtr.Zero;
644
                mDirHandle = IntPtr.Zero;
645
 
646
                mCurrentDrive = "";
647
                if (mFileOnFlash != null)
648
                {
649
                    mFileOnFlash.Close();
650
                    mFileOnFlash = null;
651
                }
652
            }
653
 
654
        }
655
 
656
        /// <summary>
657
        /// Gets drive letter from a bit mask where bit 0 = A, bit 1 = B etc.
658
        /// There can actually be more than one drive in the mask but we 
659
        /// just use the last one in this case.
660
        /// </summary>
661
        /// <param name="mask"></param>
662
        /// <returns></returns>
663
        private static char DriveMaskToLetter(int mask)
664
        {
665
            char letter;
666
            string drives = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
667
            // 1 = A
668
            // 2 = B
669
            // 4 = C...
670
            int cnt = 0;
671
            int pom = mask / 2;     
672
            while (pom != 0)
673
            {
674
                // while there is any bit set in the mask
675
                // shift it to the righ...                
676
                pom = pom / 2;
677
                cnt++;
678
            }
679
 
680
            if (cnt < drives.Length)
681
                letter = drives[cnt];
682
            else
683
                letter = '?';
684
 
685
            return letter;
686
        }
687
 
688
        /* 28.10.2007 - no longer needed
689
        /// <summary>
690
        /// Searches for any file in a given path and returns its full path
691
        /// </summary>
692
        /// <param name="drive">drive to search</param>
693
        /// <returns>path of the file or empty string</returns>
694
        private string GetAnyFile(string drive)
695
        {
696
            string file = "";
697
            // First try files in the root
698
            string[] files = Directory.GetFiles(drive);
699
            if (files.Length == 0)
700
            {
701
                // if no file in the root, search whole drive
702
                files = Directory.GetFiles(drive, "*.*", SearchOption.AllDirectories);
703
            }
704
 
705
            if (files.Length > 0)
706
                file = files[0];        // get the first file
707
 
708
            // return empty string if no file found
709
            return file;
710
        }*/
711
        #endregion
712
 
713
 
714
        #region Native Win32 API
715
        /// <summary>
716
        /// WinAPI functions
717
        /// </summary>        
718
        private class Native
719
        {
720
            //   HDEVNOTIFY RegisterDeviceNotification(HANDLE hRecipient,LPVOID NotificationFilter,DWORD Flags);
721
            [DllImport("user32.dll", CharSet = CharSet.Auto)]
722
            public static extern IntPtr RegisterDeviceNotification(IntPtr hRecipient, IntPtr NotificationFilter, uint Flags);
723
 
724
            [DllImport("user32.dll", CharSet = CharSet.Auto)]
725
            public static extern uint UnregisterDeviceNotification(IntPtr hHandle);
726
 
727
            //
728
            // CreateFile  - MSDN
729
            const uint GENERIC_READ = 0x80000000;
730
            const uint OPEN_EXISTING = 3;
731
            const uint FILE_SHARE_READ = 0x00000001;
732
            const uint FILE_SHARE_WRITE = 0x00000002;
733
            const uint FILE_ATTRIBUTE_NORMAL = 128;
734
            const uint FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
735
            static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
736
 
737
 
738
            // should be "static extern unsafe"
739
            [DllImport("kernel32", SetLastError = true)]
740
            static extern IntPtr CreateFile(
741
                  string FileName,                    // file name
742
                  uint DesiredAccess,                 // access mode
743
                  uint ShareMode,                     // share mode
744
                  uint SecurityAttributes,            // Security Attributes
745
                  uint CreationDisposition,           // how to create
746
                  uint FlagsAndAttributes,            // file attributes
747
                  int hTemplateFile                   // handle to template file
748
                  );
749
 
750
 
751
            [DllImport("kernel32", SetLastError = true)]
752
            static extern bool CloseHandle(
753
                  IntPtr hObject   // handle to object
754
                  );
755
 
756
            /// <summary>
757
            /// Opens a directory, returns it's handle or zero.
758
            /// </summary>
759
            /// <param name="dirPath">path to the directory, e.g. "C:\\dir"</param>
760
            /// <returns>handle to the directory. Close it with CloseHandle().</returns>
761
            static public IntPtr OpenDirectory(string dirPath)
762
            {
763
                // open the existing file for reading          
764
                IntPtr handle = CreateFile(
765
                      dirPath,
766
                      GENERIC_READ,
767
                      FILE_SHARE_READ | FILE_SHARE_WRITE,
768
                      0,
769
                      OPEN_EXISTING,
770
                      FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL,
771
                      0);
772
 
773
                if ( handle == INVALID_HANDLE_VALUE)
774
                    return IntPtr.Zero;
775
                else
776
                    return handle;
777
            }
778
 
779
 
780
            public static bool CloseDirectoryHandle(IntPtr handle)
781
            {
782
                return CloseHandle(handle);
783
            }
784
        }
785
 
786
 
787
        // Structure with information for RegisterDeviceNotification.
788
        [StructLayout(LayoutKind.Sequential)]
789
        public struct DEV_BROADCAST_HANDLE
790
        {
791
            public int dbch_size;
792
            public int dbch_devicetype;
793
            public int dbch_reserved;
794
            public IntPtr dbch_handle;
795
            public IntPtr dbch_hdevnotify;
796
            public Guid dbch_eventguid;
797
            public long dbch_nameoffset;
798
            //public byte[] dbch_data[1]; // = new byte[1];
799
            public byte dbch_data;
800
            public byte dbch_data1; 
801
        }
802
 
803
        // Struct for parameters of the WM_DEVICECHANGE message
804
        [StructLayout(LayoutKind.Sequential)]
805
        public struct DEV_BROADCAST_VOLUME
806
        {
807
            public int dbcv_size;
808
            public int dbcv_devicetype;
809
            public int dbcv_reserved;
810
            public int dbcv_unitmask;
811
        }
812
        #endregion
813
 
814
    }
815
}