1 /*
2  * PROJECT:     ReactOS Virtual CD Control Tool
3  * LICENSE:     GPL - See COPYING in the top level directory
4  * FILE:        modules/rosapps/applications/vcdcontroltool/vcdcontroltool.c
5  * PURPOSE:     main dialog implementation
6  * COPYRIGHT:   Copyright 2018 Pierre Schweitzer
7  *
8  */
9 
10 #define WIN32_NO_STATUS
11 #include <stdarg.h>
12 #include <windef.h>
13 #include <winbase.h>
14 #include <winuser.h>
15 #include <wingdi.h>
16 #include <winsvc.h>
17 #include <winreg.h>
18 #include <commctrl.h>
19 #include <commdlg.h>
20 #include <wchar.h>
21 #include <ndk/rtltypes.h>
22 #include <ndk/rtlfuncs.h>
23 
24 #include <vcdioctl.h>
25 #define IOCTL_CDROM_BASE FILE_DEVICE_CD_ROM
26 #define IOCTL_CDROM_EJECT_MEDIA CTL_CODE(IOCTL_CDROM_BASE, 0x0202, METHOD_BUFFERED, FILE_READ_ACCESS)
27 
28 #include "resource.h"
29 
30 HWND hWnd;
31 HWND hMountWnd;
32 HWND hDriverWnd;
33 HINSTANCE hInstance;
34 /* FIXME: to improve, ugly hack */
35 WCHAR wMountLetter;
36 
37 static
38 HANDLE
39 OpenMaster(VOID)
40 {
41     /* Just open the device */
42     return CreateFile(L"\\\\.\\\\VirtualCdRom", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
43                       NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
44 }
45 
46 static
47 HANDLE
48 OpenLetter(WCHAR Letter)
49 {
50     WCHAR Device[255];
51 
52     /* Make name */
53     wsprintf(Device, L"\\\\.\\%c:", Letter);
54 
55     /* And open */
56     return CreateFile(Device, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
57                       NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
58 }
59 
60 static
61 VOID
62 RefreshDevicesList(WCHAR Letter)
63 {
64     HWND hListView;
65     WCHAR szFormat[50];
66     WCHAR szText[MAX_PATH + 50];
67     WCHAR szImage[MAX_PATH];
68     HANDLE hMaster, hLet;
69     DWORD BytesRead, i;
70     DRIVES_LIST Drives;
71     BOOLEAN Res;
72     IMAGE_PATH Image;
73     LVITEMW lvItem;
74     LRESULT lResult;
75     INT iSelected;
76 
77     /* Get our list view */
78     hListView = GetDlgItem(hWnd, IDC_MAINDEVICES);
79 
80     /* Purge it */
81     SendMessage(hListView, LVM_DELETEALLITEMS, 0, 0);
82 
83     /* Now, query the driver for all the devices */
84     hMaster = OpenMaster();
85     if (hMaster != INVALID_HANDLE_VALUE)
86     {
87         Res = DeviceIoControl(hMaster, IOCTL_VCDROM_ENUMERATE_DRIVES, NULL, 0, &Drives, sizeof(Drives), &BytesRead, NULL);
88         CloseHandle(hMaster);
89 
90         if (Res)
91         {
92             /* Loop to add all the devices to the list */
93             iSelected = -1;
94             for (i = 0; i < Drives.Count; ++i)
95             {
96                 /* We'll query device one by one */
97                 hLet = OpenLetter(Drives.Drives[i]);
98                 if (hLet != INVALID_HANDLE_VALUE)
99                 {
100                     /* Get info about the mounted image */
101                     Res = DeviceIoControl(hLet, IOCTL_VCDROM_GET_IMAGE_PATH, NULL, 0, &Image, sizeof(Image), &BytesRead, NULL);
102                     if (Res)
103                     {
104                         /* First of all, add our driver letter to the list */
105                         ZeroMemory(&lvItem, sizeof(LVITEMW));
106                         lvItem.mask = LVIF_TEXT;
107                         lvItem.pszText = szText;
108                         lvItem.iItem = i;
109                         szText[0] = Drives.Drives[i];
110                         szText[1] = L':';
111                         szText[2] = 0;
112 
113                         /* If it worked, we'll complete with the info about the device:
114                          * (mounted? which image?)
115                          */
116                         lResult = SendMessage(hListView, LVM_INSERTITEM, 0, (LPARAM)&lvItem);
117                         if (lResult != -1)
118                         {
119                             /* If it matches arg, that's the letter to select at the end */
120                             if (Drives.Drives[i] == Letter)
121                             {
122                                 iSelected = lResult;
123                             }
124 
125                             /* We'll fill second column with info */
126                             lvItem.iSubItem = 1;
127 
128                             /* Gather the image path */
129                             if (Image.Length != 0)
130                             {
131                                 memcpy(szImage, Image.Path, Image.Length);
132                                 szImage[(Image.Length / sizeof(WCHAR))] = L'\0';
133                             }
134 
135                             /* It's not mounted... */
136                             if (Image.Mounted == 0)
137                             {
138                                 /* If we don't have an image, set default text instead */
139                                 if (Image.Length == 0)
140                                 {
141                                     szImage[0] = 0;
142                                     LoadString(hInstance, IDS_NONE, szImage, sizeof(szImage) / sizeof(WCHAR));
143                                     szImage[(sizeof(szImage) / sizeof(WCHAR)) - 1] = L'\0';
144                                 }
145 
146                                 /* Display the last known image */
147                                 szFormat[0] = 0;
148                                 LoadString(hInstance, IDS_NOMOUNTED, szFormat, sizeof(szFormat) / sizeof(WCHAR));
149                                 szFormat[(sizeof(szFormat) / sizeof(WCHAR)) - 1] = L'\0';
150 
151                                 swprintf(szText, szFormat, szImage);
152                                 lvItem.pszText = szText;
153                             }
154                             else
155                             {
156                                 /* Mounted, just display the image path */
157                                 lvItem.pszText = szImage;
158                             }
159 
160                             /* Set text */
161                             SendMessage(hListView, LVM_SETITEM, lResult, (LPARAM)&lvItem);
162                         }
163                     }
164 
165                     /* Don't leak our device */
166                     CloseHandle(hLet);
167                 }
168             }
169 
170             /* If we had something to select, then just do it */
171             if (iSelected != -1)
172             {
173                 ZeroMemory(&lvItem, sizeof(LVITEMW));
174 
175                 lvItem.mask = LVIF_STATE;
176                 lvItem.iItem = iSelected;
177                 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
178                 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
179                 SendMessage(hListView, LVM_SETITEMSTATE, iSelected, (LPARAM)&lvItem);
180             }
181         }
182     }
183 }
184 
185 VOID
186 SetServiceState(BOOLEAN Started)
187 {
188     HWND hControl;
189 
190     /* If started, disable start button */
191     hControl = GetDlgItem(hDriverWnd, IDC_DRIVERSTART);
192     EnableWindow(hControl, !Started);
193 
194     /* If started, enable stop button */
195     hControl = GetDlgItem(hDriverWnd, IDC_DRIVERSTOP);
196     EnableWindow(hControl, Started);
197 }
198 
199 INT_PTR
200 QueryDriverInfo(HWND hDlg)
201 {
202     DWORD dwSize;
203     SC_HANDLE hMgr, hSvc;
204     LPQUERY_SERVICE_CONFIGW pConfig;
205     WCHAR szText[2 * MAX_PATH];
206     HWND hControl;
207     SERVICE_STATUS Status;
208 
209     hDriverWnd = hDlg;
210 
211     /* Open service manager */
212     hMgr = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
213     if (hMgr != NULL)
214     {
215         /* Open our service */
216         hSvc = OpenService(hMgr, L"Vcdrom", SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS);
217         if (hSvc != NULL)
218         {
219             /* Probe its config size */
220             if (!QueryServiceConfig(hSvc, NULL, 0, &dwSize) &&
221                 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
222             {
223                 /* And get its config */
224                 pConfig = HeapAlloc(GetProcessHeap(), 0, dwSize);
225 
226                 if (QueryServiceConfig(hSvc, pConfig, dwSize, &dwSize))
227                 {
228                     /* Display name & driver */
229                     wsprintf(szText, L"%s:\n(%s)", pConfig->lpDisplayName, pConfig->lpBinaryPathName);
230                     hControl = GetDlgItem(hDriverWnd, IDC_DRIVERINFO);
231                     SendMessage(hControl, WM_SETTEXT, 0, (LPARAM)szText);
232                 }
233 
234                 HeapFree(GetProcessHeap(), 0, pConfig);
235             }
236 
237             /* Get its status */
238             if (QueryServiceStatus(hSvc, &Status))
239             {
240                 if (Status.dwCurrentState != SERVICE_RUNNING &&
241                     Status.dwCurrentState != SERVICE_START_PENDING)
242                 {
243                     SetServiceState(FALSE);
244                 }
245                 else
246                 {
247                     SetServiceState(TRUE);
248                 }
249             }
250 
251             CloseServiceHandle(hSvc);
252         }
253 
254         CloseServiceHandle(hMgr);
255     }
256 
257     /* FIXME: we don't allow uninstall/install */
258     {
259         hControl = GetDlgItem(hDriverWnd, IDC_DRIVERINSTALL);
260         EnableWindow(hControl, FALSE);
261         hControl = GetDlgItem(hDriverWnd, IDC_DRIVERREMOVE);
262         EnableWindow(hControl, FALSE);
263     }
264 
265     /* Display our sub window */
266     ShowWindow(hDlg, SW_SHOW);
267 
268     return TRUE;
269 }
270 
271 static
272 VOID
273 StartDriver(VOID)
274 {
275     SC_HANDLE hMgr, hSvc;
276 
277     /* Open the SC manager */
278     hMgr = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
279     if (hMgr != NULL)
280     {
281         /* Open the service matching our driver */
282         hSvc = OpenService(hMgr, L"Vcdrom", SERVICE_START);
283         if (hSvc != NULL)
284         {
285             /* Start it */
286             /* FIXME: improve */
287             StartService(hSvc, 0, NULL);
288 
289             CloseServiceHandle(hSvc);
290 
291             /* Refresh the list in case there were persistent mounts */
292             RefreshDevicesList(0);
293 
294             /* Update buttons */
295             SetServiceState(TRUE);
296         }
297 
298         CloseServiceHandle(hMgr);
299     }
300 }
301 
302 static
303 VOID
304 StopDriver(VOID)
305 {
306     SC_HANDLE hMgr, hSvc;
307     SERVICE_STATUS Status;
308 
309     /* Open the SC manager */
310     hMgr = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
311     if (hMgr != NULL)
312     {
313         /* Open the service matching our driver */
314         hSvc = OpenService(hMgr, L"Vcdrom", SERVICE_STOP);
315         if (hSvc != NULL)
316         {
317             /* Stop it */
318             /* FIXME: improve */
319             ControlService(hSvc, SERVICE_CONTROL_STOP, &Status);
320 
321             CloseServiceHandle(hSvc);
322 
323             /* Refresh the list to clear it */
324             RefreshDevicesList(0);
325 
326             /* Update buttons */
327             SetServiceState(FALSE);
328         }
329 
330         CloseServiceHandle(hMgr);
331     }
332 }
333 
334 static
335 INT_PTR
336 HandleDriverCommand(WPARAM wParam,
337                     LPARAM lParam)
338 {
339     WORD Msg;
340 
341     /* Dispatch the message for the controls we manage */
342     Msg = LOWORD(wParam);
343     switch (Msg)
344     {
345         case IDC_DRIVEROK:
346             DestroyWindow(hDriverWnd);
347             return TRUE;
348 
349         case IDC_DRIVERSTART:
350             StartDriver();
351             return TRUE;
352 
353         case IDC_DRIVERSTOP:
354             StopDriver();
355             return TRUE;
356     }
357 
358     return FALSE;
359 }
360 
361 static
362 INT_PTR
363 CALLBACK
364 DriverDialogProc(HWND hDlg,
365                  UINT Message,
366                  WPARAM wParam,
367                  LPARAM lParam)
368 {
369     /* Dispatch the message */
370     switch (Message)
371     {
372         case WM_INITDIALOG:
373             return QueryDriverInfo(hDlg);
374 
375         case WM_COMMAND:
376             return HandleDriverCommand(wParam, lParam);
377 
378         case WM_CLOSE:
379             return DestroyWindow(hDlg);
380     }
381 
382     return FALSE;
383 }
384 
385 static
386 VOID
387 DriverControl(VOID)
388 {
389     /* Just create a new window with our driver control dialog */
390     CreateDialogParamW(hInstance,
391                        MAKEINTRESOURCE(IDD_DRIVERWINDOW),
392                        NULL,
393                        DriverDialogProc,
394                        0);
395 }
396 
397 static
398 INT_PTR
399 SetMountFileName(HWND hDlg,
400                  LPARAM lParam)
401 {
402     HWND hEditText;
403 
404     hMountWnd = hDlg;
405 
406     /* Set the file name that was passed when creating dialog */
407     hEditText = GetDlgItem(hMountWnd, IDC_MOUNTIMAGE);
408     SendMessage(hEditText, WM_SETTEXT, 0, lParam);
409 
410     /* Show our window */
411     ShowWindow(hDlg, SW_SHOW);
412 
413     return TRUE;
414 }
415 
416 FORCEINLINE
417 DWORD
418 Min(DWORD a, DWORD b)
419 {
420     return (a > b ? b : a);
421 }
422 
423 static
424 VOID
425 PerformMount(VOID)
426 {
427     HWND hControl;
428     WCHAR szFileName[MAX_PATH];
429     MOUNT_PARAMETERS MountParams;
430     UNICODE_STRING NtPathName;
431     HANDLE hLet;
432     DWORD BytesRead;
433     BOOLEAN bPersist, Res;
434     WCHAR szKeyName[256];
435     HKEY hKey;
436 
437     /* Zero our input structure */
438     ZeroMemory(&MountParams, sizeof(MOUNT_PARAMETERS));
439 
440     /* Do we have to suppress UDF? */
441     hControl = GetDlgItem(hMountWnd, IDC_MOUNTUDF);
442     if (SendMessage(hControl, BM_GETCHECK, 0, 0) == BST_CHECKED)
443     {
444         MountParams.Flags |= MOUNT_FLAG_SUPP_UDF;
445     }
446 
447     /* Do we have to suppress Joliet? */
448     hControl = GetDlgItem(hMountWnd, IDC_MOUNTJOLIET);
449     if (SendMessage(hControl, BM_GETCHECK, 0, 0) == BST_CHECKED)
450     {
451         MountParams.Flags |= MOUNT_FLAG_SUPP_JOLIET;
452     }
453 
454     /* Should the mount be persistent? */
455     hControl = GetDlgItem(hMountWnd, IDC_MOUNTPERSIST);
456     bPersist = (SendMessage(hControl, BM_GETCHECK, 0, 0) == BST_CHECKED);
457 
458     /* Get the file name */
459     hControl = GetDlgItem(hMountWnd, IDC_MOUNTIMAGE);
460     GetWindowText(hControl, szFileName, sizeof(szFileName) / sizeof(WCHAR));
461 
462     /* Get NT path for the driver */
463     if (RtlDosPathNameToNtPathName_U(szFileName, &NtPathName, NULL, NULL))
464     {
465         /* Copy it in the parameter structure */
466         wcsncpy(MountParams.Path, NtPathName.Buffer, 255);
467         MountParams.Length = Min(NtPathName.Length, 255 * sizeof(WCHAR));
468         RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
469 
470         /* Open the device */
471         hLet = OpenLetter(wMountLetter);
472         if (hLet != INVALID_HANDLE_VALUE)
473         {
474             /* And issue the mount IOCTL */
475             Res = DeviceIoControl(hLet, IOCTL_VCDROM_MOUNT_IMAGE, &MountParams, sizeof(MountParams), NULL, 0, &BytesRead, NULL);
476 
477             CloseHandle(hLet);
478 
479             /* Refresh the list so that our mount appears */
480             RefreshDevicesList(0);
481 
482             /* If mount succeed and has to persistent, write it to registry */
483             if (Res && bPersist)
484             {
485                 wsprintf(szKeyName, L"SYSTEM\\CurrentControlSet\\Services\\Vcdrom\\Parameters\\Device%c", wMountLetter);
486                 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, szKeyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_CREATE_SUB_KEY | KEY_SET_VALUE, NULL, &hKey, NULL) == ERROR_SUCCESS)
487                 {
488                     wcsncpy(szKeyName, MountParams.Path, MountParams.Length);
489                     szKeyName[MountParams.Length / sizeof(WCHAR)] = 0;
490                     RegSetValueExW(hKey, L"IMAGE", 0, REG_SZ, (BYTE *)szKeyName, MountParams.Length);
491 
492                     szKeyName[0] = wMountLetter;
493                     szKeyName[1] = L':';
494                     szKeyName[2] = 0;
495                     RegSetValueExW(hKey, L"DRIVE", 0, REG_SZ, (BYTE *)szKeyName, 3 * sizeof(WCHAR));
496 
497                     RegCloseKey(hKey);
498                 }
499             }
500         }
501     }
502 
503     DestroyWindow(hMountWnd);
504 }
505 
506 static
507 INT_PTR
508 HandleMountCommand(WPARAM wParam,
509                    LPARAM lParam)
510 {
511     WORD Msg;
512 
513     /* Dispatch the message for the controls we manage */
514     Msg = LOWORD(wParam);
515     switch (Msg)
516     {
517         case IDC_MOUNTCANCEL:
518             DestroyWindow(hMountWnd);
519             return TRUE;
520 
521         case IDC_MOUNTOK:
522             PerformMount();
523             return TRUE;
524     }
525 
526     return FALSE;
527 }
528 
529 static
530 INT_PTR
531 CALLBACK
532 MountDialogProc(HWND hDlg,
533                 UINT Message,
534                 WPARAM wParam,
535                 LPARAM lParam)
536 {
537     /* Dispatch the message */
538     switch (Message)
539     {
540         case WM_INITDIALOG:
541             return SetMountFileName(hDlg, lParam);
542 
543         case WM_COMMAND:
544             return HandleMountCommand(wParam, lParam);
545 
546         case WM_CLOSE:
547             return DestroyWindow(hDlg);
548     }
549 
550     return FALSE;
551 }
552 
553 static
554 VOID
555 AddDrive(VOID)
556 {
557     WCHAR Letter;
558     BOOLEAN Res;
559     DWORD BytesRead;
560     HANDLE hMaster;
561 
562     /* Open the driver */
563     hMaster = OpenMaster();
564     if (hMaster != INVALID_HANDLE_VALUE)
565     {
566         /* Issue the create IOCTL */
567         Res = DeviceIoControl(hMaster, IOCTL_VCDROM_CREATE_DRIVE, NULL, 0, &Letter, sizeof(WCHAR), &BytesRead, NULL);
568         CloseHandle(hMaster);
569 
570         /* If it failed, reset the drive letter */
571         if (!Res)
572         {
573             Letter = 0;
574         }
575 
576         /* Refresh devices list. If it succeed, we pass the created drive letter
577          * So that, user can directly click on "mount" to mount an image, without
578          * needing to select appropriate device.
579          */
580         RefreshDevicesList(Letter);
581     }
582 }
583 
584 static
585 WCHAR
586 GetSelectedDriveLetter(VOID)
587 {
588     INT iItem;
589     HWND hListView;
590     LVITEM lvItem;
591     WCHAR szText[255];
592 
593     /* Get the select device in the list view */
594     hListView = GetDlgItem(hWnd, IDC_MAINDEVICES);
595     iItem = SendMessage(hListView, LVM_GETNEXTITEM, -1, LVNI_SELECTED);
596     /* If there's one... */
597     if (iItem != -1)
598     {
599         ZeroMemory(&lvItem, sizeof(LVITEM));
600         lvItem.pszText = szText;
601         lvItem.cchTextMax = sizeof(szText) / sizeof(WCHAR);
602         szText[0] = 0;
603 
604         /* Get the item text, it will be the drive letter */
605         SendMessage(hListView, LVM_GETITEMTEXT, iItem, (LPARAM)&lvItem);
606         return szText[0];
607     }
608 
609     /* Nothing selected */
610     return 0;
611 }
612 
613 static
614 VOID
615 MountImage(VOID)
616 {
617     WCHAR szFilter[255];
618     WCHAR szFileName[MAX_PATH];
619     OPENFILENAMEW ImageOpen;
620 
621     /* Get the selected drive letter
622      * FIXME: I make it global, because I don't know how to pass
623      * it properly to the later involved functions.
624      * Feel free to improve (without breaking ;-))
625      */
626     wMountLetter = GetSelectedDriveLetter();
627     /* We can only mount if we have a device */
628     if (wMountLetter != 0)
629     {
630         /* First of all, we need an image to mount */
631         ZeroMemory(&ImageOpen, sizeof(OPENFILENAMEW));
632 
633         ImageOpen.lStructSize = sizeof(ImageOpen);
634         ImageOpen.hwndOwner = NULL;
635         ImageOpen.lpstrFilter = szFilter;
636         ImageOpen.lpstrFile = szFileName;
637         ImageOpen.nMaxFile = MAX_PATH;
638         ImageOpen.Flags = OFN_EXPLORER| OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
639 
640         /* Get our filter (only supported images) */
641         szFileName[0] = 0;
642         szFilter[0] = 0;
643         LoadString(hInstance, IDS_FILTER, szFilter, sizeof(szFilter) / sizeof(WCHAR));
644         szFilter[(sizeof(szFilter) / sizeof(WCHAR)) - 1] = L'\0';
645 
646         /* Get the image name */
647         if (!GetOpenFileName(&ImageOpen))
648         {
649             /* The user canceled... */
650             return;
651         }
652 
653         /* Start the mount dialog, so that user can select mount options */
654         CreateDialogParamW(hInstance,
655                            MAKEINTRESOURCE(IDD_MOUNTWINDOW),
656                            NULL,
657                            MountDialogProc,
658                            (LPARAM)szFileName);
659     }
660 }
661 
662 static
663 VOID
664 RemountImage(VOID)
665 {
666     WCHAR Letter;
667     HANDLE hLet;
668     DWORD BytesRead;
669 
670     /* Get the select drive letter */
671     Letter = GetSelectedDriveLetter();
672     if (Letter != 0)
673     {
674         /* Open it */
675         hLet = OpenLetter(Letter);
676         if (hLet != INVALID_HANDLE_VALUE)
677         {
678             /* And ask the driver for a remount */
679             DeviceIoControl(hLet, IOCTL_STORAGE_LOAD_MEDIA, NULL, 0, NULL, 0, &BytesRead, NULL);
680 
681             CloseHandle(hLet);
682 
683             /* Refresh the list, to display the fact the image is now mounted.
684              * Make sure it's selected as it was previously selected.
685              */
686             RefreshDevicesList(Letter);
687         }
688     }
689 }
690 
691 static
692 VOID
693 EjectDrive(VOID)
694 {
695     WCHAR Letter;
696     HANDLE hLet;
697     DWORD BytesRead;
698 
699     /* Get the select drive letter */
700     Letter = GetSelectedDriveLetter();
701     if (Letter != 0)
702     {
703         /* Open it */
704         hLet = OpenLetter(Letter);
705         if (hLet != INVALID_HANDLE_VALUE)
706         {
707             /* And ask the driver for an ejection */
708             DeviceIoControl(hLet, IOCTL_CDROM_EJECT_MEDIA, NULL, 0, NULL, 0, &BytesRead, NULL);
709 
710             CloseHandle(hLet);
711 
712             /* Refresh the list, to display the fact the image is now unmounted but
713              * still known by the driver
714              * Make sure it's selected as it was previously selected.
715              */
716             RefreshDevicesList(Letter);
717         }
718     }
719 }
720 
721 static
722 VOID
723 RemoveDrive(VOID)
724 {
725     WCHAR Letter;
726     HANDLE hLet;
727     DWORD BytesRead;
728 
729     /* Get the select drive letter */
730     Letter = GetSelectedDriveLetter();
731     if (Letter != 0)
732     {
733         /* Open it */
734         hLet = OpenLetter(Letter);
735         if (hLet != INVALID_HANDLE_VALUE)
736         {
737             /* And ask the driver for a deletion */
738             DeviceIoControl(hLet, IOCTL_VCDROM_DELETE_DRIVE, NULL, 0, NULL, 0, &BytesRead, NULL);
739 
740             CloseHandle(hLet);
741 
742             /* Refresh the list, to make the device disappear */
743             RefreshDevicesList(0);
744         }
745     }
746 }
747 
748 static
749 INT_PTR
750 HandleCommand(WPARAM wParam,
751               LPARAM lParam)
752 {
753     WORD Msg;
754 
755     /* Dispatch the message for the controls we manage */
756     Msg = LOWORD(wParam);
757     switch (Msg)
758     {
759         case IDC_MAINCONTROL:
760             DriverControl();
761             return TRUE;
762 
763         case IDC_MAINOK:
764             DestroyWindow(hWnd);
765             return TRUE;
766 
767         case IDC_MAINADD:
768             AddDrive();
769             return TRUE;
770 
771         case IDC_MAINMOUNT:
772             MountImage();
773             return TRUE;
774 
775         case IDC_MAINREMOUNT:
776             RemountImage();
777             return TRUE;
778 
779         case IDC_MAINEJECT:
780             EjectDrive();
781             return TRUE;
782 
783         case IDC_MAINREMOVE:
784             RemoveDrive();
785             return TRUE;
786     }
787 
788     return FALSE;
789 }
790 
791 static
792 VOID ResetStats(VOID)
793 {
794     HWND hEditText;
795     static const WCHAR szText[] = { L'0', 0 };
796 
797     /* Simply set '0' in all the edittext controls we
798      * manage regarding statistics.
799      */
800     hEditText = GetDlgItem(hWnd, IDC_MAINSECTORS);
801     SendMessage(hEditText, WM_SETTEXT, 0, (LPARAM)szText);
802 
803     hEditText = GetDlgItem(hWnd, IDC_MAINSIZE);
804     SendMessage(hEditText, WM_SETTEXT, 0, (LPARAM)szText);
805 
806     hEditText = GetDlgItem(hWnd, IDC_MAINFREE);
807     SendMessage(hEditText, WM_SETTEXT, 0, (LPARAM)szText);
808 
809     hEditText = GetDlgItem(hWnd, IDC_MAINTOTAL);
810     SendMessage(hEditText, WM_SETTEXT, 0, (LPARAM)szText);
811 }
812 
813 static
814 INT_PTR
815 HandleNotify(LPARAM lParam)
816 {
817     WCHAR Letter;
818     LPNMHDR NmHdr;
819     WCHAR szText[255];
820     HWND hEditText;
821     DWORD ClusterSector, SectorSize, FreeClusters, Clusters, Sectors;
822 
823     NmHdr = (LPNMHDR)lParam;
824 
825     /* We only want notifications on click on our devices list */
826     if (NmHdr->code == NM_CLICK &&
827         NmHdr->idFrom == IDC_MAINDEVICES)
828     {
829         /* Get the newly selected device */
830         Letter = GetSelectedDriveLetter();
831         if (Letter != 0)
832         {
833             /* Setup its name */
834             szText[0] = Letter;
835             szText[1] = L':';
836             szText[2] = 0;
837 
838             /* And get its capacities */
839             if (GetDiskFreeSpace(szText, &ClusterSector, &SectorSize, &FreeClusters, &Clusters))
840             {
841                 /* Nota: the application returns the total amount of clusters and sectors
842                  * So, compute it
843                  */
844                 Sectors = ClusterSector * Clusters;
845 
846                 /* And now, update statistics about the device */
847                 hEditText = GetDlgItem(hWnd, IDC_MAINSECTORS);
848                 wsprintf(szText, L"%ld", Sectors);
849                 SendMessage(hEditText, WM_SETTEXT, 0, (LPARAM)szText);
850 
851                 hEditText = GetDlgItem(hWnd, IDC_MAINSIZE);
852                 wsprintf(szText, L"%ld", SectorSize);
853                 SendMessage(hEditText, WM_SETTEXT, 0, (LPARAM)szText);
854 
855                 hEditText = GetDlgItem(hWnd, IDC_MAINFREE);
856                 wsprintf(szText, L"%ld", FreeClusters);
857                 SendMessage(hEditText, WM_SETTEXT, 0, (LPARAM)szText);
858 
859                 hEditText = GetDlgItem(hWnd, IDC_MAINTOTAL);
860                 wsprintf(szText, L"%ld", Clusters);
861                 SendMessage(hEditText, WM_SETTEXT, 0, (LPARAM)szText);
862 
863                 return TRUE;
864             }
865         }
866 
867         /* We failed somewhere, make sure we're at 0 */
868         ResetStats();
869 
870         return TRUE;
871     }
872 
873     return FALSE;
874 }
875 
876 static
877 INT_PTR
878 CreateListViewColumns(HWND hDlg)
879 {
880     WCHAR szText[255];
881     LVCOLUMNW lvColumn;
882     HWND hListView;
883 
884     hWnd = hDlg;
885     hListView = GetDlgItem(hDlg, IDC_MAINDEVICES);
886 
887     /* Select the whole line, not just the first column */
888     SendMessage(hListView, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT);
889 
890     /* Set up the first column */
891     ZeroMemory(&lvColumn, sizeof(LVCOLUMNW));
892     lvColumn.pszText = szText;
893     lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
894     lvColumn.fmt = LVCFMT_LEFT;
895     lvColumn.cx = 100;
896     szText[0] = 0;
897     LoadString(hInstance, IDS_DRIVE, szText, sizeof(szText) / sizeof(WCHAR));
898     szText[(sizeof(szText) / sizeof(WCHAR)) - 1] = L'\0';
899     SendMessage(hListView, LVM_INSERTCOLUMNW, 0, (LPARAM)&lvColumn);
900 
901     /* Set up the second column */
902     szText[0] = 0;
903     lvColumn.cx = 350;
904     LoadString(hInstance, IDS_MAPPEDIMAGE, szText, sizeof(szText) / sizeof(WCHAR));
905     szText[(sizeof(szText) / sizeof(WCHAR)) - 1] = L'\0';
906     SendMessage(hListView, LVM_INSERTCOLUMNW, 1, (LPARAM)&lvColumn);
907 
908     /* Make sure stats are at 0 */
909     ResetStats();
910 
911     /* And populate our device list */
912     RefreshDevicesList(0);
913 
914     return TRUE;
915 }
916 
917 static
918 INT_PTR
919 CALLBACK
920 MainDialogProc(HWND hDlg,
921                UINT Message,
922                WPARAM wParam,
923                LPARAM lParam)
924 {
925     /* Dispatch the message */
926     switch (Message)
927     {
928         case WM_INITDIALOG:
929             return CreateListViewColumns(hDlg);
930 
931         case WM_COMMAND:
932             return HandleCommand(wParam, lParam);
933 
934         case WM_NOTIFY:
935             return HandleNotify(lParam);
936 
937         case WM_CLOSE:
938             return DestroyWindow(hDlg);
939 
940         case WM_DESTROY:
941             PostQuitMessage(0);
942             return TRUE;
943     }
944 
945     return FALSE;
946 }
947 
948 INT
949 WINAPI
950 wWinMain(HINSTANCE hInst,
951          HINSTANCE hPrev,
952          LPWSTR Cmd,
953          int iCmd)
954 {
955     MSG Msg;
956 
957     hInstance = hInst;
958 
959     /* Just start our main window */
960     hWnd = CreateDialogParamW(hInst,
961                               MAKEINTRESOURCE(IDD_MAINWINDOW),
962                               NULL,
963                               MainDialogProc,
964                               0);
965     /* And dispatch messages in case of a success */
966     if (hWnd != NULL)
967     {
968         while (GetMessageW(&Msg, NULL, 0, 0) != 0)
969         {
970             TranslateMessage(&Msg);
971             DispatchMessageW(&Msg);
972         }
973     }
974 
975     return 0;
976 }
977