ciderpress/app/OpenVolumeDialog.cpp
Andy McFadden d8223dbcfd Relocate method comments
This moves method comments from the .cpp file to the .h file,
where users of the methods can find them.  This also makes it
possible for the IDE to show the comments when you mouse-hover over
the method name, though Visual Studio is a bit weak in this regard.

Also, added "override" keywords on overridden methods.  Reasonably
current versions of popular compilers seem to support this.

Also, don't have the return type on a separate line in the .cpp file.
The motivation for the practice -- quickly finding a method definition
with "^name" -- is less useful in C++ than C, and modern IDEs provide
more convenient ways to do the same thing.

Also, do some more conversion from unsigned types to uintXX_t.

This commit is primarily for the "app" directory.
2014-11-21 22:33:39 -08:00

686 lines
24 KiB
C++

/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Allow the user to select a disk volume.
*/
#include "stdafx.h"
#include "OpenVolumeDialog.h"
#include "HelpTopics.h"
#include "Main.h"
#include "../diskimg/Win32Extra.h" // need disk geometry calls
#include "../diskimg/ASPI.h"
BEGIN_MESSAGE_MAP(OpenVolumeDialog, CDialog)
ON_COMMAND(IDHELP, OnHelp)
//ON_NOTIFY(NM_CLICK, IDC_VOLUME_LIST, OnListClick)
ON_NOTIFY(LVN_ITEMCHANGED, IDC_VOLUME_LIST, OnListChange)
ON_NOTIFY(NM_DBLCLK, IDC_VOLUME_LIST, OnListDblClick)
ON_CBN_SELCHANGE(IDC_VOLUME_FILTER, OnVolumeFilterSelChange)
END_MESSAGE_MAP()
BOOL OpenVolumeDialog::OnInitDialog(void)
{
/*
* Sets up the list of drives.
*/
CDialog::OnInitDialog(); // do any DDX init stuff
const Preferences* pPreferences = GET_PREFERENCES();
long defaultFilter;
/* highlight/select entire line, not just filename */
CListCtrl* pListView = (CListCtrl*) GetDlgItem(IDC_VOLUME_LIST);
ASSERT(pListView != NULL);
ListView_SetExtendedListViewStyleEx(pListView->m_hWnd,
LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
/* disable the OK button until they click on something */
CButton* pButton = (CButton*) GetDlgItem(IDOK);
ASSERT(pButton != NULL);
pButton->EnableWindow(FALSE);
/* if the read-only state is fixed, don't let them change it */
if (!fAllowROChange) {
CButton* pButton;
pButton = (CButton*) GetDlgItem(IDC_OPENVOL_READONLY);
ASSERT(pButton != NULL);
pButton->EnableWindow(FALSE);
}
/* prep the combo box */
CComboBox* pCombo = (CComboBox*) GetDlgItem(IDC_VOLUME_FILTER);
ASSERT(pCombo != NULL);
defaultFilter = pPreferences->GetPrefLong(kPrVolumeFilter);
if (defaultFilter >= kBoth && defaultFilter <= kPhysical)
pCombo->SetCurSel(defaultFilter);
else {
LOGI("GLITCH: invalid defaultFilter in prefs (%d)", defaultFilter);
pCombo->SetCurSel(kLogical);
}
/* two columns */
CRect rect;
pListView->GetClientRect(&rect);
int width;
width = pListView->GetStringWidth(L"XXVolume or Device NameXXmmmmmm");
pListView->InsertColumn(0, L"Volume or Device Name", LVCFMT_LEFT, width);
pListView->InsertColumn(1, L"Remarks", LVCFMT_LEFT,
rect.Width() - width - ::GetSystemMetrics(SM_CXVSCROLL));
// Load the drive list.
LoadDriveList();
// Kluge the physical drive 0 stuff.
DiskImg::SetAllowWritePhys0(GET_PREFERENCES()->GetPrefBool(kPrOpenVolumePhys0));
return TRUE;
}
void OpenVolumeDialog::DoDataExchange(CDataExchange* pDX)
{
DDX_Check(pDX, IDC_OPENVOL_READONLY, fReadOnly);
LOGI("DoDataExchange: fReadOnly==%d", fReadOnly);
}
void OpenVolumeDialog::LoadDriveList(void)
{
CWaitCursor waitc;
CComboBox* pCombo;
CListCtrl* pListView;
int itemIndex = 0;
int filterSelection;
pListView = (CListCtrl*) GetDlgItem(IDC_VOLUME_LIST);
ASSERT(pListView != NULL);
pCombo = (CComboBox*) GetDlgItem(IDC_VOLUME_FILTER);
ASSERT(pCombo != NULL);
pListView->DeleteAllItems();
/*
* Load the logical and physical drive sets as needed. Do the "physical"
* set first because it's usually what we want.
*/
filterSelection = pCombo->GetCurSel();
if (filterSelection == kPhysical || filterSelection == kBoth)
LoadPhysicalDriveList(pListView, &itemIndex);
if (filterSelection == kLogical || filterSelection == kBoth)
LoadLogicalDriveList(pListView, &itemIndex);
}
bool OpenVolumeDialog::LoadLogicalDriveList(CListCtrl* pListView, int* pItemIndex)
{
DWORD drivesAvailable;
bool isWin9x = IsWin9x();
int itemIndex = *pItemIndex;
ASSERT(pListView != NULL);
drivesAvailable = GetLogicalDrives();
if (drivesAvailable == 0) {
LOGI("GetLogicalDrives failed, err=0x%08lx", drivesAvailable);
return false;
}
LOGI("GetLogicalDrives returned 0x%08lx", drivesAvailable);
// SetErrorMode(SEM_FAILCRITICALERRORS)
/* run through the list, from A-Z */
int i;
for (i = 0; i < kMaxLogicalDrives; i++) {
fVolumeInfo[i].driveType = DRIVE_UNKNOWN;
if ((drivesAvailable >> i) & 0x01) {
WCHAR driveName[] = L"_:\\";
driveName[0] = 'A' + i;
unsigned int driveType;
const WCHAR* driveTypeComment = NULL;
BOOL result;
driveType = fVolumeInfo[i].driveType = GetDriveType(driveName);
switch (driveType) {
case DRIVE_UNKNOWN:
// The drive type cannot be determined.
break;
case DRIVE_NO_ROOT_DIR:
// The root path is invalid. For example, no volume is mounted at the path.
break;
case DRIVE_REMOVABLE:
// The disk can be removed from the drive.
driveTypeComment = L"Removable";
break;
case DRIVE_FIXED:
// The disk cannot be removed from the drive.
driveTypeComment = L"Local Disk";
break;
case DRIVE_REMOTE:
// The drive is a remote (network) drive.
driveTypeComment = L"Network";
break;
case DRIVE_CDROM:
// The drive is a CD-ROM drive.
driveTypeComment = L"CD-ROM";
break;
case DRIVE_RAMDISK:
// The drive is a RAM disk.
break;
default:
LOGI("UNKNOWN DRIVE TYPE %d", driveType);
break;
}
if (driveType == DRIVE_CDROM && !DiskImgLib::Global::GetHasSPTI()) {
/* use "physical" device via ASPI instead */
LOGI("Not including CD-ROM '%ls' in logical drive list",
driveName);
continue;
}
WCHAR volNameBuf[256];
WCHAR fsNameBuf[64];
const WCHAR* errorComment = NULL;
//DWORD fsFlags;
CString entryName, entryRemarks;
result = ::GetVolumeInformation(driveName, volNameBuf,
NELEM(volNameBuf), NULL, NULL, NULL /*&fsFlags*/, fsNameBuf,
NELEM(fsNameBuf));
if (result == FALSE) {
DWORD err = GetLastError();
if (err == ERROR_UNRECOGNIZED_VOLUME) {
// Win2K: media exists but format not recognized
errorComment = L"Non-Windows format";
} else if (err == ERROR_NOT_READY) {
// Win2K: device exists but no media loaded
if (isWin9x) {
LOGI("Not showing drive '%ls': not ready",
driveName);
continue; // safer not to show it
} else
errorComment = L"Not ready";
} else if (err == ERROR_PATH_NOT_FOUND /*Win2K*/ ||
err == ERROR_INVALID_DATA /*Win98*/)
{
// Win2K/Win98: device letter not in use
LOGI("GetVolumeInformation '%ls': nothing there",
driveName);
continue;
} else if (err == ERROR_INVALID_PARAMETER) {
// Win2K: device is already open
//LOGI("GetVolumeInformation '%ls': currently open??",
// driveName);
errorComment = L"(currently open?)";
//continue;
} else if (err == ERROR_ACCESS_DENIED) {
// Win2K: disk is open no-read-sharing elsewhere
errorComment = L"(already open read-write)";
} else if (err == ERROR_GEN_FAILURE) {
// Win98: floppy format not recognzied
// --> we don't want to access ProDOS floppies via A: in
// Win98, so we skip it here
LOGI("GetVolumeInformation '%ls': general failure",
driveName);
continue;
} else if (err == ERROR_INVALID_FUNCTION) {
// Win2K: CD-ROM with HFS
if (driveType == DRIVE_CDROM)
errorComment = L"Non-Windows format";
else
errorComment = L"(invalid disc?)";
} else {
LOGI("GetVolumeInformation '%ls' failed: %ld",
driveName, GetLastError());
continue;
}
ASSERT(errorComment != NULL);
entryName.Format(L"(%c:)", 'A' + i);
if (driveTypeComment != NULL)
entryRemarks.Format(L"%ls - %ls", driveTypeComment,
errorComment);
else
entryRemarks.Format(L"%ls", errorComment);
} else {
entryName.Format(L"%ls (%c:)", volNameBuf, 'A' + i);
if (driveTypeComment != NULL)
entryRemarks.Format(L"%ls", driveTypeComment);
else
entryRemarks = "";
}
pListView->InsertItem(itemIndex, entryName);
pListView->SetItemText(itemIndex, 1, entryRemarks);
pListView->SetItemData(itemIndex, (DWORD) i + 'A');
//LOGI("%%%% added logical %d", itemIndex);
itemIndex++;
} else {
LOGI(" (drive %c not available)", i + 'A');
}
}
*pItemIndex = itemIndex;
return true;
}
bool OpenVolumeDialog::LoadPhysicalDriveList(CListCtrl* pListView, int* pItemIndex)
{
/*
* I don't see a clever way to do this in Win2K except to open the first 8
* or so devices and see what happens.
*
* Win9x isn't much better, though you can be reasonably confident that there
* are at most 4 floppy drives and 4 hard drives.
*
* TODO: check again for a better way
*/
bool isWin9x = IsWin9x();
int itemIndex = *pItemIndex;
int i;
if (isWin9x) {
// fairly arbitrary choices
const int kMaxFloppies = 4;
const int kMaxHardDrives = 8;
for (i = 0; i < kMaxFloppies; i++) {
CString driveName, remark;
bool result;
result = HasPhysicalDriveWin9x(i, &remark);
if (result) {
driveName.Format(L"Floppy disk %d", i);
pListView->InsertItem(itemIndex, driveName);
pListView->SetItemText(itemIndex, 1, remark);
pListView->SetItemData(itemIndex, (DWORD) i);
//LOGI("%%%% added floppy %d", itemIndex);
itemIndex++;
}
}
for (i = 0; i < kMaxHardDrives; i++) {
CString driveName, remark;
bool result;
result = HasPhysicalDriveWin9x(i + 128, &remark);
if (result) {
driveName.Format(L"Hard drive %d", i);
pListView->InsertItem(itemIndex, driveName);
pListView->SetItemText(itemIndex, 1, remark);
pListView->SetItemData(itemIndex, (DWORD) i + 128);
//LOGI("%%%% added HD %d", itemIndex);
itemIndex++;
}
}
} else {
for (i = 0; i < kMaxPhysicalDrives; i++) {
CString driveName, remark;
bool result;
result = HasPhysicalDriveWin2K(i + 128, &remark);
if (result) {
driveName.Format(L"Physical disk %d", i);
pListView->InsertItem(itemIndex, driveName);
pListView->SetItemText(itemIndex, 1, remark);
pListView->SetItemData(itemIndex, (DWORD) i + 128); // HD volume
itemIndex++;
}
}
}
if (DiskImgLib::Global::GetHasASPI()) {
LOGI("IGNORING ASPI");
#if 0 // can we remove this?
DIError dierr;
DiskImgLib::ASPI* pASPI = DiskImgLib::Global::GetASPI();
ASPIDevice* deviceArray = NULL;
int numDevices;
dierr = pASPI->GetAccessibleDevices(
ASPI::kDevMaskCDROM | ASPI::kDevMaskHardDrive,
&deviceArray, &numDevices);
if (dierr == kDIErrNone) {
LOGI("Adding %d ASPI CD-ROM devices", numDevices);
for (i = 0; i < numDevices; i++) {
CString driveName, remark;
CString addr, vendor, product;
DWORD aspiAddr;
addr.Format("ASPI %d:%d:%d",
deviceArray[i].GetAdapter(),
deviceArray[i].GetTarget(),
deviceArray[i].GetLun());
vendor = deviceArray[i].GetVendorID();
vendor.TrimRight();
product = deviceArray[i].GetProductID();
product.TrimRight();
driveName.Format("%s %s", vendor, product);
if (deviceArray[i].GetDeviceType() == ASPIDevice::kTypeCDROM)
remark = "CD-ROM";
else if (deviceArray[i].GetDeviceType() == ASPIDevice::kTypeDASD)
remark = "Direct-access device";
if (!deviceArray[i].GetDeviceReady())
remark += " - Not ready";
aspiAddr = (DWORD) 0xaa << 24 |
(DWORD) deviceArray[i].GetAdapter() << 16 |
(DWORD) deviceArray[i].GetTarget() << 8 |
(DWORD) deviceArray[i].GetLun();
//LOGI("ADDR for '%s' is 0x%08lx",
// (const char*) driveName, aspiAddr);
pListView->InsertItem(itemIndex, driveName);
pListView->SetItemText(itemIndex, 1, remark);
pListView->SetItemData(itemIndex, aspiAddr);
itemIndex++;
}
}
delete[] deviceArray;
#endif
}
*pItemIndex = itemIndex;
return true;
}
bool OpenVolumeDialog::HasPhysicalDriveWin9x(int unit, CString* pRemark)
{
HANDLE handle = NULL;
const int VWIN32_DIOC_DOS_INT13 = 4;
const int CARRY_FLAG = 1;
BOOL result;
typedef struct _DIOC_REGISTERS {
DWORD reg_EBX;
DWORD reg_EDX;
DWORD reg_ECX;
DWORD reg_EAX;
DWORD reg_EDI;
DWORD reg_ESI;
DWORD reg_Flags;
} DIOC_REGISTERS, *PDIOC_REGISTERS;
DIOC_REGISTERS reg = {0};
DWORD lastError, cb;
unsigned char buf[512];
if (unit > 4)
return false; // floppy drives only
handle = CreateFile(L"\\\\.\\vwin32", 0, 0, NULL,
OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL);
if (handle == INVALID_HANDLE_VALUE) {
LOGI(" Unable to open vwin32: %ld", ::GetLastError());
return false;
}
#if 0 // didn't do what I wanted
reg.reg_EAX = MAKEWORD(0, 0x00); // func 0x00 == reset controller
reg.reg_EDX = MAKEWORD(unit, 0); // specify driver
result = DeviceIoControl(handle, VWIN32_DIOC_DOS_INT13, &reg,
sizeof(reg), &reg, sizeof(reg), &cb, 0);
LOGI(" DriveReset(drive=0x%02x) result=%d carry=%d",
unit, result, reg.reg_Flags & CARRY_FLAG);
#endif
reg.reg_EAX = MAKEWORD(1, 0x02); // read 1 sector
reg.reg_EBX = (DWORD) buf;
reg.reg_ECX = MAKEWORD(1, 0); // sector 0 (+1), cylinder 0
reg.reg_EDX = MAKEWORD(unit, 0); // head
result = DeviceIoControl(handle, VWIN32_DIOC_DOS_INT13, &reg,
sizeof(reg) /*bytes*/, &reg, sizeof(reg) /*bytes*/, &cb, 0);
lastError = GetLastError();
::CloseHandle(handle);
if (result == 0 || (reg.reg_Flags & CARRY_FLAG)) {
int ah = HIBYTE(reg.reg_EAX);
LOGI(" DevIoCtrl(unit=%02xh) failed: result=%d lastErr=%d Flags=0x%08lx",
unit, result, lastError, reg.reg_Flags);
LOGI(" AH=%d (EAX=0x%08lx) byte=0x%02x", ah, reg.reg_EAX, buf[0]);
if (ah != 1) {
// failure code 1 means "invalid parameter", drive doesn't exist
// mine returns 128, "timeout", when no disk is in the drive
*pRemark = "Not ready";
return true;
} else
return false;
}
*pRemark = "Removable";
return true;
}
bool OpenVolumeDialog::HasPhysicalDriveWin2K(int unit, CString* pRemark)
{
HANDLE hDevice; // handle to the drive to be examined
DISK_GEOMETRY dg; // disk drive geometry structure
DISK_GEOMETRY_EX dge; // extended geometry request buffer
BOOL result; // results flag
DWORD junk; // discard results
LONGLONG diskSize; // size of the drive, in bytes
CString fileName;
DWORD err;
/*
* See if the drive is there.
*/
ASSERT(unit >= 128 && unit < 160); // arbitrary max
fileName.Format(L"\\\\.\\PhysicalDrive%d", unit - 128);
hDevice = ::CreateFile(fileName, // drive to open
0, // no access to the drive
FILE_SHARE_READ | FILE_SHARE_WRITE, // share mode
NULL, // default security attributes
OPEN_EXISTING, // disposition
0, // file attributes
NULL); // do not copy file attributes
if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive
return false;
/*
* Try to get the drive geometry. First try with the fancy WinXP call,
* then fall back to the Win2K call if it doesn't exist.
*/
result = ::DeviceIoControl(hDevice,
IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
NULL, 0, // input buffer
&dge, sizeof(dge), // output buffer + size in bytes
&junk, // # bytes returned
(LPOVERLAPPED) NULL); // synchronous I/O
if (result) {
diskSize = dge.DiskSize.QuadPart;
LOGI(" EX results for device %02xh", unit);
LOGI(" Disk size = %I64d (bytes) = %I64d (MB)",
diskSize, diskSize / (1024*1024));
if (diskSize > 1024*1024*1024)
pRemark->Format(L"Size is %.2fGB",
(double) diskSize / (1024.0 * 1024.0 * 1024.0));
else
pRemark->Format(L"Size is %.2fMB",
(double) diskSize / (1024.0 * 1024.0));
} else {
// Win2K shows ERROR_INVALID_FUNCTION or ERROR_NOT_SUPPORTED
LOGI("IOCTL_DISK_GET_DRIVE_GEOMETRY_EX failed, error was %ld",
GetLastError());
result = ::DeviceIoControl(hDevice, // device to be queried
IOCTL_DISK_GET_DRIVE_GEOMETRY, // operation to perform
NULL, 0, // no input buffer
&dg, sizeof(dg), // output buffer + size in bytes
&junk, // # bytes returned
(LPOVERLAPPED) NULL); // synchronous I/O
if (result) {
LOGI(" Results for device %02xh", unit);
LOGI(" Cylinders = %I64d", dg.Cylinders.QuadPart);
LOGI(" Tracks per cylinder = %ld", (ULONG) dg.TracksPerCylinder);
LOGI(" Sectors per track = %ld", (ULONG) dg.SectorsPerTrack);
LOGI(" Bytes per sector = %ld", (ULONG) dg.BytesPerSector);
diskSize = dg.Cylinders.QuadPart * (ULONG)dg.TracksPerCylinder *
(ULONG)dg.SectorsPerTrack * (ULONG)dg.BytesPerSector;
LOGI("Disk size = %I64d (bytes) = %I64d (MB)", diskSize,
diskSize / (1024 * 1024));
if (diskSize > 1024*1024*1024)
pRemark->Format(L"Size is %.2fGB",
(double) diskSize / (1024.0 * 1024.0 * 1024.0));
else
pRemark->Format(L"Size is %.2fMB",
(double) diskSize / (1024.0 * 1024.0));
} else {
err = GetLastError();
}
}
::CloseHandle(hDevice);
if (!result) {
LOGI("DeviceIoControl(IOCTL_DISK_GET_DRIVE_GEOMETRY) failed (err=%ld)",
err);
*pRemark = "Not ready";
}
return true;
}
void OpenVolumeDialog::OnListChange(NMHDR*, LRESULT* pResult)
{
CListCtrl* pListView = (CListCtrl*) GetDlgItem(IDC_VOLUME_LIST);
CButton* pButton = (CButton*) GetDlgItem(IDOK);
pButton->EnableWindow(pListView->GetSelectedCount() != 0);
//LOGI("ENABLE %d", pListView->GetSelectedCount() != 0);
*pResult = 0;
}
void OpenVolumeDialog::OnListDblClick(NMHDR* pNotifyStruct, LRESULT* pResult)
{
CListCtrl* pListView = (CListCtrl*) GetDlgItem(IDC_VOLUME_LIST);
CButton* pButton = (CButton*) GetDlgItem(IDOK);
if (pListView->GetSelectedCount() != 0) {
pButton->EnableWindow();
OnOK();
}
*pResult = 0;
}
void OpenVolumeDialog::OnVolumeFilterSelChange(void)
{
CComboBox* pCombo = (CComboBox*) GetDlgItem(IDC_VOLUME_FILTER);
ASSERT(pCombo != NULL);
LOGI("+++ SELECTION IS NOW %d", pCombo->GetCurSel());
LoadDriveList();
}
void OpenVolumeDialog::OnOK(void)
{
/*
* Figure out the (zero-based) drive letter.
*/
CListCtrl* pListView = (CListCtrl*) GetDlgItem(IDC_VOLUME_LIST);
ASSERT(pListView != NULL);
if (pListView->GetSelectedCount() != 1) {
CString msg, failed;
failed.LoadString(IDS_FAILED);
msg.LoadString(IDS_VOLUME_SELECT_ONE);
MessageBox(msg, failed, MB_OK);
return;
}
POSITION posn;
posn = pListView->GetFirstSelectedItemPosition();
if (posn == NULL) {
ASSERT(false);
return;
}
int num = pListView->GetNextSelectedItem(posn);
DWORD driveID = pListView->GetItemData(num);
UINT formatID = 0;
if (HIBYTE(HIWORD(driveID)) == 0xaa) {
// TODO: remove this?
fChosenDrive.Format(L"%hs%d:%d:%d\\",
DiskImgLib::kASPIDev,
LOBYTE(HIWORD(driveID)),
HIBYTE(LOWORD(driveID)),
LOBYTE(LOWORD(driveID)));
//ForceReadOnly(true);
} else if (driveID >= 'A' && driveID <= 'Z') {
/*
* Do we want to let them do this? We show some logical drives
* that we don't want them to actually use.
*/
switch (fVolumeInfo[driveID-'A'].driveType) {
case DRIVE_REMOVABLE:
case DRIVE_FIXED:
break; // allow
case DRIVE_CDROM: //formatID = IDS_VOLUME_NO_CDROM;
ForceReadOnly(true);
break;
case DRIVE_REMOTE: formatID = IDS_VOLUME_NO_REMOTE;
break;
case DRIVE_RAMDISK: formatID = IDS_VOLUME_NO_RAMDISK;
break;
case DRIVE_UNKNOWN:
case DRIVE_NO_ROOT_DIR:
default: formatID = IDS_VOLUME_NO_GENERIC;
break;
}
fChosenDrive.Format(L"%c:\\", driveID);
} else if ((driveID >= 0 && driveID < 4) ||
(driveID >= 0x80 && driveID < 0x88))
{
fChosenDrive.Format(L"%02x:\\", driveID);
} else {
ASSERT(false);
return;
}
if (formatID != 0) {
CString msg, notAllowed;
notAllowed.LoadString(IDS_NOT_ALLOWED);
msg.LoadString(formatID);
MessageBox(msg, notAllowed, MB_OK);
} else {
Preferences* pPreferences = GET_PREFERENCES_WR();
CComboBox* pCombo = (CComboBox*) GetDlgItem(IDC_VOLUME_FILTER);
pPreferences->SetPrefLong(kPrVolumeFilter, pCombo->GetCurSel());
LOGI("SETTING PREF TO %ld", pCombo->GetCurSel());
CDialog::OnOK();
}
}
void OpenVolumeDialog::OnHelp(void)
{
WinHelp(HELP_TOPIC_OPEN_VOLUME, HELP_CONTEXT);
}
void OpenVolumeDialog::ForceReadOnly(bool readOnly) const
{
CButton* pButton = (CButton*) GetDlgItem(IDC_OPENVOL_READONLY);
ASSERT(pButton != NULL);
if (readOnly)
pButton->SetCheck(BST_CHECKED);
else
pButton->SetCheck(BST_UNCHECKED);
LOGW("FORCED READ ONLY %d", readOnly);
}