mirror of
https://github.com/reactos/reactos.git
synced 2024-08-23 03:42:40 +00:00
977 lines
26 KiB
C
977 lines
26 KiB
C
/*
|
|
* PROJECT: ReactOS Virtual CD Control Tool
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: modules/rosapps/applications/vcdcontroltool/vcdcontroltool.c
|
|
* PURPOSE: main dialog implementation
|
|
* COPYRIGHT: Copyright 2018 Pierre Schweitzer
|
|
*
|
|
*/
|
|
|
|
#define WIN32_NO_STATUS
|
|
#include <stdarg.h>
|
|
#include <windef.h>
|
|
#include <winbase.h>
|
|
#include <winuser.h>
|
|
#include <wingdi.h>
|
|
#include <winsvc.h>
|
|
#include <winreg.h>
|
|
#include <commctrl.h>
|
|
#include <commdlg.h>
|
|
#include <wchar.h>
|
|
#include <ndk/rtltypes.h>
|
|
#include <ndk/rtlfuncs.h>
|
|
|
|
#include <vcdioctl.h>
|
|
#define IOCTL_CDROM_BASE FILE_DEVICE_CD_ROM
|
|
#define IOCTL_CDROM_EJECT_MEDIA CTL_CODE(IOCTL_CDROM_BASE, 0x0202, METHOD_BUFFERED, FILE_READ_ACCESS)
|
|
|
|
#include "resource.h"
|
|
|
|
HWND hWnd;
|
|
HWND hMountWnd;
|
|
HWND hDriverWnd;
|
|
HINSTANCE hInstance;
|
|
/* FIXME: to improve, ugly hack */
|
|
WCHAR wMountLetter;
|
|
|
|
static
|
|
HANDLE
|
|
OpenMaster(VOID)
|
|
{
|
|
/* Just open the device */
|
|
return CreateFile(L"\\\\.\\\\VirtualCdRom", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
}
|
|
|
|
static
|
|
HANDLE
|
|
OpenLetter(WCHAR Letter)
|
|
{
|
|
WCHAR Device[255];
|
|
|
|
/* Make name */
|
|
wsprintf(Device, L"\\\\.\\%c:", Letter);
|
|
|
|
/* And open */
|
|
return CreateFile(Device, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
}
|
|
|
|
static
|
|
VOID
|
|
RefreshDevicesList(WCHAR Letter)
|
|
{
|
|
HWND hListView;
|
|
WCHAR szFormat[50];
|
|
WCHAR szText[MAX_PATH + 50];
|
|
WCHAR szImage[MAX_PATH];
|
|
HANDLE hMaster, hLet;
|
|
DWORD BytesRead, i;
|
|
DRIVES_LIST Drives;
|
|
BOOLEAN Res;
|
|
IMAGE_PATH Image;
|
|
LVITEMW lvItem;
|
|
LRESULT lResult;
|
|
INT iSelected;
|
|
|
|
/* Get our list view */
|
|
hListView = GetDlgItem(hWnd, IDC_MAINDEVICES);
|
|
|
|
/* Purge it */
|
|
SendMessage(hListView, LVM_DELETEALLITEMS, 0, 0);
|
|
|
|
/* Now, query the driver for all the devices */
|
|
hMaster = OpenMaster();
|
|
if (hMaster != INVALID_HANDLE_VALUE)
|
|
{
|
|
Res = DeviceIoControl(hMaster, IOCTL_VCDROM_ENUMERATE_DRIVES, NULL, 0, &Drives, sizeof(Drives), &BytesRead, NULL);
|
|
CloseHandle(hMaster);
|
|
|
|
if (Res)
|
|
{
|
|
/* Loop to add all the devices to the list */
|
|
iSelected = -1;
|
|
for (i = 0; i < Drives.Count; ++i)
|
|
{
|
|
/* We'll query device one by one */
|
|
hLet = OpenLetter(Drives.Drives[i]);
|
|
if (hLet != INVALID_HANDLE_VALUE)
|
|
{
|
|
/* Get info about the mounted image */
|
|
Res = DeviceIoControl(hLet, IOCTL_VCDROM_GET_IMAGE_PATH, NULL, 0, &Image, sizeof(Image), &BytesRead, NULL);
|
|
if (Res)
|
|
{
|
|
/* First of all, add our driver letter to the list */
|
|
ZeroMemory(&lvItem, sizeof(LVITEMW));
|
|
lvItem.mask = LVIF_TEXT;
|
|
lvItem.pszText = szText;
|
|
lvItem.iItem = i;
|
|
szText[0] = Drives.Drives[i];
|
|
szText[1] = L':';
|
|
szText[2] = 0;
|
|
|
|
/* If it worked, we'll complete with the info about the device:
|
|
* (mounted? which image?)
|
|
*/
|
|
lResult = SendMessage(hListView, LVM_INSERTITEM, 0, (LPARAM)&lvItem);
|
|
if (lResult != -1)
|
|
{
|
|
/* If it matches arg, that's the letter to select at the end */
|
|
if (Drives.Drives[i] == Letter)
|
|
{
|
|
iSelected = lResult;
|
|
}
|
|
|
|
/* We'll fill second column with info */
|
|
lvItem.iSubItem = 1;
|
|
|
|
/* Gather the image path */
|
|
if (Image.Length != 0)
|
|
{
|
|
memcpy(szImage, Image.Path, Image.Length);
|
|
szImage[(Image.Length / sizeof(WCHAR))] = L'\0';
|
|
}
|
|
|
|
/* It's not mounted... */
|
|
if (Image.Mounted == 0)
|
|
{
|
|
/* If we don't have an image, set default text instead */
|
|
if (Image.Length == 0)
|
|
{
|
|
szImage[0] = 0;
|
|
LoadString(hInstance, IDS_NONE, szImage, sizeof(szImage) / sizeof(WCHAR));
|
|
szImage[(sizeof(szImage) / sizeof(WCHAR)) - 1] = L'\0';
|
|
}
|
|
|
|
/* Display the last known image */
|
|
szFormat[0] = 0;
|
|
LoadString(hInstance, IDS_NOMOUNTED, szFormat, sizeof(szFormat) / sizeof(WCHAR));
|
|
szFormat[(sizeof(szFormat) / sizeof(WCHAR)) - 1] = L'\0';
|
|
|
|
swprintf(szText, szFormat, szImage);
|
|
lvItem.pszText = szText;
|
|
}
|
|
else
|
|
{
|
|
/* Mounted, just display the image path */
|
|
lvItem.pszText = szImage;
|
|
}
|
|
|
|
/* Set text */
|
|
SendMessage(hListView, LVM_SETITEM, lResult, (LPARAM)&lvItem);
|
|
}
|
|
}
|
|
|
|
/* Don't leak our device */
|
|
CloseHandle(hLet);
|
|
}
|
|
}
|
|
|
|
/* If we had something to select, then just do it */
|
|
if (iSelected != -1)
|
|
{
|
|
ZeroMemory(&lvItem, sizeof(LVITEMW));
|
|
|
|
lvItem.mask = LVIF_STATE;
|
|
lvItem.iItem = iSelected;
|
|
lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
|
|
lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
|
|
SendMessage(hListView, LVM_SETITEMSTATE, iSelected, (LPARAM)&lvItem);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
SetServiceState(BOOLEAN Started)
|
|
{
|
|
HWND hControl;
|
|
|
|
/* If started, disable start button */
|
|
hControl = GetDlgItem(hDriverWnd, IDC_DRIVERSTART);
|
|
EnableWindow(hControl, !Started);
|
|
|
|
/* If started, enable stop button */
|
|
hControl = GetDlgItem(hDriverWnd, IDC_DRIVERSTOP);
|
|
EnableWindow(hControl, Started);
|
|
}
|
|
|
|
INT_PTR
|
|
QueryDriverInfo(HWND hDlg)
|
|
{
|
|
DWORD dwSize;
|
|
SC_HANDLE hMgr, hSvc;
|
|
LPQUERY_SERVICE_CONFIGW pConfig;
|
|
WCHAR szText[2 * MAX_PATH];
|
|
HWND hControl;
|
|
SERVICE_STATUS Status;
|
|
|
|
hDriverWnd = hDlg;
|
|
|
|
/* Open service manager */
|
|
hMgr = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
|
|
if (hMgr != NULL)
|
|
{
|
|
/* Open our service */
|
|
hSvc = OpenService(hMgr, L"Vcdrom", SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS);
|
|
if (hSvc != NULL)
|
|
{
|
|
/* Probe its config size */
|
|
if (!QueryServiceConfig(hSvc, NULL, 0, &dwSize) &&
|
|
GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
/* And get its config */
|
|
pConfig = HeapAlloc(GetProcessHeap(), 0, dwSize);
|
|
|
|
if (QueryServiceConfig(hSvc, pConfig, dwSize, &dwSize))
|
|
{
|
|
/* Display name & driver */
|
|
wsprintf(szText, L"%s:\n(%s)", pConfig->lpDisplayName, pConfig->lpBinaryPathName);
|
|
hControl = GetDlgItem(hDriverWnd, IDC_DRIVERINFO);
|
|
SendMessage(hControl, WM_SETTEXT, 0, (LPARAM)szText);
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, pConfig);
|
|
}
|
|
|
|
/* Get its status */
|
|
if (QueryServiceStatus(hSvc, &Status))
|
|
{
|
|
if (Status.dwCurrentState != SERVICE_RUNNING &&
|
|
Status.dwCurrentState != SERVICE_START_PENDING)
|
|
{
|
|
SetServiceState(FALSE);
|
|
}
|
|
else
|
|
{
|
|
SetServiceState(TRUE);
|
|
}
|
|
}
|
|
|
|
CloseServiceHandle(hSvc);
|
|
}
|
|
|
|
CloseServiceHandle(hMgr);
|
|
}
|
|
|
|
/* FIXME: we don't allow uninstall/install */
|
|
{
|
|
hControl = GetDlgItem(hDriverWnd, IDC_DRIVERINSTALL);
|
|
EnableWindow(hControl, FALSE);
|
|
hControl = GetDlgItem(hDriverWnd, IDC_DRIVERREMOVE);
|
|
EnableWindow(hControl, FALSE);
|
|
}
|
|
|
|
/* Display our sub window */
|
|
ShowWindow(hDlg, SW_SHOW);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
VOID
|
|
StartDriver(VOID)
|
|
{
|
|
SC_HANDLE hMgr, hSvc;
|
|
|
|
/* Open the SC manager */
|
|
hMgr = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
|
|
if (hMgr != NULL)
|
|
{
|
|
/* Open the service matching our driver */
|
|
hSvc = OpenService(hMgr, L"Vcdrom", SERVICE_START);
|
|
if (hSvc != NULL)
|
|
{
|
|
/* Start it */
|
|
/* FIXME: improve */
|
|
StartService(hSvc, 0, NULL);
|
|
|
|
CloseServiceHandle(hSvc);
|
|
|
|
/* Refresh the list in case there were persistent mounts */
|
|
RefreshDevicesList(0);
|
|
|
|
/* Update buttons */
|
|
SetServiceState(TRUE);
|
|
}
|
|
|
|
CloseServiceHandle(hMgr);
|
|
}
|
|
}
|
|
|
|
static
|
|
VOID
|
|
StopDriver(VOID)
|
|
{
|
|
SC_HANDLE hMgr, hSvc;
|
|
SERVICE_STATUS Status;
|
|
|
|
/* Open the SC manager */
|
|
hMgr = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
|
|
if (hMgr != NULL)
|
|
{
|
|
/* Open the service matching our driver */
|
|
hSvc = OpenService(hMgr, L"Vcdrom", SERVICE_STOP);
|
|
if (hSvc != NULL)
|
|
{
|
|
/* Stop it */
|
|
/* FIXME: improve */
|
|
ControlService(hSvc, SERVICE_CONTROL_STOP, &Status);
|
|
|
|
CloseServiceHandle(hSvc);
|
|
|
|
/* Refresh the list to clear it */
|
|
RefreshDevicesList(0);
|
|
|
|
/* Update buttons */
|
|
SetServiceState(FALSE);
|
|
}
|
|
|
|
CloseServiceHandle(hMgr);
|
|
}
|
|
}
|
|
|
|
static
|
|
INT_PTR
|
|
HandleDriverCommand(WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
WORD Msg;
|
|
|
|
/* Dispatch the message for the controls we manage */
|
|
Msg = LOWORD(wParam);
|
|
switch (Msg)
|
|
{
|
|
case IDC_DRIVEROK:
|
|
DestroyWindow(hDriverWnd);
|
|
return TRUE;
|
|
|
|
case IDC_DRIVERSTART:
|
|
StartDriver();
|
|
return TRUE;
|
|
|
|
case IDC_DRIVERSTOP:
|
|
StopDriver();
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static
|
|
INT_PTR
|
|
CALLBACK
|
|
DriverDialogProc(HWND hDlg,
|
|
UINT Message,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
/* Dispatch the message */
|
|
switch (Message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
return QueryDriverInfo(hDlg);
|
|
|
|
case WM_COMMAND:
|
|
return HandleDriverCommand(wParam, lParam);
|
|
|
|
case WM_CLOSE:
|
|
return DestroyWindow(hDlg);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static
|
|
VOID
|
|
DriverControl(VOID)
|
|
{
|
|
/* Just create a new window with our driver control dialog */
|
|
CreateDialogParamW(hInstance,
|
|
MAKEINTRESOURCE(IDD_DRIVERWINDOW),
|
|
NULL,
|
|
DriverDialogProc,
|
|
0);
|
|
}
|
|
|
|
static
|
|
INT_PTR
|
|
SetMountFileName(HWND hDlg,
|
|
LPARAM lParam)
|
|
{
|
|
HWND hEditText;
|
|
|
|
hMountWnd = hDlg;
|
|
|
|
/* Set the file name that was passed when creating dialog */
|
|
hEditText = GetDlgItem(hMountWnd, IDC_MOUNTIMAGE);
|
|
SendMessage(hEditText, WM_SETTEXT, 0, lParam);
|
|
|
|
/* Show our window */
|
|
ShowWindow(hDlg, SW_SHOW);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
FORCEINLINE
|
|
DWORD
|
|
Min(DWORD a, DWORD b)
|
|
{
|
|
return (a > b ? b : a);
|
|
}
|
|
|
|
static
|
|
VOID
|
|
PerformMount(VOID)
|
|
{
|
|
HWND hControl;
|
|
WCHAR szFileName[MAX_PATH];
|
|
MOUNT_PARAMETERS MountParams;
|
|
UNICODE_STRING NtPathName;
|
|
HANDLE hLet;
|
|
DWORD BytesRead;
|
|
BOOLEAN bPersist, Res;
|
|
WCHAR szKeyName[256];
|
|
HKEY hKey;
|
|
|
|
/* Zero our input structure */
|
|
ZeroMemory(&MountParams, sizeof(MOUNT_PARAMETERS));
|
|
|
|
/* Do we have to suppress UDF? */
|
|
hControl = GetDlgItem(hMountWnd, IDC_MOUNTUDF);
|
|
if (SendMessage(hControl, BM_GETCHECK, 0, 0) == BST_CHECKED)
|
|
{
|
|
MountParams.Flags |= MOUNT_FLAG_SUPP_UDF;
|
|
}
|
|
|
|
/* Do we have to suppress Joliet? */
|
|
hControl = GetDlgItem(hMountWnd, IDC_MOUNTJOLIET);
|
|
if (SendMessage(hControl, BM_GETCHECK, 0, 0) == BST_CHECKED)
|
|
{
|
|
MountParams.Flags |= MOUNT_FLAG_SUPP_JOLIET;
|
|
}
|
|
|
|
/* Should the mount be persistent? */
|
|
hControl = GetDlgItem(hMountWnd, IDC_MOUNTPERSIST);
|
|
bPersist = (SendMessage(hControl, BM_GETCHECK, 0, 0) == BST_CHECKED);
|
|
|
|
/* Get the file name */
|
|
hControl = GetDlgItem(hMountWnd, IDC_MOUNTIMAGE);
|
|
GetWindowText(hControl, szFileName, sizeof(szFileName) / sizeof(WCHAR));
|
|
|
|
/* Get NT path for the driver */
|
|
if (RtlDosPathNameToNtPathName_U(szFileName, &NtPathName, NULL, NULL))
|
|
{
|
|
/* Copy it in the parameter structure */
|
|
wcsncpy(MountParams.Path, NtPathName.Buffer, 255);
|
|
MountParams.Length = Min(NtPathName.Length, 255 * sizeof(WCHAR));
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
|
|
|
|
/* Open the device */
|
|
hLet = OpenLetter(wMountLetter);
|
|
if (hLet != INVALID_HANDLE_VALUE)
|
|
{
|
|
/* And issue the mount IOCTL */
|
|
Res = DeviceIoControl(hLet, IOCTL_VCDROM_MOUNT_IMAGE, &MountParams, sizeof(MountParams), NULL, 0, &BytesRead, NULL);
|
|
|
|
CloseHandle(hLet);
|
|
|
|
/* Refresh the list so that our mount appears */
|
|
RefreshDevicesList(0);
|
|
|
|
/* If mount succeed and has to persistent, write it to registry */
|
|
if (Res && bPersist)
|
|
{
|
|
wsprintf(szKeyName, L"SYSTEM\\CurrentControlSet\\Services\\Vcdrom\\Parameters\\Device%c", wMountLetter);
|
|
if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, szKeyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_CREATE_SUB_KEY | KEY_SET_VALUE, NULL, &hKey, NULL) == ERROR_SUCCESS)
|
|
{
|
|
wcsncpy(szKeyName, MountParams.Path, MountParams.Length);
|
|
szKeyName[MountParams.Length / sizeof(WCHAR)] = 0;
|
|
RegSetValueExW(hKey, L"IMAGE", 0, REG_SZ, (BYTE *)szKeyName, MountParams.Length);
|
|
|
|
szKeyName[0] = wMountLetter;
|
|
szKeyName[1] = L':';
|
|
szKeyName[2] = 0;
|
|
RegSetValueExW(hKey, L"DRIVE", 0, REG_SZ, (BYTE *)szKeyName, 3 * sizeof(WCHAR));
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DestroyWindow(hMountWnd);
|
|
}
|
|
|
|
static
|
|
INT_PTR
|
|
HandleMountCommand(WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
WORD Msg;
|
|
|
|
/* Dispatch the message for the controls we manage */
|
|
Msg = LOWORD(wParam);
|
|
switch (Msg)
|
|
{
|
|
case IDC_MOUNTCANCEL:
|
|
DestroyWindow(hMountWnd);
|
|
return TRUE;
|
|
|
|
case IDC_MOUNTOK:
|
|
PerformMount();
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static
|
|
INT_PTR
|
|
CALLBACK
|
|
MountDialogProc(HWND hDlg,
|
|
UINT Message,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
/* Dispatch the message */
|
|
switch (Message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
return SetMountFileName(hDlg, lParam);
|
|
|
|
case WM_COMMAND:
|
|
return HandleMountCommand(wParam, lParam);
|
|
|
|
case WM_CLOSE:
|
|
return DestroyWindow(hDlg);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static
|
|
VOID
|
|
AddDrive(VOID)
|
|
{
|
|
WCHAR Letter;
|
|
BOOLEAN Res;
|
|
DWORD BytesRead;
|
|
HANDLE hMaster;
|
|
|
|
/* Open the driver */
|
|
hMaster = OpenMaster();
|
|
if (hMaster != INVALID_HANDLE_VALUE)
|
|
{
|
|
/* Issue the create IOCTL */
|
|
Res = DeviceIoControl(hMaster, IOCTL_VCDROM_CREATE_DRIVE, NULL, 0, &Letter, sizeof(WCHAR), &BytesRead, NULL);
|
|
CloseHandle(hMaster);
|
|
|
|
/* If it failed, reset the drive letter */
|
|
if (!Res)
|
|
{
|
|
Letter = 0;
|
|
}
|
|
|
|
/* Refresh devices list. If it succeed, we pass the created drive letter
|
|
* So that, user can directly click on "mount" to mount an image, without
|
|
* needing to select appropriate device.
|
|
*/
|
|
RefreshDevicesList(Letter);
|
|
}
|
|
}
|
|
|
|
static
|
|
WCHAR
|
|
GetSelectedDriveLetter(VOID)
|
|
{
|
|
INT iItem;
|
|
HWND hListView;
|
|
LVITEM lvItem;
|
|
WCHAR szText[255];
|
|
|
|
/* Get the select device in the list view */
|
|
hListView = GetDlgItem(hWnd, IDC_MAINDEVICES);
|
|
iItem = SendMessage(hListView, LVM_GETNEXTITEM, -1, LVNI_SELECTED);
|
|
/* If there's one... */
|
|
if (iItem != -1)
|
|
{
|
|
ZeroMemory(&lvItem, sizeof(LVITEM));
|
|
lvItem.pszText = szText;
|
|
lvItem.cchTextMax = sizeof(szText) / sizeof(WCHAR);
|
|
szText[0] = 0;
|
|
|
|
/* Get the item text, it will be the drive letter */
|
|
SendMessage(hListView, LVM_GETITEMTEXT, iItem, (LPARAM)&lvItem);
|
|
return szText[0];
|
|
}
|
|
|
|
/* Nothing selected */
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
VOID
|
|
MountImage(VOID)
|
|
{
|
|
WCHAR szFilter[255];
|
|
WCHAR szFileName[MAX_PATH];
|
|
OPENFILENAMEW ImageOpen;
|
|
|
|
/* Get the selected drive letter
|
|
* FIXME: I make it global, because I don't know how to pass
|
|
* it properly to the later involved functions.
|
|
* Feel free to improve (without breaking ;-))
|
|
*/
|
|
wMountLetter = GetSelectedDriveLetter();
|
|
/* We can only mount if we have a device */
|
|
if (wMountLetter != 0)
|
|
{
|
|
/* First of all, we need an image to mount */
|
|
ZeroMemory(&ImageOpen, sizeof(OPENFILENAMEW));
|
|
|
|
ImageOpen.lStructSize = sizeof(ImageOpen);
|
|
ImageOpen.hwndOwner = NULL;
|
|
ImageOpen.lpstrFilter = szFilter;
|
|
ImageOpen.lpstrFile = szFileName;
|
|
ImageOpen.nMaxFile = MAX_PATH;
|
|
ImageOpen.Flags = OFN_EXPLORER| OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
|
|
|
|
/* Get our filter (only supported images) */
|
|
szFileName[0] = 0;
|
|
szFilter[0] = 0;
|
|
LoadString(hInstance, IDS_FILTER, szFilter, sizeof(szFilter) / sizeof(WCHAR));
|
|
szFilter[(sizeof(szFilter) / sizeof(WCHAR)) - 1] = L'\0';
|
|
|
|
/* Get the image name */
|
|
if (!GetOpenFileName(&ImageOpen))
|
|
{
|
|
/* The user canceled... */
|
|
return;
|
|
}
|
|
|
|
/* Start the mount dialog, so that user can select mount options */
|
|
CreateDialogParamW(hInstance,
|
|
MAKEINTRESOURCE(IDD_MOUNTWINDOW),
|
|
NULL,
|
|
MountDialogProc,
|
|
(LPARAM)szFileName);
|
|
}
|
|
}
|
|
|
|
static
|
|
VOID
|
|
RemountImage(VOID)
|
|
{
|
|
WCHAR Letter;
|
|
HANDLE hLet;
|
|
DWORD BytesRead;
|
|
|
|
/* Get the select drive letter */
|
|
Letter = GetSelectedDriveLetter();
|
|
if (Letter != 0)
|
|
{
|
|
/* Open it */
|
|
hLet = OpenLetter(Letter);
|
|
if (hLet != INVALID_HANDLE_VALUE)
|
|
{
|
|
/* And ask the driver for a remount */
|
|
DeviceIoControl(hLet, IOCTL_STORAGE_LOAD_MEDIA, NULL, 0, NULL, 0, &BytesRead, NULL);
|
|
|
|
CloseHandle(hLet);
|
|
|
|
/* Refresh the list, to display the fact the image is now mounted.
|
|
* Make sure it's selected as it was previously selected.
|
|
*/
|
|
RefreshDevicesList(Letter);
|
|
}
|
|
}
|
|
}
|
|
|
|
static
|
|
VOID
|
|
EjectDrive(VOID)
|
|
{
|
|
WCHAR Letter;
|
|
HANDLE hLet;
|
|
DWORD BytesRead;
|
|
|
|
/* Get the select drive letter */
|
|
Letter = GetSelectedDriveLetter();
|
|
if (Letter != 0)
|
|
{
|
|
/* Open it */
|
|
hLet = OpenLetter(Letter);
|
|
if (hLet != INVALID_HANDLE_VALUE)
|
|
{
|
|
/* And ask the driver for an ejection */
|
|
DeviceIoControl(hLet, IOCTL_CDROM_EJECT_MEDIA, NULL, 0, NULL, 0, &BytesRead, NULL);
|
|
|
|
CloseHandle(hLet);
|
|
|
|
/* Refresh the list, to display the fact the image is now unmounted but
|
|
* still known by the driver
|
|
* Make sure it's selected as it was previously selected.
|
|
*/
|
|
RefreshDevicesList(Letter);
|
|
}
|
|
}
|
|
}
|
|
|
|
static
|
|
VOID
|
|
RemoveDrive(VOID)
|
|
{
|
|
WCHAR Letter;
|
|
HANDLE hLet;
|
|
DWORD BytesRead;
|
|
|
|
/* Get the select drive letter */
|
|
Letter = GetSelectedDriveLetter();
|
|
if (Letter != 0)
|
|
{
|
|
/* Open it */
|
|
hLet = OpenLetter(Letter);
|
|
if (hLet != INVALID_HANDLE_VALUE)
|
|
{
|
|
/* And ask the driver for a deletion */
|
|
DeviceIoControl(hLet, IOCTL_VCDROM_DELETE_DRIVE, NULL, 0, NULL, 0, &BytesRead, NULL);
|
|
|
|
CloseHandle(hLet);
|
|
|
|
/* Refresh the list, to make the device disappear */
|
|
RefreshDevicesList(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
static
|
|
INT_PTR
|
|
HandleCommand(WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
WORD Msg;
|
|
|
|
/* Dispatch the message for the controls we manage */
|
|
Msg = LOWORD(wParam);
|
|
switch (Msg)
|
|
{
|
|
case IDC_MAINCONTROL:
|
|
DriverControl();
|
|
return TRUE;
|
|
|
|
case IDC_MAINOK:
|
|
DestroyWindow(hWnd);
|
|
return TRUE;
|
|
|
|
case IDC_MAINADD:
|
|
AddDrive();
|
|
return TRUE;
|
|
|
|
case IDC_MAINMOUNT:
|
|
MountImage();
|
|
return TRUE;
|
|
|
|
case IDC_MAINREMOUNT:
|
|
RemountImage();
|
|
return TRUE;
|
|
|
|
case IDC_MAINEJECT:
|
|
EjectDrive();
|
|
return TRUE;
|
|
|
|
case IDC_MAINREMOVE:
|
|
RemoveDrive();
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static
|
|
VOID ResetStats(VOID)
|
|
{
|
|
HWND hEditText;
|
|
static const WCHAR szText[] = { L'0', 0 };
|
|
|
|
/* Simply set '0' in all the edittext controls we
|
|
* manage regarding statistics.
|
|
*/
|
|
hEditText = GetDlgItem(hWnd, IDC_MAINSECTORS);
|
|
SendMessage(hEditText, WM_SETTEXT, 0, (LPARAM)szText);
|
|
|
|
hEditText = GetDlgItem(hWnd, IDC_MAINSIZE);
|
|
SendMessage(hEditText, WM_SETTEXT, 0, (LPARAM)szText);
|
|
|
|
hEditText = GetDlgItem(hWnd, IDC_MAINFREE);
|
|
SendMessage(hEditText, WM_SETTEXT, 0, (LPARAM)szText);
|
|
|
|
hEditText = GetDlgItem(hWnd, IDC_MAINTOTAL);
|
|
SendMessage(hEditText, WM_SETTEXT, 0, (LPARAM)szText);
|
|
}
|
|
|
|
static
|
|
INT_PTR
|
|
HandleNotify(LPARAM lParam)
|
|
{
|
|
WCHAR Letter;
|
|
LPNMHDR NmHdr;
|
|
WCHAR szText[255];
|
|
HWND hEditText;
|
|
DWORD ClusterSector, SectorSize, FreeClusters, Clusters, Sectors;
|
|
|
|
NmHdr = (LPNMHDR)lParam;
|
|
|
|
/* We only want notifications on click on our devices list */
|
|
if (NmHdr->code == NM_CLICK &&
|
|
NmHdr->idFrom == IDC_MAINDEVICES)
|
|
{
|
|
/* Get the newly selected device */
|
|
Letter = GetSelectedDriveLetter();
|
|
if (Letter != 0)
|
|
{
|
|
/* Setup its name */
|
|
szText[0] = Letter;
|
|
szText[1] = L':';
|
|
szText[2] = 0;
|
|
|
|
/* And get its capacities */
|
|
if (GetDiskFreeSpace(szText, &ClusterSector, &SectorSize, &FreeClusters, &Clusters))
|
|
{
|
|
/* Nota: the application returns the total amount of clusters and sectors
|
|
* So, compute it
|
|
*/
|
|
Sectors = ClusterSector * Clusters;
|
|
|
|
/* And now, update statistics about the device */
|
|
hEditText = GetDlgItem(hWnd, IDC_MAINSECTORS);
|
|
wsprintf(szText, L"%ld", Sectors);
|
|
SendMessage(hEditText, WM_SETTEXT, 0, (LPARAM)szText);
|
|
|
|
hEditText = GetDlgItem(hWnd, IDC_MAINSIZE);
|
|
wsprintf(szText, L"%ld", SectorSize);
|
|
SendMessage(hEditText, WM_SETTEXT, 0, (LPARAM)szText);
|
|
|
|
hEditText = GetDlgItem(hWnd, IDC_MAINFREE);
|
|
wsprintf(szText, L"%ld", FreeClusters);
|
|
SendMessage(hEditText, WM_SETTEXT, 0, (LPARAM)szText);
|
|
|
|
hEditText = GetDlgItem(hWnd, IDC_MAINTOTAL);
|
|
wsprintf(szText, L"%ld", Clusters);
|
|
SendMessage(hEditText, WM_SETTEXT, 0, (LPARAM)szText);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
/* We failed somewhere, make sure we're at 0 */
|
|
ResetStats();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static
|
|
INT_PTR
|
|
CreateListViewColumns(HWND hDlg)
|
|
{
|
|
WCHAR szText[255];
|
|
LVCOLUMNW lvColumn;
|
|
HWND hListView;
|
|
|
|
hWnd = hDlg;
|
|
hListView = GetDlgItem(hDlg, IDC_MAINDEVICES);
|
|
|
|
/* Select the whole line, not just the first column */
|
|
SendMessage(hListView, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT);
|
|
|
|
/* Set up the first column */
|
|
ZeroMemory(&lvColumn, sizeof(LVCOLUMNW));
|
|
lvColumn.pszText = szText;
|
|
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
|
|
lvColumn.fmt = LVCFMT_LEFT;
|
|
lvColumn.cx = 100;
|
|
szText[0] = 0;
|
|
LoadString(hInstance, IDS_DRIVE, szText, sizeof(szText) / sizeof(WCHAR));
|
|
szText[(sizeof(szText) / sizeof(WCHAR)) - 1] = L'\0';
|
|
SendMessage(hListView, LVM_INSERTCOLUMNW, 0, (LPARAM)&lvColumn);
|
|
|
|
/* Set up the second column */
|
|
szText[0] = 0;
|
|
lvColumn.cx = 350;
|
|
LoadString(hInstance, IDS_MAPPEDIMAGE, szText, sizeof(szText) / sizeof(WCHAR));
|
|
szText[(sizeof(szText) / sizeof(WCHAR)) - 1] = L'\0';
|
|
SendMessage(hListView, LVM_INSERTCOLUMNW, 1, (LPARAM)&lvColumn);
|
|
|
|
/* Make sure stats are at 0 */
|
|
ResetStats();
|
|
|
|
/* And populate our device list */
|
|
RefreshDevicesList(0);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
INT_PTR
|
|
CALLBACK
|
|
MainDialogProc(HWND hDlg,
|
|
UINT Message,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
/* Dispatch the message */
|
|
switch (Message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
return CreateListViewColumns(hDlg);
|
|
|
|
case WM_COMMAND:
|
|
return HandleCommand(wParam, lParam);
|
|
|
|
case WM_NOTIFY:
|
|
return HandleNotify(lParam);
|
|
|
|
case WM_CLOSE:
|
|
return DestroyWindow(hDlg);
|
|
|
|
case WM_DESTROY:
|
|
PostQuitMessage(0);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
INT
|
|
WINAPI
|
|
wWinMain(HINSTANCE hInst,
|
|
HINSTANCE hPrev,
|
|
LPWSTR Cmd,
|
|
int iCmd)
|
|
{
|
|
MSG Msg;
|
|
|
|
hInstance = hInst;
|
|
|
|
/* Just start our main window */
|
|
hWnd = CreateDialogParamW(hInst,
|
|
MAKEINTRESOURCE(IDD_MAINWINDOW),
|
|
NULL,
|
|
MainDialogProc,
|
|
0);
|
|
/* And dispatch messages in case of a success */
|
|
if (hWnd != NULL)
|
|
{
|
|
while (GetMessageW(&Msg, NULL, 0, 0) != 0)
|
|
{
|
|
TranslateMessage(&Msg);
|
|
DispatchMessageW(&Msg);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|