1289 lines
37 KiB
C
1289 lines
37 KiB
C
/*++
|
|
|
|
Routine Description:
|
|
|
|
contains functions to format a floppy disk
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
--*/
|
|
#include <windows.h> // required for all Windows applications
|
|
#include <tchar.h> // unicode definitions
|
|
|
|
#include "otnboot.h" // application definitions
|
|
#include "otnbtdlg.h" // dialog box constants
|
|
#include "fmifs.h" // file manager IFS DLL functions
|
|
|
|
#ifdef JAPAN
|
|
// fixed kkntbug #11940
|
|
// Network client administrator can not make install disks on PC with C drive as FD
|
|
|
|
#define OEM_ID_PC98 0X0D00
|
|
|
|
//
|
|
// Japanese specific floppy format styles
|
|
//
|
|
#define F3_12 1 // 3.5" drive that supports 1.2M-byte format
|
|
#define F3_123 2 // 3.5" drive that supports 1.23M-byte format
|
|
#define F5_123 4 // 5.25" drive that supports 1.23M-byte format
|
|
#endif
|
|
|
|
//
|
|
// local windows messages
|
|
//
|
|
#define FS_CANCELUPDATE (WM_USER+104)
|
|
|
|
//
|
|
// Disk format information structure
|
|
//
|
|
#define FF_ONLYONE 0x1000
|
|
#define FF_RETRY 0x4000
|
|
|
|
typedef struct _CANCEL_INFO {
|
|
HWND hCancelDlg;
|
|
BOOL bCancel;
|
|
HANDLE hThread;
|
|
BOOL fmifsSuccess;
|
|
UINT dReason;
|
|
UINT fuStyle; // Message box style
|
|
INT nPercentDrawn; // percent drawn so FAR
|
|
enum _CANCEL_TYPE {
|
|
CANCEL_NULL=0,
|
|
CANCEL_FORMAT,
|
|
CANCEL_COPY,
|
|
CANCEL_BACKUP,
|
|
CANCEL_RESTORE,
|
|
CANCEL_COMPRESS,
|
|
CANCEL_UNCOMPRESS
|
|
} eCancelType;
|
|
BOOL bModal;
|
|
struct _INFO {
|
|
struct _FORMAT {
|
|
INT iFormatDrive;
|
|
FMIFS_MEDIA_TYPE fmMediaType;
|
|
BOOL fQuick;
|
|
DWORD fFlags; // FF_ONLYONE = 0x1000
|
|
TCHAR szLabel[MAXLABELLEN+1];
|
|
} Format;
|
|
struct _COPY {
|
|
INT iSourceDrive;
|
|
INT iDestDrive;
|
|
BOOL bFormatDest;
|
|
} Copy;
|
|
} Info;
|
|
} CANCEL_INFO, *PCANCEL_INFO;
|
|
|
|
static HANDLE hfmifsDll = NULL; // dll w/ file system utils
|
|
static TCHAR szFmifsDll[] = {TEXT("fmifs.dll")}; // dll for FAT file system
|
|
static TCHAR SZ_PERCENTFORMAT[] = {TEXT("%3d%%")};
|
|
//
|
|
// pointers to DLL functions;
|
|
//
|
|
static PFMIFS_FORMAT_ROUTINE lpfnFormat = NULL;
|
|
static PFMIFS_DISKCOPY_ROUTINE lpfnDiskCopy = NULL;
|
|
static PFMIFS_SETLABEL_ROUTINE lpfnSetLabel = NULL;
|
|
static PFMIFS_QSUPMEDIA_ROUTINE lpfnQuerySupportedMedia = NULL;
|
|
//
|
|
// Format dialog box information
|
|
//
|
|
static CANCEL_INFO CancelInfo;
|
|
static BOOL bDataInitialized = FALSE;
|
|
|
|
ULONG ulSpaceAvail = 0;
|
|
ULONG ulTotalSpace = 0;
|
|
|
|
static
|
|
LONG
|
|
GetFloppyDiskSize (
|
|
IN TCHAR cDrive
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Examines the specified floppy drive to determine if it
|
|
supports 1.2M or 1.44M formats.
|
|
|
|
Arguments:
|
|
|
|
IN TCHAR cDrive
|
|
drive letter of floppy drive to examine
|
|
|
|
Return Value:
|
|
|
|
0 if unable to read drive or drive does not support a HD format
|
|
3 if the drive is a 3.5" drive that supports 1.44M-byte format
|
|
5 if the drive is a 5.25" drive that supports the 1.2M-byte format
|
|
// Japanese specific floppy format styles
|
|
F3_12 if the drive is a 3.5" drive that supports 1.2M-byte format
|
|
F3_123 if the drive is a 3.5" drive that supports 1.23M-byte format
|
|
F5_123 if the drive is a 5.25" drive that supports 1.23M-byte format
|
|
|
|
--*/
|
|
{
|
|
#define DG_ELEMS 16 // accomodate up to 16 different formats
|
|
TCHAR szDrivePath[8]; // local drive spec. string
|
|
HANDLE hDrive; // handle to drive device driver
|
|
DWORD dwError; // local error code
|
|
DWORD dwBytesReturned; // size of buffer returned
|
|
LONG lSize; // disk size returned to calling fn.
|
|
DWORD dwCount; // number of DISK_GEOMETRY structures in buffer
|
|
DWORD dwEntry; // structure being evaluated
|
|
DISK_GEOMETRY dgArray[DG_ELEMS]; // buffer to put data in
|
|
|
|
#ifndef JAPAN
|
|
// fixed kkntbug #11940
|
|
// Network client administrator can not make install disks on PC with C drive as FD
|
|
|
|
// check input argument
|
|
switch (cDrive){
|
|
case TEXT('A'):
|
|
case TEXT('a'):
|
|
case TEXT('B'):
|
|
case TEXT('b'):
|
|
break;
|
|
|
|
default:
|
|
SetLastError (ERROR_INVALID_PARAMETER);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
// make NT device path for drive
|
|
szDrivePath[0] = cBackslash;
|
|
szDrivePath[1] = cBackslash;
|
|
szDrivePath[2] = cPeriod;
|
|
szDrivePath[3] = cBackslash;
|
|
szDrivePath[4] = cDrive;
|
|
szDrivePath[5] = cColon;
|
|
szDrivePath[6] = 0;
|
|
szDrivePath[7] = 0;
|
|
|
|
// open drive
|
|
hDrive = CreateFile (
|
|
szDrivePath, // drive to open
|
|
0, // just talk to the driver, not the drive
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE, // allow sharing
|
|
NULL, // default security
|
|
OPEN_EXISTING, // open existing device
|
|
FILE_ATTRIBUTE_NORMAL, // this is ignored
|
|
NULL); // no template
|
|
|
|
if (hDrive == INVALID_HANDLE_VALUE) {
|
|
// unable to open drive so return error
|
|
dwError = GetLastError ();
|
|
return 0;
|
|
}
|
|
|
|
// get device information
|
|
if (DeviceIoControl (hDrive,
|
|
IOCTL_DISK_GET_MEDIA_TYPES,
|
|
NULL, 0, // no input buffer
|
|
&dgArray[0], sizeof(DISK_GEOMETRY)*DG_ELEMS,// output buffer info
|
|
&dwBytesReturned, NULL)) { // return information
|
|
// see if at least one entry was returned
|
|
if (dwBytesReturned >= sizeof(DISK_GEOMETRY)) {
|
|
dwCount = dwBytesReturned / sizeof(DISK_GEOMETRY);
|
|
// go through array to see if there's a desired entry
|
|
// i.e. a HD format in the list of supported formats
|
|
for (lSize = 0, dwEntry = 0;
|
|
(dwEntry < dwCount) && (lSize == 0);
|
|
dwEntry++) {
|
|
switch (dgArray[dwEntry].MediaType) {
|
|
// only return a size if a supported
|
|
// format is allowed by this drive
|
|
case F5_1Pt2_512:
|
|
lSize = 5;
|
|
break;
|
|
|
|
case F3_1Pt44_512:
|
|
lSize = 3;
|
|
break;
|
|
#ifdef JAPAN
|
|
// fixed kkntbug #11940
|
|
// Network client administrator can not make install disks on PC with C drive as FD
|
|
case F5_1Pt23_1024:
|
|
lSize = F5_123;
|
|
break;
|
|
|
|
case F3_1Pt2_512:
|
|
lSize = F3_12;
|
|
break;
|
|
|
|
case F3_1Pt23_1024:
|
|
lSize = F3_123;
|
|
break;
|
|
#endif
|
|
|
|
case F5_360_512:
|
|
case F5_320_512:
|
|
case F5_320_1024:
|
|
case F5_180_512:
|
|
case F5_160_512:
|
|
case F3_2Pt88_512:
|
|
case F3_20Pt8_512:
|
|
case F3_720_512:
|
|
default:
|
|
lSize = 0;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
// no data returned so return error
|
|
dwError = GetLastError ();
|
|
SetLastError (ERROR_NO_DATA);
|
|
lSize = 0;
|
|
}
|
|
} else {
|
|
// unable to read device driver info
|
|
dwError = GetLastError ();
|
|
lSize = 0;
|
|
}
|
|
// close handle and return data found.
|
|
CloseHandle (hDrive);
|
|
|
|
return lSize;
|
|
}
|
|
|
|
static
|
|
BOOL
|
|
DriveLoaded (
|
|
IN TCHAR cDrive,
|
|
IN BOOL bCheckFormat
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
formats call to MediaPresent function for use with just a drive letter
|
|
|
|
Arguments:
|
|
|
|
IN TCHAR cDrive
|
|
drive letter to detect.
|
|
|
|
Return Value:
|
|
|
|
TRUE if a disk (formatted or unformatted) is detected.
|
|
FALSE if disk is not present in drive
|
|
|
|
--*/
|
|
{
|
|
TCHAR szPath[4];
|
|
szPath[0] = cDrive;
|
|
szPath[1] = cColon;
|
|
szPath[2] = cBackslash;
|
|
szPath[3] = 0;
|
|
|
|
return MediaPresent (szPath, bCheckFormat);
|
|
}
|
|
|
|
static
|
|
VOID
|
|
CancelDlgQuit(
|
|
VOID
|
|
)
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Name: CancelDlgQuit
|
|
//
|
|
// Synopsis: Quits the cancel modeless dialog (status for diskcopy/format)
|
|
//
|
|
// IN: VOID
|
|
//
|
|
// Return: VOID
|
|
//
|
|
// Assumes: Called from worker thread only; CancelInfo.hThread valid
|
|
//
|
|
// Effects: Kills calling thread
|
|
//
|
|
//
|
|
// Notes:
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
{
|
|
//
|
|
// Close thread if successful
|
|
//
|
|
|
|
if (CancelInfo.hThread) {
|
|
CloseHandle(CancelInfo.hThread);
|
|
CancelInfo.hThread = NULL;
|
|
}
|
|
|
|
//
|
|
// At this point, when we call FS_CANCELEND,
|
|
// the other thread thinks that this one has died since
|
|
// CancelInfo.hThread is NULL.
|
|
// This is exactly what we want, since we will very shortly
|
|
// exit after the SendMessage.
|
|
//
|
|
EndDialog (CancelInfo.hCancelDlg, IDOK);
|
|
|
|
ExitThread(0L);
|
|
}
|
|
|
|
static
|
|
BOOL
|
|
Callback_Function(
|
|
IN FMIFS_PACKET_TYPE PacketType,
|
|
IN DWORD PacketLength,
|
|
IN PVOID PacketData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Callback function used by IFS dll (stolen from Winfile code)
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
--*/
|
|
{
|
|
TCHAR szTemp[128];
|
|
|
|
// Quit if told to do so..
|
|
|
|
if (CancelInfo.bCancel)
|
|
return FALSE;
|
|
|
|
switch (PacketType) {
|
|
case FmIfsPercentCompleted:
|
|
//
|
|
// If we are copying and we just finished a destination format,
|
|
// then set the window text back to the original message
|
|
//
|
|
if (CANCEL_COPY == CancelInfo.eCancelType &&
|
|
CancelInfo.Info.Copy.bFormatDest) {
|
|
|
|
CancelInfo.Info.Copy.bFormatDest = FALSE;
|
|
lstrcpy (szTemp,
|
|
GetStringResource (CancelInfo.Info.Copy.bFormatDest ?
|
|
IDS_FORMATTINGDEST : IDS_COPYINGDISKTITLE));
|
|
|
|
SetWindowText(CancelInfo.hCancelDlg, szTemp);
|
|
}
|
|
CancelInfo.nPercentDrawn = ((PFMIFS_PERCENT_COMPLETE_INFORMATION)PacketData)->PercentCompleted;
|
|
PostMessage(CancelInfo.hCancelDlg, FS_CANCELUPDATE, ((PFMIFS_PERCENT_COMPLETE_INFORMATION)PacketData)->PercentCompleted, 0L);
|
|
|
|
break;
|
|
|
|
case FmIfsFormatReport:
|
|
ulTotalSpace = ((PFMIFS_FORMAT_REPORT_INFORMATION)PacketData)->KiloBytesTotalDiskSpace * 1024L;
|
|
ulSpaceAvail = ((PFMIFS_FORMAT_REPORT_INFORMATION)PacketData)->KiloBytesAvailable * 1024L;
|
|
break;
|
|
|
|
case FmIfsInsertDisk:
|
|
switch(((PFMIFS_INSERT_DISK_INFORMATION)PacketData)->DiskType) {
|
|
case DISK_TYPE_GENERIC:
|
|
CancelInfo.fuStyle = MB_OK_TASK_INFO;
|
|
return DisplayMessageBox(CancelInfo.hCancelDlg,
|
|
IDS_INSERTSRC, IDS_COPYDISK, CancelInfo.fuStyle);
|
|
|
|
case DISK_TYPE_SOURCE:
|
|
CancelInfo.fuStyle = MB_OK_TASK_INFO;
|
|
return DisplayMessageBox(CancelInfo.hCancelDlg,
|
|
IDS_INSERTSRC, IDS_COPYDISK, CancelInfo.fuStyle);
|
|
|
|
case DISK_TYPE_TARGET:
|
|
CancelInfo.fuStyle = MB_OK_TASK_INFO;
|
|
return DisplayMessageBox(CancelInfo.hCancelDlg,
|
|
IDS_INSERTDEST, IDS_COPYDISK, CancelInfo.fuStyle);
|
|
|
|
case DISK_TYPE_SOURCE_AND_TARGET:
|
|
CancelInfo.fuStyle = MB_OK_TASK_INFO;
|
|
return DisplayMessageBox(CancelInfo.hCancelDlg,
|
|
IDS_INSERTSRCDEST, IDS_COPYDISK, CancelInfo.fuStyle);
|
|
}
|
|
break;
|
|
|
|
case FmIfsIncompatibleFileSystem:
|
|
CancelInfo.dReason = IDS_FFERR_INCFS;
|
|
break;
|
|
|
|
case FmIfsFormattingDestination:
|
|
CancelInfo.Info.Copy.bFormatDest = TRUE;
|
|
lstrcpy (szTemp, GetStringResource (
|
|
(CancelInfo.Info.Copy.bFormatDest ?
|
|
IDS_FORMATTINGDEST : IDS_COPYINGDISKTITLE)));
|
|
SetWindowText(CancelInfo.hCancelDlg, szTemp);
|
|
CancelInfo.nPercentDrawn = ((PFMIFS_PERCENT_COMPLETE_INFORMATION)PacketData)->PercentCompleted;
|
|
PostMessage(CancelInfo.hCancelDlg, FS_CANCELUPDATE, ((PFMIFS_PERCENT_COMPLETE_INFORMATION)PacketData)->PercentCompleted, 0L);
|
|
break;
|
|
|
|
case FmIfsIncompatibleMedia:
|
|
CancelInfo.fuStyle = MB_ICONHAND | MB_OK;
|
|
return DisplayMessageBox(CancelInfo.hCancelDlg,
|
|
IDS_COPYSRCDESTINCOMPAT, IDS_COPYDISK, CancelInfo.fuStyle);
|
|
|
|
case FmIfsAccessDenied:
|
|
CancelInfo.dReason = IDS_FFERR_ACCESSDENIED;
|
|
break;
|
|
|
|
case FmIfsMediaWriteProtected:
|
|
CancelInfo.dReason = IDS_FFERR_DISKWP;
|
|
break;
|
|
|
|
case FmIfsCantLock:
|
|
CancelInfo.dReason = IDS_FFERR_CANTLOCK;
|
|
break;
|
|
|
|
case FmIfsBadLabel:
|
|
CancelInfo.fuStyle = MB_OK_TASK_EXCL;
|
|
return DisplayMessageBox(CancelInfo.hCancelDlg,
|
|
IDS_FFERR_BADLABEL, IDS_COPYERROR + FUNC_LABEL, CancelInfo.fuStyle);
|
|
|
|
case FmIfsCantQuickFormat:
|
|
// Can't quick format, ask if user wants to regular format:
|
|
CancelInfo.fuStyle = MB_ICONEXCLAMATION | MB_YESNO;
|
|
|
|
if (IDYES == DisplayMessageBox(
|
|
CancelInfo.hCancelDlg,
|
|
IDS_FORMATQUICKFAILURE,
|
|
IDS_FORMATERR,
|
|
CancelInfo.fuStyle)) {
|
|
|
|
CancelInfo.Info.Format.fQuick = FALSE;
|
|
CancelInfo.Info.Format.fFlags |= FF_RETRY;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Just fake a cancel
|
|
//
|
|
CancelInfo.fmifsSuccess = FALSE;
|
|
CancelInfo.bCancel = TRUE;
|
|
}
|
|
|
|
break;
|
|
|
|
case FmIfsIoError:
|
|
switch(((PFMIFS_IO_ERROR_INFORMATION)PacketData)->DiskType) {
|
|
case DISK_TYPE_GENERIC:
|
|
CancelInfo.dReason = IDS_FFERR_GENIOERR;
|
|
break;
|
|
|
|
case DISK_TYPE_SOURCE:
|
|
CancelInfo.dReason = IDS_FFERR_SRCIOERR;
|
|
break;
|
|
|
|
case DISK_TYPE_TARGET:
|
|
CancelInfo.dReason = IDS_FFERR_DSTIOERR;
|
|
break;
|
|
|
|
case DISK_TYPE_SOURCE_AND_TARGET:
|
|
CancelInfo.dReason = IDS_FFERR_SRCDSTIOERR;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case FmIfsFinished:
|
|
CancelInfo.fmifsSuccess = ((PFMIFS_FINISHED_INFORMATION)PacketData)->Success;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
VOID
|
|
FormatDrive(
|
|
IN PVOID ThreadParameter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Thread routine to format the floppy diskette as described in the
|
|
CancelInfo data structure.
|
|
|
|
Arguments:
|
|
|
|
Not used
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
WCHAR wszDrive[3];
|
|
WCHAR wszFileSystem[4] = L"FAT";
|
|
|
|
wszDrive[0] = (WCHAR)(CancelInfo.Info.Format.iFormatDrive + cA);
|
|
wszDrive[1] = cColon;
|
|
wszDrive[2] = 0;
|
|
|
|
#define wszLabel CancelInfo.Info.Format.szLabel
|
|
|
|
do {
|
|
CancelInfo.Info.Format.fFlags &= ~FF_RETRY;
|
|
|
|
(*lpfnFormat)(wszDrive,
|
|
CancelInfo.Info.Format.fmMediaType,
|
|
wszFileSystem,
|
|
wszLabel,
|
|
(BOOLEAN)CancelInfo.Info.Format.fQuick,
|
|
(FMIFS_CALLBACK)&Callback_Function);
|
|
} while (CancelInfo.Info.Format.fFlags & FF_RETRY);
|
|
|
|
CancelDlgQuit();
|
|
}
|
|
|
|
static
|
|
FMIFS_MEDIA_TYPE
|
|
GetDriveTypeFromDriveLetter (
|
|
IN TCHAR cDrive
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
returns the drive type of the drive specified in the path argument
|
|
|
|
Arguments:
|
|
|
|
IN LPCTSTR szPath
|
|
path on drive to examine
|
|
|
|
Return Value:
|
|
|
|
MEDIA_TYPE value identifying drive type in format compatible with
|
|
IFS DLL
|
|
|
|
--*/
|
|
{
|
|
HANDLE hFloppy;
|
|
DWORD dwRetSize;
|
|
DISK_GEOMETRY dgFloppy;
|
|
TCHAR szDevicePath[16];
|
|
UINT nDriveType;
|
|
UINT nErrorMode;
|
|
|
|
// make device name from drive letter
|
|
|
|
szDevicePath[0] = cBackslash;
|
|
szDevicePath[1] = cBackslash;
|
|
szDevicePath[2] = cPeriod;
|
|
szDevicePath[3] = cBackslash;
|
|
szDevicePath[4] = cDrive;
|
|
szDevicePath[5] = cColon; // colon
|
|
szDevicePath[6] = cBackslash; // null terminator
|
|
szDevicePath[7] = 0; // null terminator
|
|
|
|
nDriveType = GetDriveType((LPTSTR)&szDevicePath[4]);
|
|
// see if this is a remote disk and exit if it is.
|
|
if (nDriveType == DRIVE_REMOTE) return FmMediaUnknown;
|
|
|
|
if ((nDriveType == DRIVE_REMOVABLE) || (nDriveType == DRIVE_CDROM)) {
|
|
// make device path into an NT device path
|
|
szDevicePath[6] = 0; // null terminator
|
|
|
|
// disable windows error message popup
|
|
nErrorMode = SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
|
|
|
|
// open device to get type
|
|
hFloppy = CreateFile (
|
|
szDevicePath,
|
|
GENERIC_READ,
|
|
(FILE_SHARE_READ | FILE_SHARE_WRITE),
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if (hFloppy != INVALID_HANDLE_VALUE) {
|
|
// get drive information
|
|
if (!DeviceIoControl (hFloppy,
|
|
IOCTL_DISK_GET_DRIVE_GEOMETRY,
|
|
NULL, 0,
|
|
&dgFloppy,
|
|
sizeof(DISK_GEOMETRY),
|
|
&dwRetSize,
|
|
NULL) ){
|
|
// unable to get data so set to unknown
|
|
dgFloppy.MediaType = Unknown;
|
|
} // else return data from returned structure
|
|
CloseHandle (hFloppy);
|
|
} else {
|
|
// unable to open handle to device
|
|
dgFloppy.MediaType = Unknown;
|
|
}
|
|
SetErrorMode (nErrorMode); // reset error mode
|
|
}
|
|
// translate from MEDIA_TYPE to FMIFS_MEDIA_TYPE here
|
|
|
|
switch (dgFloppy.MediaType) {
|
|
#ifdef JAPAN
|
|
// fixed kkntbug #11940
|
|
// Network client administrator can not make install disks on PC with C drive as FD
|
|
|
|
case (F3_1Pt2_512): return FmMediaF3_1Pt2_512;
|
|
case (F3_1Pt23_1024): return FmMediaF3_1Pt23_1024;
|
|
case (F5_1Pt23_1024): return FmMediaF5_1Pt23_1024;
|
|
#endif
|
|
case (F5_1Pt2_512): return FmMediaF5_1Pt2_512;
|
|
case (F3_1Pt44_512): return FmMediaF3_1Pt44_512;
|
|
case (F3_2Pt88_512): return FmMediaF3_2Pt88_512;
|
|
case (F3_20Pt8_512): return FmMediaF3_20Pt8_512;
|
|
case (F3_720_512): return FmMediaF3_720_512;
|
|
case (F5_360_512): return FmMediaF5_360_512;
|
|
case (F5_320_512): return FmMediaF5_320_512;
|
|
case (F5_320_1024): return FmMediaF5_320_1024;
|
|
case (F5_180_512): return FmMediaF5_180_512;
|
|
case (F5_160_512): return FmMediaF5_160_512;
|
|
case (FixedMedia): return FmMediaFixed;
|
|
case (Unknown): return FmMediaUnknown;
|
|
default: return FmMediaUnknown;
|
|
}
|
|
}
|
|
|
|
static
|
|
BOOL
|
|
FmifsLoaded(
|
|
IN HWND hWnd
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
loads (if not already loaded) the File Manager IFS dll and initializes
|
|
the pointers to it's functions
|
|
|
|
Arguments:
|
|
|
|
IN HWND hWnd
|
|
window handle of parent, used for MessageBox calls
|
|
|
|
Return Value:
|
|
|
|
TRUE if file loaded
|
|
FALSE if not
|
|
|
|
--*/
|
|
{
|
|
// Load the fmifs dll.
|
|
|
|
if (hfmifsDll < (HANDLE)32) {
|
|
hfmifsDll = LoadLibrary(szFmifsDll);
|
|
if (hfmifsDll < (HANDLE)32) {
|
|
/* FMIFS not available. */
|
|
DisplayMessageBox(hWnd,
|
|
IDS_APP_NAME,
|
|
IDS_FMIFSLOADERR,
|
|
MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL);
|
|
hfmifsDll = NULL;
|
|
return FALSE;
|
|
}
|
|
else {
|
|
lpfnFormat = (PVOID)GetProcAddress(hfmifsDll, "Format");
|
|
lpfnQuerySupportedMedia = (PVOID)GetProcAddress(hfmifsDll, "QuerySupportedMedia");
|
|
lpfnSetLabel = (PVOID)GetProcAddress(hfmifsDll, "SetLabel");
|
|
lpfnDiskCopy = (PVOID)GetProcAddress(hfmifsDll, "DiskCopy");
|
|
if (!lpfnFormat || !lpfnQuerySupportedMedia ||
|
|
!lpfnSetLabel || !lpfnDiskCopy) {
|
|
DisplayMessageBox(hWnd,
|
|
IDS_APP_NAME,
|
|
IDS_FMIFSLOADERR,
|
|
MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL);
|
|
FreeLibrary(hfmifsDll);
|
|
hfmifsDll = NULL;
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
VOID
|
|
DestroyCancelWindow(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destroys the CANCEL (i.e. Format) window.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
if (!CancelInfo.hCancelDlg)
|
|
return;
|
|
|
|
if (CancelInfo.bModal) {
|
|
EndDialog(CancelInfo.hCancelDlg,0);
|
|
} else {
|
|
DestroyWindow(CancelInfo.hCancelDlg);
|
|
}
|
|
CancelInfo.hCancelDlg = NULL;
|
|
}
|
|
|
|
static
|
|
INT_PTR
|
|
CancelDlgProc(HWND hDlg,
|
|
IN UINT message,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam
|
|
)
|
|
/*------------------------- CancelDlgProc
|
|
*
|
|
* DESCRIPTION:
|
|
* dialog procedure for the modeless dialog. two main purposes
|
|
* here:
|
|
*
|
|
* 1. if the user chooses CANCEL we set bCancel to TRUE
|
|
* which will end the PeekMessage background processing loop
|
|
*
|
|
* 2. handle the private FS_CANCELUPDATE message and draw
|
|
* a "gas gauge" indication of how FAR the background job
|
|
* has progressed
|
|
*
|
|
* ARGUMENTS:
|
|
* stock dialog proc arguments
|
|
*
|
|
* RETURN VALUE:
|
|
* stock dialog proc return value - BOOL
|
|
*
|
|
* GLOBALS READ:
|
|
* none
|
|
*
|
|
* GLOBALS WRITTEN:
|
|
* CancelInfo structure
|
|
*
|
|
* MESSAGES:
|
|
* WM_COMMAND - handle IDCANCEL by setting bCancel to TRUE
|
|
* and calling DestroyWindow to end the dialog
|
|
*
|
|
* WM_INITDIALOG - set control text, get coordinates of gas gauge,
|
|
* disable main window so we look modal
|
|
*
|
|
* WM_PAINT - draw the "gas gauge" control
|
|
*
|
|
* FS_CANCELUPDATE - the percentage done has changed, so update
|
|
* nPercentDrawn and force a repaint
|
|
*
|
|
* NOTES:
|
|
*
|
|
* The bCancel global variable is used to communicate
|
|
* with the main window. If the user chooses to cancel
|
|
* we set bCancel to TRUE.
|
|
*
|
|
* When we get the private message FS_CANCELUPDATE
|
|
* we update the "gas gauge" control that indicates
|
|
* what percentage of the rectangles have been drawn
|
|
* so FAR. This shows that we can draw in the dialog
|
|
* as the looping operation progresses. (FS_CANCELUPDATE is sent
|
|
* first to hwndFrame, which sets %completed then sends message to us.)
|
|
*
|
|
*/
|
|
{
|
|
static RECT rectGG; // GasGauge rectangle
|
|
DWORD Ignore;
|
|
TCHAR szTemp[128];
|
|
static BOOL bLastQuick;
|
|
|
|
static HFONT hFont = NULL;
|
|
|
|
switch (message) {
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam)) {
|
|
case IDCANCEL:
|
|
DestroyCancelWindow();
|
|
if (hFont != NULL) {
|
|
DeleteObject(hFont);
|
|
hFont = NULL;
|
|
}
|
|
CancelInfo.bCancel = TRUE;
|
|
return TRUE;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
case WM_INITDIALOG:
|
|
{
|
|
CancelInfo.hCancelDlg = hDlg;
|
|
bLastQuick = TRUE;
|
|
|
|
switch(CancelInfo.eCancelType) {
|
|
case CANCEL_FORMAT:
|
|
|
|
//
|
|
// Formatting disk requires that we release any notification
|
|
// requests on this drive.
|
|
//
|
|
// NotifyPause(CancelInfo.Info.Format.iFormatDrive, DRIVE_REMOVABLE);
|
|
|
|
break;
|
|
case CANCEL_COPY:
|
|
|
|
//
|
|
// Pause notifications on dest drive.
|
|
//
|
|
// NotifyPause(CancelInfo.Info.Copy.iDestDrive, DRIVE_REMOVABLE);
|
|
lstrcpy (szTemp, GetStringResource(
|
|
CancelInfo.Info.Copy.bFormatDest ?
|
|
IDS_FORMATTINGDEST : IDS_COPYINGDISKTITLE));
|
|
SetWindowText(hDlg, szTemp);
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!CancelInfo.hThread) {
|
|
switch (CancelInfo.eCancelType) {
|
|
case CANCEL_FORMAT:
|
|
CancelInfo.hThread = CreateThread( NULL, // Security
|
|
0L, // Stack Size
|
|
(LPTHREAD_START_ROUTINE)FormatDrive,
|
|
NULL,
|
|
0L,
|
|
&Ignore );
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// a-jagram: bug fix 305171
|
|
|
|
// Create the font
|
|
if (hFont == NULL) { // it should be
|
|
LOGFONT lf;
|
|
HGDIOBJ hGObj;
|
|
|
|
hGObj = GetStockObject(SYSTEM_FONT);
|
|
|
|
if( hGObj != NULL ) {
|
|
if (GetObject(hGObj, sizeof(lf), (LPVOID) &lf)) {
|
|
lstrcpy(lf.lfFaceName, TEXT("MS Shell Dlg"));
|
|
hFont = CreateFontIndirect(&lf);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get the coordinates of the gas gauge static control rectangle,
|
|
// and convert them to dialog client area coordinates
|
|
GetClientRect(GetDlgItem(hDlg, IDD_GASGAUGE), &rectGG);
|
|
ClientToScreen(GetDlgItem(hDlg, IDD_GASGAUGE), (LPPOINT)&rectGG.left);
|
|
ClientToScreen(GetDlgItem(hDlg, IDD_GASGAUGE), (LPPOINT)&rectGG.right);
|
|
ScreenToClient(hDlg, (LPPOINT)&rectGG.left);
|
|
ScreenToClient(hDlg, (LPPOINT)&rectGG.right);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_PAINT:
|
|
{
|
|
HDC hDC;
|
|
PAINTSTRUCT ps;
|
|
TCHAR buffer[48];
|
|
SIZE size;
|
|
INT xText, yText;
|
|
INT nDivideRects;
|
|
RECT rectDone, rectLeftToDo;
|
|
HGDIOBJ hOldFont = NULL;
|
|
|
|
// The gas gauge is drawn by drawing a text string stating
|
|
// what percentage of the job is done into the middle of
|
|
// the gas gauge rectangle, and by separating that rectangle
|
|
// into two parts: rectDone (the left part, filled in blue)
|
|
// and rectLeftToDo(the right part, filled in white).
|
|
// nDivideRects is the x coordinate that divides these two rects.
|
|
//
|
|
// The text in the blue rectangle is drawn white, and vice versa
|
|
// This is easy to do with ExtTextOut()!
|
|
|
|
hDC = BeginPaint(hDlg, &ps);
|
|
|
|
if (hFont) {
|
|
hOldFont = SelectObject(hDC, hFont);
|
|
}
|
|
|
|
//
|
|
// If formatting quick, set this display
|
|
//
|
|
if (CancelInfo.Info.Format.fQuick &&
|
|
CANCEL_FORMAT == CancelInfo.eCancelType) {
|
|
lstrcpy (buffer, GetStringResource (IDS_QUICKFORMATTINGTITLE));
|
|
SendDlgItemMessage(hDlg, IDD_TEXT, WM_SETTEXT, 0, (LPARAM)cszEmptyString);
|
|
|
|
bLastQuick = TRUE;
|
|
|
|
} else {
|
|
|
|
if (bLastQuick) {
|
|
lstrcpy (buffer, GetStringResource (IDS_PERCENTCOMPLETE));
|
|
SendDlgItemMessage(hDlg, IDD_TEXT, WM_SETTEXT, 0, (LPARAM)buffer);
|
|
|
|
bLastQuick = FALSE;
|
|
}
|
|
|
|
wsprintf(buffer, SZ_PERCENTFORMAT, CancelInfo.nPercentDrawn);
|
|
}
|
|
|
|
GetTextExtentPoint32(hDC, buffer, lstrlen(buffer), &size);
|
|
xText = rectGG.left
|
|
+ ((rectGG.right - rectGG.left) - size.cx) / 2;
|
|
yText = rectGG.top
|
|
+ ((rectGG.bottom - rectGG.top) - size.cy) / 2;
|
|
|
|
nDivideRects = ((rectGG.right - rectGG.left) * CancelInfo.nPercentDrawn) / 100;
|
|
|
|
// Paint in the "done so FAR" rectangle of the gas
|
|
// gauge with blue background and white text
|
|
SetRect(&rectDone, rectGG.left, rectGG.top,
|
|
rectGG.left + nDivideRects, rectGG.bottom);
|
|
SetTextColor(hDC, RGB(255, 255, 255));
|
|
SetBkColor(hDC, RGB(0, 0, 255));
|
|
|
|
ExtTextOut(hDC, xText, yText, ETO_CLIPPED | ETO_OPAQUE,
|
|
&rectDone, buffer, lstrlen(buffer), NULL);
|
|
|
|
// Paint in the "still left to do" rectangle of the gas
|
|
// gauge with white background and blue text
|
|
SetRect(&rectLeftToDo, rectGG.left+nDivideRects, rectGG.top,
|
|
rectGG.right, rectGG.bottom);
|
|
SetTextColor(hDC, RGB(0, 0, 255));
|
|
SetBkColor(hDC, RGB(255, 255, 255));
|
|
|
|
ExtTextOut(hDC, xText, yText, ETO_CLIPPED | ETO_OPAQUE,
|
|
&rectLeftToDo, buffer, lstrlen(buffer), NULL);
|
|
|
|
if (hOldFont) {
|
|
SelectObject(hDC, hOldFont);
|
|
}
|
|
|
|
EndPaint(hDlg, &ps);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
case FS_CANCELUPDATE:
|
|
InvalidateRect(hDlg, &rectGG, TRUE);
|
|
UpdateWindow(hDlg);
|
|
return TRUE;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static
|
|
BOOL
|
|
InitUserData (
|
|
IN HWND hWnd
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
initializes the CancelInfo data structure used to format the disk
|
|
|
|
Arguments:
|
|
|
|
Window handle of calling function
|
|
|
|
Return Value:
|
|
|
|
TRUE If library loaded and data initialized
|
|
|
|
--*/
|
|
{
|
|
CancelInfo.hCancelDlg = NULL;
|
|
CancelInfo.bCancel = FALSE;
|
|
CancelInfo.hThread = NULL;
|
|
CancelInfo.fmifsSuccess = FmifsLoaded(hWnd);
|
|
CancelInfo.dReason = 0;
|
|
CancelInfo.fuStyle = 0;
|
|
CancelInfo.nPercentDrawn = 0;
|
|
CancelInfo.eCancelType = CANCEL_NULL;
|
|
CancelInfo.bModal = TRUE;
|
|
|
|
CancelInfo.Info.Format.iFormatDrive = 0;
|
|
CancelInfo.Info.Format.fmMediaType = Unknown;
|
|
CancelInfo.Info.Format.fQuick = FALSE;
|
|
CancelInfo.Info.Format.fFlags = 0;
|
|
CancelInfo.Info.Format.szLabel[0] = 0;
|
|
|
|
CancelInfo.Info.Copy.iSourceDrive = 0;
|
|
CancelInfo.Info.Copy.iDestDrive = 0;
|
|
CancelInfo.Info.Copy.bFormatDest = FALSE;
|
|
|
|
if (CancelInfo.fmifsSuccess) {
|
|
bDataInitialized = TRUE;
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
FormatDiskInDrive (
|
|
IN HWND hWnd, // owner window
|
|
IN TCHAR cDrive, // drive letter to format (only A or B)
|
|
IN LPCTSTR szLabel, // label text
|
|
IN BOOL bConfirmFormat // prompt with "r-u-sure?" dialog
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
formats the floppy disk in the specified drive and labels it if desired.
|
|
Always use a complete format.
|
|
Arguments:
|
|
|
|
IN HWND hWnd, // owner window
|
|
IN TCHAR cDrive, // drive letter to format (only A or B)
|
|
IN LPCTSTR szLabel, // label text
|
|
IN BOOL bConfirmFormat // prompt with "r-u-sure?" dialog before formatting
|
|
|
|
Return Value:
|
|
|
|
TRUE if disk is formatted
|
|
FALSE if not
|
|
|
|
--*/
|
|
{
|
|
UINT nDlgBox;
|
|
#if defined(JAPAN) && defined(_X86_)
|
|
// fixed kkntbug #11940
|
|
// Network client administrator can not make install disks on PC with C drive as FD
|
|
|
|
LONG lFloppyDiskSize;
|
|
#endif
|
|
|
|
#ifndef JAPAN
|
|
// fixed kkntbug #11940
|
|
// Network client administrator can not make install disks on PC with C drive as FD
|
|
|
|
// check input argument
|
|
switch (cDrive){
|
|
case TEXT('A'):
|
|
case TEXT('a'):
|
|
case TEXT('B'):
|
|
case TEXT('b'):
|
|
break;
|
|
|
|
default:
|
|
SetLastError (ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
if (lstrlen(szLabel) > MAXLABELLEN) {
|
|
SetLastError (ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
// make sure the DLL and data structures are initialized
|
|
if (!bDataInitialized) {
|
|
if (!InitUserData (hWnd)) {
|
|
SetLastError (ERROR_FILE_NOT_FOUND);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// set the remaining fields to format the diskette
|
|
CancelInfo.hThread = NULL;
|
|
CancelInfo.hCancelDlg = NULL;
|
|
CancelInfo.eCancelType = CANCEL_FORMAT;
|
|
CancelInfo.bCancel = FALSE;
|
|
CancelInfo.dReason = 0;
|
|
CancelInfo.nPercentDrawn = 0;
|
|
CancelInfo.Info.Format.iFormatDrive = (cDrive - TEXT('A'));
|
|
CancelInfo.Info.Format.fmMediaType = GetDriveTypeFromDriveLetter (cDrive);
|
|
if (CancelInfo.Info.Format.fmMediaType == FmMediaUnknown) {
|
|
switch (GetFloppyDiskSize(cDrive)) {
|
|
case 3:
|
|
CancelInfo.Info.Format.fmMediaType = FmMediaF3_1Pt44_512;
|
|
break;
|
|
|
|
case 5:
|
|
CancelInfo.Info.Format.fmMediaType = FmMediaF5_1Pt2_512;
|
|
break;
|
|
|
|
#ifdef JAPAN
|
|
// fixed kkntbug #11940
|
|
// Network client administrator can not make install disks on PC with C drive as FD
|
|
|
|
case F3_12:
|
|
CancelInfo.Info.Format.fmMediaType = FmMediaF3_1Pt2_512;
|
|
break;
|
|
|
|
case F3_123:
|
|
CancelInfo.Info.Format.fmMediaType = FmMediaF3_1Pt23_1024;
|
|
break;
|
|
|
|
case F5_123:
|
|
CancelInfo.Info.Format.fmMediaType = FmMediaF5_1Pt23_1024;
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
CancelInfo.Info.Format.fQuick = FALSE;
|
|
} else {
|
|
#if defined(JAPAN) && defined(_X86_)
|
|
// fixed kkntbug #11940
|
|
// Network client administrator can not make install disks on PC with C drive as FD
|
|
|
|
lFloppyDiskSize = GetFloppyDiskSize(cDrive);
|
|
|
|
if ( ((GetKeyboardType(1)&0xff00) == OEM_ID_PC98) &&
|
|
((lFloppyDiskSize == 3) ||
|
|
(lFloppyDiskSize == F3_123) ||
|
|
(lFloppyDiskSize == F3_12)) ) {
|
|
if (CancelInfo.Info.Format.fmMediaType == FmMediaF5_1Pt23_1024) {
|
|
CancelInfo.Info.Format.fmMediaType = FmMediaF3_1Pt23_1024;
|
|
}
|
|
if (CancelInfo.Info.Format.fmMediaType == FmMediaF5_1Pt2_512) {
|
|
CancelInfo.Info.Format.fmMediaType = FmMediaF3_1Pt2_512;
|
|
}
|
|
}
|
|
#endif
|
|
//
|
|
// Always do a full format.
|
|
//
|
|
CancelInfo.Info.Format.fQuick = FALSE;
|
|
}
|
|
CancelInfo.Info.Format.fFlags = FF_ONLYONE;
|
|
lstrcpy (CancelInfo.Info.Format.szLabel, szLabel);
|
|
if (bConfirmFormat) {
|
|
if (DisplayMessageBox(hWnd,
|
|
IDS_DISKCOPYCONFIRM,
|
|
IDS_DISKCOPYCONFIRMTITLE,
|
|
MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON1) != IDYES)
|
|
// bail out here if they don't want to format the disk
|
|
return FALSE;
|
|
}
|
|
|
|
// make sure disk is in drive and prompt if not
|
|
while (!DriveLoaded (cDrive, FALSE)) {
|
|
if (DisplayMessageBox (hWnd,
|
|
IDS_INSERTDEST,
|
|
FMT_INSERT_FLOPPY,
|
|
MB_OKCANCEL_TASK_INFO) == IDCANCEL) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// display formatting... dialog box
|
|
nDlgBox = (int)DialogBox(GetModuleHandle(NULL), (LPTSTR) MAKEINTRESOURCE(CANCELDLG), hWnd, CancelDlgProc);
|
|
|
|
if (nDlgBox == IDOK) {
|
|
if (CancelInfo.dReason != 0) {
|
|
// display reason for not being formatted if it didn't work
|
|
DisplayMessageBox (hWnd,
|
|
CancelInfo.dReason,
|
|
IDS_APP_NAME,
|
|
MB_OK_TASK_EXCL);
|
|
return FALSE;
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
LabelDiskInDrive (
|
|
IN HWND hWnd, // owner window
|
|
IN TCHAR cDrive, // drive letter to format (only A or B)
|
|
IN LPCTSTR szLabel // label text
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
labels the floppy disk in the specified drive.
|
|
|
|
Arguments:
|
|
|
|
IN HWND hWnd, // owner window
|
|
IN TCHAR cDrive, // drive letter to label (only A or B)
|
|
IN LPCTSTR szLabel, // label text
|
|
|
|
Return Value:
|
|
|
|
TRUE if disk is formatted
|
|
FALSE if not
|
|
|
|
--*/
|
|
{
|
|
TCHAR szDrive[4];
|
|
|
|
#ifndef JAPAN
|
|
// check input argument
|
|
switch (cDrive){
|
|
case TEXT('A'):
|
|
case TEXT('a'):
|
|
case TEXT('B'):
|
|
case TEXT('b'):
|
|
break;
|
|
|
|
default:
|
|
SetLastError (ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
#endif
|
|
|
|
if (lstrlen(szLabel) > MAXLABELLEN) {
|
|
SetLastError (ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
// make sure the DLL and data structures are initialized
|
|
if (!bDataInitialized) {
|
|
if (!InitUserData (hWnd)) {
|
|
SetLastError (ERROR_FILE_NOT_FOUND);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
szDrive[0] = cDrive;
|
|
szDrive[1] = cColon;
|
|
szDrive[2] = 0;
|
|
|
|
// make sure disk is in drive and prompt if not
|
|
while (!DriveLoaded (cDrive, FALSE)) {
|
|
if (DisplayMessageBox (hWnd,
|
|
IDS_INSERTDEST,
|
|
FMT_INSERT_FLOPPY,
|
|
MB_OKCANCEL_TASK_INFO) == IDCANCEL) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return (*lpfnSetLabel)(szDrive, (PWSTR)szLabel);
|
|
}
|
|
|