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
10 #define WIN32_NO_STATUS
21 #include <ndk/rtltypes.h>
22 #include <ndk/rtlfuncs.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)
34 /* FIXME: to improve, ugly hack */
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
);
48 OpenLetter(WCHAR Letter
)
53 wsprintf(Device
, L
"\\\\.\\%c:", Letter
);
56 return CreateFile(Device
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_WRITE
,
57 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
62 RefreshDevicesList(WCHAR Letter
)
66 WCHAR szText
[MAX_PATH
+ 50];
67 WCHAR szImage
[MAX_PATH
];
77 /* Get our list view */
78 hListView
= GetDlgItem(hWnd
, IDC_MAINDEVICES
);
81 SendMessage(hListView
, LVM_DELETEALLITEMS
, 0, 0);
83 /* Now, query the driver for all the devices */
84 hMaster
= OpenMaster();
85 if (hMaster
!= INVALID_HANDLE_VALUE
)
87 Res
= DeviceIoControl(hMaster
, IOCTL_VCDROM_ENUMERATE_DRIVES
, NULL
, 0, &Drives
, sizeof(Drives
), &BytesRead
, NULL
);
92 /* Loop to add all the devices to the list */
94 for (i
= 0; i
< Drives
.Count
; ++i
)
96 /* We'll query device one by one */
97 hLet
= OpenLetter(Drives
.Drives
[i
]);
98 if (hLet
!= INVALID_HANDLE_VALUE
)
100 /* Get info about the mounted image */
101 Res
= DeviceIoControl(hLet
, IOCTL_VCDROM_GET_IMAGE_PATH
, NULL
, 0, &Image
, sizeof(Image
), &BytesRead
, NULL
);
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
;
109 szText
[0] = Drives
.Drives
[i
];
113 /* If it worked, we'll complete with the info about the device:
114 * (mounted? which image?)
116 lResult
= SendMessage(hListView
, LVM_INSERTITEM
, 0, (LPARAM
)&lvItem
);
119 /* If it matches arg, that's the letter to select at the end */
120 if (Drives
.Drives
[i
] == Letter
)
125 /* We'll fill second column with info */
128 /* Gather the image path */
129 if (Image
.Length
!= 0)
131 memcpy(szImage
, Image
.Path
, Image
.Length
);
132 szImage
[(Image
.Length
/ sizeof(WCHAR
))] = L
'\0';
135 /* It's not mounted... */
136 if (Image
.Mounted
== 0)
138 /* If we don't have an image, set default text instead */
139 if (Image
.Length
== 0)
142 LoadString(hInstance
, IDS_NONE
, szImage
, sizeof(szImage
) / sizeof(WCHAR
));
143 szImage
[(sizeof(szImage
) / sizeof(WCHAR
)) - 1] = L
'\0';
146 /* Display the last known image */
148 LoadString(hInstance
, IDS_NOMOUNTED
, szFormat
, sizeof(szFormat
) / sizeof(WCHAR
));
149 szFormat
[(sizeof(szFormat
) / sizeof(WCHAR
)) - 1] = L
'\0';
151 swprintf(szText
, szFormat
, szImage
);
152 lvItem
.pszText
= szText
;
156 /* Mounted, just display the image path */
157 lvItem
.pszText
= szImage
;
161 SendMessage(hListView
, LVM_SETITEM
, lResult
, (LPARAM
)&lvItem
);
165 /* Don't leak our device */
170 /* If we had something to select, then just do it */
173 ZeroMemory(&lvItem
, sizeof(LVITEMW
));
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
);
186 SetServiceState(BOOLEAN Started
)
190 /* If started, disable start button */
191 hControl
= GetDlgItem(hDriverWnd
, IDC_DRIVERSTART
);
192 EnableWindow(hControl
, !Started
);
194 /* If started, enable stop button */
195 hControl
= GetDlgItem(hDriverWnd
, IDC_DRIVERSTOP
);
196 EnableWindow(hControl
, Started
);
200 QueryDriverInfo(HWND hDlg
)
203 SC_HANDLE hMgr
, hSvc
;
204 LPQUERY_SERVICE_CONFIGW pConfig
;
205 WCHAR szText
[2 * MAX_PATH
];
207 SERVICE_STATUS Status
;
211 /* Open service manager */
212 hMgr
= OpenSCManager(NULL
, NULL
, SC_MANAGER_CONNECT
);
215 /* Open our service */
216 hSvc
= OpenService(hMgr
, L
"Vcdrom", SERVICE_QUERY_CONFIG
| SERVICE_QUERY_STATUS
);
219 /* Probe its config size */
220 if (!QueryServiceConfig(hSvc
, NULL
, 0, &dwSize
) &&
221 GetLastError() == ERROR_INSUFFICIENT_BUFFER
)
223 /* And get its config */
224 pConfig
= HeapAlloc(GetProcessHeap(), 0, dwSize
);
226 if (QueryServiceConfig(hSvc
, pConfig
, dwSize
, &dwSize
))
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
);
234 HeapFree(GetProcessHeap(), 0, pConfig
);
238 if (QueryServiceStatus(hSvc
, &Status
))
240 if (Status
.dwCurrentState
!= SERVICE_RUNNING
&&
241 Status
.dwCurrentState
!= SERVICE_START_PENDING
)
243 SetServiceState(FALSE
);
247 SetServiceState(TRUE
);
251 CloseServiceHandle(hSvc
);
254 CloseServiceHandle(hMgr
);
257 /* FIXME: we don't allow uninstall/install */
259 hControl
= GetDlgItem(hDriverWnd
, IDC_DRIVERINSTALL
);
260 EnableWindow(hControl
, FALSE
);
261 hControl
= GetDlgItem(hDriverWnd
, IDC_DRIVERREMOVE
);
262 EnableWindow(hControl
, FALSE
);
265 /* Display our sub window */
266 ShowWindow(hDlg
, SW_SHOW
);
275 SC_HANDLE hMgr
, hSvc
;
277 /* Open the SC manager */
278 hMgr
= OpenSCManager(NULL
, NULL
, SC_MANAGER_CONNECT
);
281 /* Open the service matching our driver */
282 hSvc
= OpenService(hMgr
, L
"Vcdrom", SERVICE_START
);
287 StartService(hSvc
, 0, NULL
);
289 CloseServiceHandle(hSvc
);
291 /* Refresh the list in case there were persistent mounts */
292 RefreshDevicesList(0);
295 SetServiceState(TRUE
);
298 CloseServiceHandle(hMgr
);
306 SC_HANDLE hMgr
, hSvc
;
307 SERVICE_STATUS Status
;
309 /* Open the SC manager */
310 hMgr
= OpenSCManager(NULL
, NULL
, SC_MANAGER_CONNECT
);
313 /* Open the service matching our driver */
314 hSvc
= OpenService(hMgr
, L
"Vcdrom", SERVICE_STOP
);
319 ControlService(hSvc
, SERVICE_CONTROL_STOP
, &Status
);
321 CloseServiceHandle(hSvc
);
323 /* Refresh the list to clear it */
324 RefreshDevicesList(0);
327 SetServiceState(FALSE
);
330 CloseServiceHandle(hMgr
);
336 HandleDriverCommand(WPARAM wParam
,
341 /* Dispatch the message for the controls we manage */
342 Msg
= LOWORD(wParam
);
346 DestroyWindow(hDriverWnd
);
349 case IDC_DRIVERSTART
:
364 DriverDialogProc(HWND hDlg
,
369 /* Dispatch the message */
373 return QueryDriverInfo(hDlg
);
376 return HandleDriverCommand(wParam
, lParam
);
379 return DestroyWindow(hDlg
);
389 /* Just create a new window with our driver control dialog */
390 CreateDialogParamW(hInstance
,
391 MAKEINTRESOURCE(IDD_DRIVERWINDOW
),
399 SetMountFileName(HWND hDlg
,
406 /* Set the file name that was passed when creating dialog */
407 hEditText
= GetDlgItem(hMountWnd
, IDC_MOUNTIMAGE
);
408 SendMessage(hEditText
, WM_SETTEXT
, 0, lParam
);
410 /* Show our window */
411 ShowWindow(hDlg
, SW_SHOW
);
418 Min(DWORD a
, DWORD b
)
420 return (a
> b
? b
: a
);
428 WCHAR szFileName
[MAX_PATH
];
429 MOUNT_PARAMETERS MountParams
;
430 UNICODE_STRING NtPathName
;
433 BOOLEAN bPersist
, Res
;
434 WCHAR szKeyName
[256];
437 /* Zero our input structure */
438 ZeroMemory(&MountParams
, sizeof(MOUNT_PARAMETERS
));
440 /* Do we have to suppress UDF? */
441 hControl
= GetDlgItem(hMountWnd
, IDC_MOUNTUDF
);
442 if (SendMessage(hControl
, BM_GETCHECK
, 0, 0) == BST_CHECKED
)
444 MountParams
.Flags
|= MOUNT_FLAG_SUPP_UDF
;
447 /* Do we have to suppress Joliet? */
448 hControl
= GetDlgItem(hMountWnd
, IDC_MOUNTJOLIET
);
449 if (SendMessage(hControl
, BM_GETCHECK
, 0, 0) == BST_CHECKED
)
451 MountParams
.Flags
|= MOUNT_FLAG_SUPP_JOLIET
;
454 /* Should the mount be persistent? */
455 hControl
= GetDlgItem(hMountWnd
, IDC_MOUNTPERSIST
);
456 bPersist
= (SendMessage(hControl
, BM_GETCHECK
, 0, 0) == BST_CHECKED
);
458 /* Get the file name */
459 hControl
= GetDlgItem(hMountWnd
, IDC_MOUNTIMAGE
);
460 GetWindowText(hControl
, szFileName
, sizeof(szFileName
) / sizeof(WCHAR
));
462 /* Get NT path for the driver */
463 if (RtlDosPathNameToNtPathName_U(szFileName
, &NtPathName
, NULL
, NULL
))
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
);
470 /* Open the device */
471 hLet
= OpenLetter(wMountLetter
);
472 if (hLet
!= INVALID_HANDLE_VALUE
)
474 /* And issue the mount IOCTL */
475 Res
= DeviceIoControl(hLet
, IOCTL_VCDROM_MOUNT_IMAGE
, &MountParams
, sizeof(MountParams
), NULL
, 0, &BytesRead
, NULL
);
479 /* Refresh the list so that our mount appears */
480 RefreshDevicesList(0);
482 /* If mount succeed and has to persistent, write it to registry */
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
)
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
);
492 szKeyName
[0] = wMountLetter
;
495 RegSetValueExW(hKey
, L
"DRIVE", 0, REG_SZ
, (BYTE
*)szKeyName
, 3 * sizeof(WCHAR
));
503 DestroyWindow(hMountWnd
);
508 HandleMountCommand(WPARAM wParam
,
513 /* Dispatch the message for the controls we manage */
514 Msg
= LOWORD(wParam
);
517 case IDC_MOUNTCANCEL
:
518 DestroyWindow(hMountWnd
);
532 MountDialogProc(HWND hDlg
,
537 /* Dispatch the message */
541 return SetMountFileName(hDlg
, lParam
);
544 return HandleMountCommand(wParam
, lParam
);
547 return DestroyWindow(hDlg
);
562 /* Open the driver */
563 hMaster
= OpenMaster();
564 if (hMaster
!= INVALID_HANDLE_VALUE
)
566 /* Issue the create IOCTL */
567 Res
= DeviceIoControl(hMaster
, IOCTL_VCDROM_CREATE_DRIVE
, NULL
, 0, &Letter
, sizeof(WCHAR
), &BytesRead
, NULL
);
568 CloseHandle(hMaster
);
570 /* If it failed, reset the drive letter */
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.
580 RefreshDevicesList(Letter
);
586 GetSelectedDriveLetter(VOID
)
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... */
599 ZeroMemory(&lvItem
, sizeof(LVITEM
));
600 lvItem
.pszText
= szText
;
601 lvItem
.cchTextMax
= sizeof(szText
) / sizeof(WCHAR
);
604 /* Get the item text, it will be the drive letter */
605 SendMessage(hListView
, LVM_GETITEMTEXT
, iItem
, (LPARAM
)&lvItem
);
609 /* Nothing selected */
618 WCHAR szFileName
[MAX_PATH
];
619 OPENFILENAMEW ImageOpen
;
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 ;-))
626 wMountLetter
= GetSelectedDriveLetter();
627 /* We can only mount if we have a device */
628 if (wMountLetter
!= 0)
630 /* First of all, we need an image to mount */
631 ZeroMemory(&ImageOpen
, sizeof(OPENFILENAMEW
));
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
;
640 /* Get our filter (only supported images) */
643 LoadString(hInstance
, IDS_FILTER
, szFilter
, sizeof(szFilter
) / sizeof(WCHAR
));
644 szFilter
[(sizeof(szFilter
) / sizeof(WCHAR
)) - 1] = L
'\0';
646 /* Get the image name */
647 if (!GetOpenFileName(&ImageOpen
))
649 /* The user canceled... */
653 /* Start the mount dialog, so that user can select mount options */
654 CreateDialogParamW(hInstance
,
655 MAKEINTRESOURCE(IDD_MOUNTWINDOW
),
670 /* Get the select drive letter */
671 Letter
= GetSelectedDriveLetter();
675 hLet
= OpenLetter(Letter
);
676 if (hLet
!= INVALID_HANDLE_VALUE
)
678 /* And ask the driver for a remount */
679 DeviceIoControl(hLet
, IOCTL_STORAGE_LOAD_MEDIA
, NULL
, 0, NULL
, 0, &BytesRead
, NULL
);
683 /* Refresh the list, to display the fact the image is now mounted.
684 * Make sure it's selected as it was previously selected.
686 RefreshDevicesList(Letter
);
699 /* Get the select drive letter */
700 Letter
= GetSelectedDriveLetter();
704 hLet
= OpenLetter(Letter
);
705 if (hLet
!= INVALID_HANDLE_VALUE
)
707 /* And ask the driver for an ejection */
708 DeviceIoControl(hLet
, IOCTL_CDROM_EJECT_MEDIA
, NULL
, 0, NULL
, 0, &BytesRead
, NULL
);
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.
716 RefreshDevicesList(Letter
);
729 /* Get the select drive letter */
730 Letter
= GetSelectedDriveLetter();
734 hLet
= OpenLetter(Letter
);
735 if (hLet
!= INVALID_HANDLE_VALUE
)
737 /* And ask the driver for a deletion */
738 DeviceIoControl(hLet
, IOCTL_VCDROM_DELETE_DRIVE
, NULL
, 0, NULL
, 0, &BytesRead
, NULL
);
742 /* Refresh the list, to make the device disappear */
743 RefreshDevicesList(0);
750 HandleCommand(WPARAM wParam
,
755 /* Dispatch the message for the controls we manage */
756 Msg
= LOWORD(wParam
);
759 case IDC_MAINCONTROL
:
775 case IDC_MAINREMOUNT
:
792 VOID
ResetStats(VOID
)
795 static const WCHAR szText
[] = { L
'0', 0 };
797 /* Simply set '0' in all the edittext controls we
798 * manage regarding statistics.
800 hEditText
= GetDlgItem(hWnd
, IDC_MAINSECTORS
);
801 SendMessage(hEditText
, WM_SETTEXT
, 0, (LPARAM
)szText
);
803 hEditText
= GetDlgItem(hWnd
, IDC_MAINSIZE
);
804 SendMessage(hEditText
, WM_SETTEXT
, 0, (LPARAM
)szText
);
806 hEditText
= GetDlgItem(hWnd
, IDC_MAINFREE
);
807 SendMessage(hEditText
, WM_SETTEXT
, 0, (LPARAM
)szText
);
809 hEditText
= GetDlgItem(hWnd
, IDC_MAINTOTAL
);
810 SendMessage(hEditText
, WM_SETTEXT
, 0, (LPARAM
)szText
);
815 HandleNotify(LPARAM lParam
)
821 DWORD ClusterSector
, SectorSize
, FreeClusters
, Clusters
, Sectors
;
823 NmHdr
= (LPNMHDR
)lParam
;
825 /* We only want notifications on click on our devices list */
826 if (NmHdr
->code
== NM_CLICK
&&
827 NmHdr
->idFrom
== IDC_MAINDEVICES
)
829 /* Get the newly selected device */
830 Letter
= GetSelectedDriveLetter();
838 /* And get its capacities */
839 if (GetDiskFreeSpace(szText
, &ClusterSector
, &SectorSize
, &FreeClusters
, &Clusters
))
841 /* Nota: the application returns the total amount of clusters and sectors
844 Sectors
= ClusterSector
* Clusters
;
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
);
851 hEditText
= GetDlgItem(hWnd
, IDC_MAINSIZE
);
852 wsprintf(szText
, L
"%ld", SectorSize
);
853 SendMessage(hEditText
, WM_SETTEXT
, 0, (LPARAM
)szText
);
855 hEditText
= GetDlgItem(hWnd
, IDC_MAINFREE
);
856 wsprintf(szText
, L
"%ld", FreeClusters
);
857 SendMessage(hEditText
, WM_SETTEXT
, 0, (LPARAM
)szText
);
859 hEditText
= GetDlgItem(hWnd
, IDC_MAINTOTAL
);
860 wsprintf(szText
, L
"%ld", Clusters
);
861 SendMessage(hEditText
, WM_SETTEXT
, 0, (LPARAM
)szText
);
867 /* We failed somewhere, make sure we're at 0 */
878 CreateListViewColumns(HWND hDlg
)
885 hListView
= GetDlgItem(hDlg
, IDC_MAINDEVICES
);
887 /* Select the whole line, not just the first column */
888 SendMessage(hListView
, LVM_SETEXTENDEDLISTVIEWSTYLE
, 0, LVS_EX_FULLROWSELECT
);
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
;
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
);
901 /* Set up the second column */
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
);
908 /* Make sure stats are at 0 */
911 /* And populate our device list */
912 RefreshDevicesList(0);
920 MainDialogProc(HWND hDlg
,
925 /* Dispatch the message */
929 return CreateListViewColumns(hDlg
);
932 return HandleCommand(wParam
, lParam
);
935 return HandleNotify(lParam
);
938 return DestroyWindow(hDlg
);
950 wWinMain(HINSTANCE hInst
,
959 /* Just start our main window */
960 hWnd
= CreateDialogParamW(hInst
,
961 MAKEINTRESOURCE(IDD_MAINWINDOW
),
965 /* And dispatch messages in case of a success */
968 while (GetMessageW(&Msg
, NULL
, 0, 0) != 0)
970 TranslateMessage(&Msg
);
971 DispatchMessageW(&Msg
);