#include "diskcopy.h" #include "ids.h" #include "..\..\inc\help.h" #define WM_DONE_WITH_FORMAT (WM_USER + 100) #ifndef WINNT // // NT has multiple disk formats handled by FMIFS.DLL // #ifdef DBCS // changed from NEC_98 to DBCS. // 3mode FD will use this on non NEC and BOOTSEC doesn't // suffer from this anyway. #define SEC_SIZE 1024 #else #define SEC_SIZE 512 #endif #define DEVPB_DEVATT_DMF 0x0800 // // The low BYTE of Options is the lock sub-type // #define LFS_OPT_EXCLUSIVE 0 // Only owner of lock can read or write #define LFS_OPT_ALLOWOPEN 1 // See below option definitions #define LFS_OPT_ALLOWRDBLKWRT 2 // Requires LFS_OPT_ALLOWOPEN first #define LFS_OPT_BLKALL 3 // Requires LFS_OPT_ALLOWRDBLKWRT first // // The HIGH WORD of Options is the lock type modifyer for LFS_OPT_ALLOWOPEN // // NOTE that this is actually a flag field as opposed to a value setting. // #define LFS_OPT_ALLOWRDFLWRT 0x0000 // Allow others to read, fail writes #define LFS_OPT_ALLOWRDWRT 0x0001 // Allow others to read and write #define LFS_OPT_ALLOWMMACT 0x0000 // Allow memory mapped file activity #define LFS_OPT_NOMMACT 0x0002 // Fail memory mapped file activity #define LFS_OPT_FORMAT 0x0004 // Mount default FSD #define IOCTL_FORMAT 0x0842 #define IOCTL_GET_DPB 0x0860 #define IOCTL_SET_DPB 0x0840 #define IOCTL_READ 0x0861 #define IOCTL_WRITE 0x0841 #define IOCTL_LOCK 0x084A #define IOCTL_UNLOCK 0x086A /* Media descriptor values for different floppy drives */ // NOTE: these are not all unique! #define MEDIA_160 0xFE /* 160KB */ #define MEDIA_320 0xFF /* 320KB */ #define MEDIA_180 0xFC /* 180KB */ #define MEDIA_360 0xFD /* 360KB */ #define MEDIA_1200 0xF9 /* 1.2MB */ #define MEDIA_720 0xF9 /* 720KB */ #ifdef NEC_98 #define MEDIA_1250 0xFE /* 1.25MB(1024bps) */ #endif #define MEDIA_1440 0xF0 /* 1.44M */ #define MEDIA_2880 0xF0 /* 2.88M */ /* DriveIOCTL error codes */ #define SECNOTFOUND 0x1B #define CRCERROR 0x17 #define GENERALERROR 0x1F #pragma pack(1) /*--------------------------------------------------------------------------*/ /* BIOS Parameter Block Structure - */ /*--------------------------------------------------------------------------*/ typedef struct { WORD cbSec; /* Bytes per sector */ BYTE secPerClus; /* Sectors per cluster */ WORD cSecRes; /* Reserved sectors */ BYTE cFAT; /* FATS */ WORD cDir; /* Root Directory Entries */ WORD cSec; /* Total number of sectors in image */ BYTE bMedia; /* Media descriptor */ WORD secPerFAT; /* Sectors per FAT */ WORD secPerTrack; /* Sectors per track */ WORD cHead; /* Heads */ WORD cSecHidden; /* Hidden sectors */ } BPB, *PBPB; /*--------------------------------------------------------------------------*/ /* Drive Parameter Block Structure - */ /*--------------------------------------------------------------------------*/ typedef struct { BYTE drive; BYTE unit; WORD sector_size; BYTE cluster_mask; BYTE cluster_shift; WORD first_FAT; BYTE FAT_count; WORD root_entries; WORD first_sector; WORD max_cluster; BYTE FAT_size; WORD dir_sector; LONG reserved1; BYTE media; BYTE first_access; BYTE reserved2[4]; WORD next_free; WORD free_cnt; BYTE DOS4_Extra; } DPB, *PDPB; #define MAX_SEC_PER_TRACK 40 /*--------------------------------------------------------------------------*/ /* Device Parameter Block Structure - */ /*--------------------------------------------------------------------------*/ typedef struct { BYTE SplFunctions; BYTE devType; WORD devAtt; // see dskmaint for what these are WORD NumCyls; BYTE bMediaType; /* 0=>1.2MB and 1=>360KB */ BPB BPB; BYTE reserved3[MAX_SEC_PER_TRACK * 4 + 2]; } DEVPB, *PDEVPB; #define TRACKLAYOUT_OFFSET (7+31) /* Offset of tracklayout * in a Device Parameter Block */ typedef struct { BYTE jump[3]; /* 3 byte jump */ BYTE label[8]; /* OEM name and version */ BPB BPB; /* BPB */ BYTE bootdrive; /* INT 13h indicator for boot device */ BYTE dontcare[SEC_SIZE-12-3-sizeof(BPB)]; BYTE phydrv; WORD signature; } BOOTSEC; #pragma pack() #endif // ndef WINNT typedef struct { int nSrcDrive; int nDestDrive; UINT nCylinderSize; UINT nCylinders; UINT nHeads; UINT nSectorsPerTrack; UINT nSectorSize; #ifndef WINNT PDEVPB pTrackLayout; /* DEVPB with the track layout */ LPBYTE pCopyBuffer; DWORD dwCopyBufferSize; #else BOOL bNotifiedWriting; #endif BOOL bFormatTried; HWND hdlg; HANDLE hThread; BOOL bUserAbort; DWORD dwError; } DISKINFO, *PDISKINFO; int ErrorMessageBox(DISKINFO* pdi, UINT uFlags); void SetStatusText(DISKINFO* pdi, int id); BOOL PromptInsertDisk(DISKINFO *pdi, LPCTSTR lpsz, BOOL fAutoCheck); #ifndef WINNT extern BOOL DriveIOCTL(int iDrive, int cmd, void *pv); BOOL LockDrive(int iDrive, BOOL fLock, WORD wPermissions); const TCHAR c_szVWIN32[] = TEXT("\\\\.\\vwin32"); // in: // iDrive 0 based drive number // // returns: // TRUE success // FALSE failure BOOL DriveIOCTL(int iDrive, int cmd, void *pv) { DWORD reg[7]; DWORD cbBytes; HANDLE h; BOOL bRet; reg[0] = iDrive + 1; // make 1 based drive number reg[1] = (DWORD)pv; // out buffer reg[2] = cmd; // device specific command code reg[3] = 0x440D; // generic read ioctl reg[6] = 0x0001; // flags, assume error (carry) h = CreateFile(c_szVWIN32, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (h != INVALID_HANDLE_VALUE) { DeviceIoControl(h, 1, ®, sizeof(reg), ®, sizeof(reg), &cbBytes, 0); CloseHandle(h); } bRet = !(reg[6] & 0x0001); #ifdef DEBUG // all we ever get is access denied if (!bRet) { DebugMsg(DM_TRACE, TEXT("IOCtl error %x, %d"), reg[3], GetLastError()); //SetLastError(reg[3]); } #endif return bRet; } BOOL ReadWriteSector(void *pBuf, UINT nFunc, UINT nDrive, UINT nCylinder, UINT wHead, UINT wCount) { #pragma pack(1) typedef struct { BYTE bSplFn; WORD wHead; WORD nCylinder; WORD wStSector; WORD wCount; LPBYTE pBuf; WORD wSel; } RW_PARMBLOCK; #pragma pack() RW_PARMBLOCK rwp; rwp.bSplFn = 0; rwp.wHead = wHead; rwp.nCylinder = nCylinder; rwp.wStSector = 0; rwp.wCount = wCount; rwp.pBuf = pBuf; _asm mov rwp.wSel, ds return DriveIOCTL(nDrive, nFunc, &rwp); } // This reads the boot sector of a floppy and returns a ptr to // the BIOS PARAMETER BLOCK in the Boot sector. // BUGBUG: boot sector sizes != 512 will puke BOOL GetBootBPB(int nDrive, PBPB pBPB) { BOOTSEC Boot; if (ReadWriteSector(&Boot, IOCTL_READ, nDrive, 0, 0, 1) && (Boot.jump[0] == 0xEB || Boot.jump[0] == 0xE9)) { *pBPB = Boot.BPB; return TRUE; } return FALSE; } // Gets get the BPB of the Physical Drive. BOOL GetBPB(int nDrive, PDEVPB pDevicePB, BYTE bDisk) { /* All fields in pDevicePB must be initialized to zero. */ memset(pDevicePB, 0, sizeof(DEVPB)); pDevicePB->SplFunctions = bDisk; /* Spl Function field must be set to get parameters */ Assert(pDevicePB->SplFunctions == 0); return DriveIOCTL(nDrive, IOCTL_GET_DPB, pDevicePB); } /* Checks whether the two BPB are compatible for the purpose of performing * the diskcopy operation. */ BOOL CheckBPBCompatibility(PDEVPB pSrc, PDEVPB pDst) { #ifndef NEC_98 /* Let us compare the media byte */ if (pSrc->BPB.bMedia == 0xF9) { /* If the source and dest have the same number of sectors, * or if srce is 720KB and Dest is 1.44MB floppy drive, * thnigs are kosher. */ if ((pSrc->BPB.cSec == pDst->BPB.cSec) || ((pSrc->BPB.secPerTrack == 9) && (pDst->BPB.bMedia == 0xF0))) return TRUE; } else #endif { /* If they have the same media byte */ if ((pSrc->BPB.bMedia == pDst->BPB.bMedia) && (pSrc->BPB.cbSec == pDst->BPB.cbSec) && // bytes per sector are the same (pSrc->BPB.cSec == pDst->BPB.cSec)) // total sectors on drive are the same return TRUE; /* They are compatible */ #ifndef NEC_98 else if /* srce is 160KB and dest is 320KB drive */ (((pSrc->BPB.bMedia == MEDIA_160) && (pDst->BPB.bMedia == MEDIA_320)) || /* or if srce is 180KB and dest is 360KB drive */ ((pSrc->BPB.bMedia == MEDIA_180) && (pDst->BPB.bMedia == MEDIA_360)) || /* or if srce is 1.44MB and dest is 2.88MB drive */ ((pSrc->BPB.bMedia == MEDIA_1440) && (pDst->BPB.bMedia == MEDIA_2880) && ((pSrc->devType == 7) || (pSrc->devType == 9)) && (pDst->devType == 9)) || /* or if srce is 360KB and dest is 1.2MB drive */ ((pSrc->BPB.bMedia == MEDIA_360) && (pDst->BPB.secPerTrack == 15))) return TRUE; /* They are compatible */ #endif } /* All other combinations are currently incompatible. */ return FALSE; } PDEVPB BuildDEVPB(PDEVPB pDEVPB) { PDEVPB pNewDEVPB = LocalAlloc(LPTR, TRACKLAYOUT_OFFSET + 2 + pDEVPB->BPB.secPerTrack * 4); if (pNewDEVPB) { WORD wTrackNumber, *pData; memcpy(pNewDEVPB, pDEVPB, TRACKLAYOUT_OFFSET); pData = (WORD *)((LPBYTE)pNewDEVPB + TRACKLAYOUT_OFFSET); *pData++ = pDEVPB->BPB.secPerTrack; for (wTrackNumber = 1; wTrackNumber <= pDEVPB->BPB.secPerTrack; wTrackNumber++) { *pData++ = wTrackNumber; *pData++ = pDEVPB->BPB.cbSec; } } return pNewDEVPB; } /* Saves a copy of the drive parameters block and * Checks if the BPB of Drive and BPB of disk are different and if * so, modifies the drive parameter block accordingly. */ BOOL ModifyDeviceParams(int nDrive, PDEVPB pdpbParams, PDEVPB *ppSaveDevPB, PBPB pDriveBPB, PBPB pMediaBPB) { PDEVPB pNewDPB; *ppSaveDevPB = BuildDEVPB(pdpbParams); if (!*ppSaveDevPB) return FALSE; /* Check if the Disk and Drive have the same parameters */ //if (pMediaBPB->bMedia != pDriveBPB->bMedia) //{ /* They are not equal; So, it must be a 360KB floppy in a 1.2MB drive * or a 720KB floppy in a 1.44MB drive kind of situation!. * So, modify the DriveParameterBlock's BPB. */ // copy these always because sometimes (liek 2.88) bMedia is the same on // both when the media are different pdpbParams->BPB = *pMediaBPB; DebugMsg(DM_TRACE, TEXT("BPB = %x %x %x %x\n %x %x %x %x\n %x %x %x \n"), (DWORD)pMediaBPB->cbSec, (DWORD)pMediaBPB->secPerClus, (DWORD)pMediaBPB->cSecRes, (DWORD)pMediaBPB->cFAT, (DWORD)pMediaBPB->cDir, (DWORD)pMediaBPB->cSec, (DWORD)pMediaBPB->bMedia, (DWORD)pMediaBPB->secPerFAT, (DWORD)pMediaBPB->secPerTrack, (DWORD)pMediaBPB->cHead, (DWORD)pMediaBPB->cSecHidden); //} // Build a DPB with TrackLayout pNewDPB = BuildDEVPB(pdpbParams); if (!pNewDPB) { LocalFree(*ppSaveDevPB); *ppSaveDevPB = NULL; return FALSE; } pNewDPB->SplFunctions = 4; /* To Set parameters */ // REVIEW: special case, may not be needed on win95 if (pMediaBPB->bMedia == MEDIA_360) { pNewDPB->NumCyls = 40; pNewDPB->bMediaType = 1; } DriveIOCTL(nDrive, IOCTL_SET_DPB, pNewDPB); LocalFree(pNewDPB); return TRUE; } /* This calls IOCTL format if DOS ver >= 3.2; Else calls BIOS. * * Returns : 0 if no error * > 0 if tolerable error (resuling in bad sectors); * -1 if fatal error (Format has to be aborted); */ int FormatTrack(UINT nDisk, UINT nCylinder, UINT wHead) { #pragma pack(1) typedef struct { BYTE bSpl; WORD wHead; WORD nCylinder; } FORMATPARAMS; #pragma pack() FORMATPARAMS fp; // int iErrCode; DebugMsg(DM_TRACE, TEXT("Format %d %d"), wHead, nCylinder); fp.bSpl = 0; fp.wHead = wHead; fp.nCylinder = nCylinder; if (DriveIOCTL(nDisk, IOCTL_FORMAT, &fp)) return 0; // success else { DebugMsg(DM_ERROR, TEXT("FormatTrack failed %d"), GetLastError()); return -1; // fatial error } #if 0 // BUGBUG: need extended error switch (iErrCode) { case NOERROR: case CRCERROR: case SECNOTFOUND: case GENERALERROR: return iErrCode; default: return -1; } #endif } #define FORMAT_RETRY -1 #define FORMAT_ERROR 0 #define FORMAT_SUCCESS 1 int FormatAllTracks(PDISKINFO pdi, UINT nStartCylinder, UINT nStartHead) { int iErrCode; BOOL bRetValue = FORMAT_SUCCESS; LockDrive(pdi->nDestDrive, TRUE, LFS_OPT_FORMAT); SetStatusText(pdi, IDS_FORMATTINGDEST); pdi->pTrackLayout->SplFunctions = 5; DriveIOCTL(pdi->nDestDrive, IOCTL_SET_DPB, pdi->pTrackLayout); // Format tracks one by one, checking if the user has "Aborted" // after each track is formatted; DlgProgreeProc() will set the global // bUserAbort, if the user has aborted; while (nStartCylinder < pdi->nCylinders) { /* Has the user aborted? */ if (pdi->bUserAbort) { bRetValue = FORMAT_ERROR; break; } Retry: /* If no message is pending, go ahead and format one track */ if ((iErrCode = FormatTrack(pdi->nDestDrive, nStartCylinder, nStartHead))) { /* Check if it is a fatal error */ if (iErrCode == -1) { pdi->dwError = IDS_ERROR_FORMAT; LockDrive(pdi->nDestDrive, FALSE, LFS_OPT_FORMAT); LockDrive(pdi->nDestDrive, FALSE, 0); if (ErrorMessageBox(pdi, MB_RETRYCANCEL | MB_ICONERROR) == IDRETRY) { LockDrive(pdi->nDestDrive, TRUE, 0); LockDrive(pdi->nDestDrive, TRUE, LFS_OPT_FORMAT); pdi->dwError = 0; goto Retry; } bRetValue = FORMAT_ERROR; break; } } if (++nStartHead >= pdi->nHeads) { nStartHead = 0; nStartCylinder++; } } pdi->pTrackLayout->SplFunctions = 4; DriveIOCTL(pdi->nDestDrive, IOCTL_SET_DPB, pdi->pTrackLayout); LockDrive(pdi->nDestDrive, FALSE, 0); return bRetValue; } BOOL AllocCopyDiskBuffers(PDISKINFO pdi) { // now, lets try to allocate a buffer for the whole disk, and // if that fails try smaller pdi->dwCopyBufferSize = pdi->nCylinderSize * pdi->nCylinders; // we will try down to 8 cylinders worth, less than that means // there will be too much disk swapping so don't bother do { pdi->pCopyBuffer = GlobalAlloc(GPTR, pdi->dwCopyBufferSize); if (pdi->pCopyBuffer) return TRUE; // reduce request and try again DebugMsg(DM_TRACE, TEXT("Failed alloc, trying smaller size")); pdi->dwCopyBufferSize /= 2; } while (pdi->dwCopyBufferSize > (8 * pdi->nCylinderSize)); DebugMsg(DM_ERROR, TEXT("Failed to alloc copy buffers")); return FALSE; } // BOOL bWrite; TRUE for Write, FALSE for Read int ReadWriteCylinder(PDISKINFO pdi, LPBYTE pBuf, BOOL bWrite, UINT nCylinder) { UINT nHead; DebugMsg(DM_TRACE, TEXT("%s Cylinder %d"), bWrite ? TEXT("Write") : TEXT("Read"), nCylinder); /* Perform the operation for all the heads for a given cylinder */ for (nHead = 0; nHead < pdi->nHeads; nHead++, pBuf += pdi->nSectorsPerTrack * pdi->nSectorSize) { Retry: if (bWrite) { if (!ReadWriteSector(pBuf, IOCTL_WRITE, pdi->nDestDrive, nCylinder, nHead, pdi->nSectorsPerTrack)) { // that didn't work, try formatting if (!pdi->bFormatTried) { DebugMsg(DM_ERROR, TEXT("ReadWriteCylinder() write failed, trying format")); pdi->bFormatTried = TRUE; switch (FormatAllTracks(pdi, nCylinder, nHead)) { case FORMAT_ERROR: return -1; /* Failure or user cancel */ case FORMAT_SUCCESS: break; } SetStatusText(pdi, IDS_WRITING); if (!ReadWriteSector(pBuf, IOCTL_WRITE, pdi->nDestDrive, nCylinder, nHead, pdi->nSectorsPerTrack)) { pdi->dwError = IDS_ERROR_WRITE; goto PromptRetry; } } else return -1; } } else { if (!ReadWriteSector(pBuf, IOCTL_READ, pdi->nSrcDrive, nCylinder, nHead, pdi->nSectorsPerTrack)) { pdi->dwError = IDS_ERROR_READ; PromptRetry: if (ErrorMessageBox(pdi, MB_RETRYCANCEL | MB_ICONERROR) == IDRETRY) { pdi->dwError = 0; goto Retry; } DebugMsg(DM_ERROR, TEXT("RWS Failed %d %d %d"), nCylinder, nHead, pdi->nSectorsPerTrack); return -1; } } } return 0; } // BOOL bWrite TRUE for Write, FALSE for Read // // reads or writes as many cylinders as possible using whats in // pCopyBuffer // // returns: // the next cylinder to be read. // int ReadWriteMaxPossible(PDISKINFO pdi, BOOL bWrite, UINT nStartCylinder) { LPBYTE pBuf; SetStatusText(pdi, bWrite ? IDS_WRITING : IDS_READING); pdi->bFormatTried = FALSE; // buffer needs to be multiple of cyl size Assert((pdi->dwCopyBufferSize % pdi->nCylinderSize) == 0); /* We will read a cylinder only if we can read the entire cylinder. */ for (pBuf = pdi->pCopyBuffer; pBuf < (pdi->pCopyBuffer + pdi->dwCopyBufferSize); pBuf += pdi->nCylinderSize) { if (pdi->bUserAbort) return -1; if (ReadWriteCylinder(pdi, pBuf, bWrite, nStartCylinder)) { DebugMsg(DM_ERROR, TEXT("ReadWriteCylinder failed")); return -1; } nStartCylinder++; SendDlgItemMessage(pdi->hdlg, IDD_PROBAR, PBM_DELTAPOS, 1, 0); /* Have we read/written all the cylinders? */ if (nStartCylinder >= pdi->nCylinders) break; } return nStartCylinder; } void RestoreDPB(int nDisk, PDEVPB pDEVPB) { if (pDEVPB) { pDEVPB->SplFunctions = 4; DriveIOCTL(nDisk, IOCTL_SET_DPB, pDEVPB); LocalFree(pDEVPB); } } BOOL LockDrive(int iDrive, BOOL fLock, WORD wPermissions) { int idCmd = fLock ? IOCTL_LOCK : IOCTL_UNLOCK; DWORD reg[7]; BOOL bRet; DWORD cbBytes; HANDLE h; reg[0] = MAKELONG(iDrive + 1, LFS_OPT_EXCLUSIVE); // make 1 based drive number and lock level reg[1] = MAKELONG(wPermissions, wPermissions); // permissions reg[2] = idCmd; // device specific command code reg[3] = 0x440D; // generic read ioctl reg[6] = 0x0001; // flags, assume error (carry) h = CreateFile(c_szVWIN32, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (h != INVALID_HANDLE_VALUE) { DeviceIoControl(h, 1, ®, sizeof(reg), ®, sizeof(reg), &cbBytes, 0); CloseHandle(h); } bRet = !(reg[6] & 0x0001); DebugMsg(DM_TRACE, TEXT("LockDrive bRet = %d"), bRet); return bRet; } DWORD CALLBACK CopyDiskThreadProc(DISKINFO *pdi) { int rc = -1; UINT nCylinder, nNextCylinder; BPB BootBPB; /* Boot Drive's BPB (taken from Boot sector) */ #ifdef DBCS //NEC_98 // we need this for 3mode as well. // BPB DstBootBPB; /* Destination BPB */ #endif DEVPB dpbSrcParams, dpbDstParams; PDEVPB pSaveSrcParams, pSaveDstParams; BOOL bSingleDrive = pdi->nSrcDrive == pdi->nDestDrive; HWND hwndProgress; EnableWindow(GetDlgItem(pdi->hdlg, IDD_FROM), FALSE); EnableWindow(GetDlgItem(pdi->hdlg, IDD_TO), FALSE); DebugMsg(DM_TRACE, TEXT("CopyDisk %d -> %d"), pdi->nSrcDrive, pdi->nDestDrive); hwndProgress = GetDlgItem(pdi->hdlg, IDD_PROBAR); // src and dest the same, special case this. if (!PromptInsertDisk(pdi, bSingleDrive ? MAKEINTRESOURCE(IDS_INSERTSRC) : MAKEINTRESOURCE(IDS_INSERTSRCDEST), TRUE)) { goto Failure; } // lock the drive. do it here after the prompt so that the user // has a chance to view the disk and make sure it's right LockDrive(pdi->nSrcDrive, TRUE, 0); if (pdi->nSrcDrive != pdi->nDestDrive) LockDrive(pdi->nDestDrive, TRUE, 0); /* Get the BiosParameterBlock of source drive */ if (!GetBPB(pdi->nSrcDrive, &dpbSrcParams, 1)) { DebugMsg(DM_ERROR, TEXT("Bad source disk")); goto BadSourceDisk; } /* Get the BiosParameterBlock of the Source Diskette */ if (!GetBootBPB(pdi->nSrcDrive, &BootBPB)) { DebugMsg(DM_ERROR, TEXT("Bad source disk boot sector")); BadSourceDisk: pdi->dwError = IDS_SRCDISKBAD; ShellMessageBox(g_hinst, pdi->hdlg, MAKEINTRESOURCE(IDS_SRCDISKBAD), NULL, MB_ICONHAND | MB_OK); goto Failure; } #if defined(DBCS) && !defined(NEC_98) // Reject 1024 b/sec in case 3 mode FDD. if (BootBPB.cbSec == 1024) { pdi->dwError = IDS_SRCDISK1024; ShellMessageBox(g_hinst, pdi->hdlg, MAKEINTRESOURCE(IDS_SRCDISK1024), NULL, MB_ICONERROR | MB_OK); goto Failure; } #endif if (dpbSrcParams.devAtt & DEVPB_DEVATT_DMF) { pdi->dwError = IDS_SRCDISKDMF; ShellMessageBox(g_hinst, pdi->hdlg, MAKEINTRESOURCE(IDS_SRCDISKDMF), NULL, MB_ICONERROR | MB_OK); goto Failure; } /* Get the BPB and DPB for the Destination drive also; */ if (!bSingleDrive) { #ifdef NEC_98 /* Since NEC_98 3.5" FD drive can handle both 1.25MB/1.21MB media and 1.44 MB media, we should check actaul medias for both src and dest are completely same. */ if (!GetBootBPB(pdi->nDestDrive, &DstBootBPB)) #else if (!GetBPB(pdi->nDestDrive, &dpbDstParams, 0)) #endif { DebugMsg(DM_ERROR, TEXT("Bad dest disk")); pdi->dwError = IDS_DSTDISKBAD; ShellMessageBox(g_hinst, pdi->hdlg, MAKEINTRESOURCE(IDS_DSTDISKBAD), NULL, MB_ICONHAND | MB_OK); goto Failure; } #ifdef NEC_98 /* Set ACTUAL BPB, not device default BPB due to above reason */ dpbSrcParams.BPB = BootBPB; dpbDstParams.BPB = DstBootBPB; #endif /* Compare BPB of source and Dest to see if they are compatible */ if (!(CheckBPBCompatibility(&dpbSrcParams, &dpbDstParams))) { DebugMsg(DM_ERROR, TEXT("disks don't match")); pdi->dwError = IDS_COPYSRCDESTINCOMPAT; ShellMessageBox(g_hinst, pdi->hdlg, MAKEINTRESOURCE(IDS_COPYSRCDESTINCOMPAT), NULL, MB_ICONHAND | MB_OK); goto Failure; } } if (!ModifyDeviceParams(pdi->nSrcDrive, &dpbSrcParams, &pSaveSrcParams, &dpbSrcParams.BPB, &BootBPB)) { DebugMsg(DM_ERROR, TEXT("can't set device params for source")); goto Failure; } if (!bSingleDrive) { if (!ModifyDeviceParams(pdi->nDestDrive, &dpbDstParams, &pSaveDstParams, &dpbDstParams.BPB, &BootBPB)) { DebugMsg(DM_ERROR, TEXT("can't set device params for dest")); RestoreDPB(pdi->nSrcDrive, pSaveSrcParams); goto Failure; } } pdi->nCylinderSize = BootBPB.secPerTrack * BootBPB.cbSec * BootBPB.cHead; pdi->nCylinders = BootBPB.cSec / (BootBPB.secPerTrack * BootBPB.cHead); pdi->nHeads = BootBPB.cHead; pdi->nSectorsPerTrack = BootBPB.secPerTrack; pdi->nSectorSize = BootBPB.cbSec; if (!pdi->nCylinderSize || !pdi->nCylinders) { pdi->dwError = IDS_ERROR_GENERAL; ErrorMessageBox(pdi, MB_OK | MB_ICONERROR); goto Failure; } PostMessage(hwndProgress, PBM_SETRANGE, 0, MAKELONG(0, (WORD)pdi->nCylinders * 2)); // In case we need to format the destination diskette, we need to know the // track layout; So, build a DPB with the required track layout pdi->pTrackLayout = BuildDEVPB(&dpbSrcParams); if (!pdi->pTrackLayout) goto Failure0; /* The following is required to format a 360KB floppy in a 1.2MB * drive of NCR PC916 machine; We do formatting, if the destination * floppy is an unformatted one; * Fix for Bug #6894 --01-10-90-- SANKAR -- */ if (pdi->pTrackLayout->BPB.bMedia == MEDIA_360) { pdi->pTrackLayout->NumCyls = 40; pdi->pTrackLayout->bMediaType = 1; } /* We wish we could do the following allocation at the begining of this * function, but we can not do so, because we need di * and we just got it; */ if (!AllocCopyDiskBuffers(pdi)) { // ShellMessageBox(g_hinst, pdi->hdlg, MAKEINTRESOURCE(IDS_REASONS+DE_INSMEM), NULL, MB_ICONHAND | MB_OK); goto Failure0; } for (nCylinder = 0; nCylinder < pdi->nCylinders; nCylinder = nNextCylinder) { // Do not prompt for the first time, because the Source diskette is // already in the drive. if (bSingleDrive && (nCylinder > 0)) { LockDrive(pdi->nSrcDrive, FALSE, 0); if (!PromptInsertDisk(pdi, MAKEINTRESOURCE(IDS_INSERTSRC), FALSE)) { pdi->bUserAbort = TRUE; goto Failure0; } LockDrive(pdi->nSrcDrive, TRUE, 0); } // Read in the current cylinders rc = ReadWriteMaxPossible(pdi, FALSE, nCylinder); if (rc < 0) break; else nNextCylinder = rc; // If this is a single drive system, ask the user to insert // the destination diskette. if (bSingleDrive) { LockDrive(pdi->nSrcDrive, FALSE, 0); if (!PromptInsertDisk(pdi, MAKEINTRESOURCE(IDS_INSERTDEST), FALSE)) { pdi->bUserAbort = TRUE; goto Failure0; } LockDrive(pdi->nSrcDrive, TRUE, 0); #ifdef DBCS // NEC_98 // we need this for 3mode as well. // /* Get destination media BPB */ if (!GetBootBPB(pdi->nSrcDrive, &DstBootBPB)) { DebugMsg(DM_ERROR, "Bad dest disk"); pdi->dwError = IDS_DSTDISKBAD; ShellMessageBox(g_hinst, pdi->hdlg, MAKEINTRESOURCE(IDS_DSTDISKBAD), NULL, MB_ICONHAND | MB_OK); rc = -1; break; } dpbDstParams.BPB = DstBootBPB; /* Compare BPB of source and Dest to see if they are compatible */ if (!(CheckBPBCompatibility(&dpbSrcParams, &dpbDstParams))) { DebugMsg(DM_ERROR, "disks don't match"); pdi->dwError = IDS_COPYSRCDESTINCOMPAT; ShellMessageBox(g_hinst, pdi->hdlg, MAKEINTRESOURCE(IDS_COPYSRCDESTINCOMPAT), NULL, MB_ICONHAND | MB_OK); rc = -1; break; } #endif } // Write out the current cylinders rc = ReadWriteMaxPossible(pdi, TRUE, nCylinder); if (rc < 0) break; } if (pdi->pCopyBuffer) { GlobalFree(pdi->pCopyBuffer); pdi->pCopyBuffer = NULL; } Failure0: // Reset the Source drive parameters to the same as old RestoreDPB(pdi->nSrcDrive, pSaveSrcParams); if (!bSingleDrive) RestoreDPB(pdi->nDestDrive, pSaveDstParams); if (pdi->pTrackLayout) { LocalFree(pdi->pTrackLayout); pdi->pTrackLayout = NULL; } Failure: PostMessage(pdi->hdlg, WM_DONE_WITH_FORMAT, 0, 0); return rc; } #else typedef struct _fmifs { HANDLE hDll; PFMIFS_DISKCOPY_ROUTINE DiskCopy; } FMIFS; typedef FMIFS *PFMIFS; BOOL LoadFMIFS(PFMIFS pFMIFS) { // // Load the FMIFS DLL and query for the entry points we need // pFMIFS->hDll = LoadLibrary(TEXT("FMIFS.DLL")); if (NULL == pFMIFS->hDll) return FALSE; pFMIFS->DiskCopy = (PFMIFS_DISKCOPY_ROUTINE)GetProcAddress(pFMIFS->hDll, "DiskCopy"); if (NULL == pFMIFS->DiskCopy) { FreeLibrary(pFMIFS->hDll); pFMIFS->hDll = (HANDLE)0; return FALSE; } return TRUE; } void UnloadFMIFS(PFMIFS pFMIFS) { FreeLibrary(pFMIFS->hDll); pFMIFS->hDll = NULL; pFMIFS->DiskCopy = NULL; } // // Thread-Local Storage index for our DISKINFO structure pointer // static DWORD g_iTLSDiskInfo = 0; static LONG g_cTLSDiskInfo = 0; // Usage count __inline void UnstuffDiskInfoPtr() { if (InterlockedDecrement(&g_cTLSDiskInfo) == 0) TlsFree(g_iTLSDiskInfo); } BOOL StuffDiskInfoPtr(PDISKINFO pDiskInfo) { // // Allocate an index slot for our thread-local DISKINFO pointer, if one // doesn't already exist, then stuff our DISKINFO ptr at that index. // if (0 == g_iTLSDiskInfo) { if (0xFFFFFFFF == (g_iTLSDiskInfo = TlsAlloc())) { return FALSE; } g_cTLSDiskInfo = 0; } InterlockedIncrement(&g_cTLSDiskInfo); if (!TlsSetValue(g_iTLSDiskInfo, (LPVOID) pDiskInfo)) { UnstuffDiskInfoPtr(); return FALSE; } return TRUE; } __inline PDISKINFO GetDiskInfoPtr() { return TlsGetValue(g_iTLSDiskInfo); } BOOLEAN CopyDiskCallback( FMIFS_PACKET_TYPE PacketType, DWORD PacketLength, PVOID PacketData) { PDISKINFO pdi = GetDiskInfoPtr(); int iDisk; // Quit if told to do so.. if (pdi->bUserAbort) return FALSE; switch (PacketType) { case FmIfsPercentCompleted: { DWORD dwPercent = ((PFMIFS_PERCENT_COMPLETE_INFORMATION) PacketData)->PercentCompleted; // // Hokey method of determining "writing" // if (dwPercent > 50 && !pdi->bNotifiedWriting) { pdi->bNotifiedWriting = TRUE; SetStatusText(pdi, IDS_WRITING); } SendDlgItemMessage(pdi->hdlg, IDD_PROBAR, PBM_SETPOS, dwPercent,0); break; } case FmIfsInsertDisk: switch(((PFMIFS_INSERT_DISK_INFORMATION)PacketData)->DiskType) { case DISK_TYPE_SOURCE: case DISK_TYPE_GENERIC: iDisk = IDS_INSERTSRC; break; case DISK_TYPE_TARGET: iDisk = IDS_INSERTDEST; break; case DISK_TYPE_SOURCE_AND_TARGET: iDisk = IDS_INSERTSRCDEST; break; } if (!PromptInsertDisk(pdi, MAKEINTRESOURCE(iDisk), FALSE)) { pdi->bUserAbort = TRUE; return FALSE; } break; case FmIfsFormattingDestination: pdi->bNotifiedWriting = FALSE; // Reset so we get Writing later SetStatusText(pdi, IDS_FORMATTINGDEST); break; case FmIfsIncompatibleFileSystem: case FmIfsIncompatibleMedia: pdi->dwError = IDS_COPYSRCDESTINCOMPAT; ShellMessageBox(g_hinst, pdi->hdlg, MAKEINTRESOURCE(IDS_COPYSRCDESTINCOMPAT), NULL, MB_ICONHAND | MB_OK); return FALSE; case FmIfsMediaWriteProtected: pdi->dwError = IDS_DSTDISKBAD; ShellMessageBox(g_hinst, pdi->hdlg, MAKEINTRESOURCE(IDS_DSTDISKBAD), NULL, MB_ICONHAND | MB_OK); return FALSE; case FmIfsCantLock: // BUGBUG - BobDay - We should do something for this! pdi->dwError = IDS_ERROR_GENERAL; ErrorMessageBox(pdi, MB_OK | MB_ICONERROR); return FALSE; case FmIfsAccessDenied: case FmIfsBadLabel: case FmIfsCantQuickFormat: pdi->dwError = IDS_ERROR_GENERAL; ErrorMessageBox(pdi, MB_OK | MB_ICONERROR); return FALSE; case FmIfsIoError: switch(((PFMIFS_IO_ERROR_INFORMATION)PacketData)->DiskType) { case DISK_TYPE_SOURCE: pdi->dwError = IDS_SRCDISKBAD; break; case DISK_TYPE_TARGET: pdi->dwError = IDS_DSTDISKBAD; break; default: // BUGBUG - BobDay - We should never get this!! pdi->dwError = IDS_ERROR_GENERAL; break; } if (ErrorMessageBox(pdi, MB_RETRYCANCEL | MB_ICONERROR) == IDRETRY) { pdi->dwError = 0; } else { return FALSE; } break; case FmIfsFinished: if (((PFMIFS_FINISHED_INFORMATION)PacketData)->Success) { pdi->dwError = 0; } else { pdi->dwError = IDS_ERROR_GENERAL; } break; default: break; } return TRUE; } DWORD CALLBACK CopyDiskThreadProc(DISKINFO *pdi) { FMIFS fmifs; HWND hwndProgress = GetDlgItem(pdi->hdlg, IDD_PROBAR); EnableWindow(GetDlgItem(pdi->hdlg, IDD_FROM), FALSE); EnableWindow(GetDlgItem(pdi->hdlg, IDD_TO), FALSE); PostMessage(hwndProgress, PBM_SETRANGE, 0, MAKELONG(0, 100)); pdi->bFormatTried = FALSE; pdi->bNotifiedWriting = FALSE; pdi->dwError = 0; if (StuffDiskInfoPtr(pdi) && LoadFMIFS(&fmifs)) { TCHAR szSource[3]; TCHAR szDestination[3]; // // Now copy the disk // szSource[0] = TEXT('A') + pdi->nSrcDrive; szSource[1] = TEXT(':'); szSource[2] = 0; szDestination[0] = TEXT('A') + pdi->nDestDrive; szDestination[1] = TEXT(':'); szDestination[2] = 0; SetStatusText(pdi, IDS_READING); fmifs.DiskCopy(szSource, szDestination, FALSE, CopyDiskCallback); UnstuffDiskInfoPtr(); UnloadFMIFS(&fmifs); } PostMessage(pdi->hdlg, WM_DONE_WITH_FORMAT, 0, 0); return 0; } #endif int ErrorMessageBox(DISKINFO* pdi, UINT uFlags) { if (!pdi->bUserAbort && pdi->dwError) { TCHAR szTemp[1024]; DWORD dwLastError = GetLastError(); DebugMsg(DM_TRACE, TEXT("ERROR %d %d"), pdi->dwError, dwLastError); if (dwLastError) { FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastError, 0, szTemp, sizeof(szTemp), NULL); } else { LoadString(g_hinst, (int)pdi->dwError, szTemp, sizeof(szTemp)); } // if the user didn't abort and it didn't complete normally, post an error box return ShellMessageBox(g_hinst, pdi->hdlg, szTemp, NULL, uFlags); } else return -1; } void SetStatusText(DISKINFO* pdi, int id) { TCHAR szMsg[128]; LoadString(g_hinst, id, szMsg, sizeof(szMsg)); SendDlgItemMessage(pdi->hdlg, IDD_STATUS, WM_SETTEXT, 0, (LPARAM)szMsg); } BOOL PromptInsertDisk(DISKINFO *pdi, LPCTSTR lpsz, BOOL fAutoCheck) { if (fAutoCheck) goto AutoCheckBegin; for (;;) { DWORD dwLastErrorSrc = 0; DWORD dwLastErrorDest = 0 ; TCHAR szPath[4]; if (ShellMessageBox(g_hinst, pdi->hdlg, lpsz, NULL, MB_OKCANCEL | MB_ICONINFORMATION) != IDOK) { pdi->bUserAbort = TRUE; return FALSE; } AutoCheckBegin: szPath[0] = TEXT('A') + pdi->nSrcDrive; szPath[1] = TEXT(':'); szPath[2] = TEXT('\\'); szPath[3] = 0; // make sure both disks are in if (GetFileAttributes(szPath) == (UINT)-1) dwLastErrorDest = GetLastError(); if (pdi->nDestDrive != pdi->nSrcDrive) { szPath[0] = TEXT('A') + pdi->nDestDrive; if (GetFileAttributes(szPath) == (UINT)-1) dwLastErrorDest = GetLastError(); } if (dwLastErrorDest != ERROR_NOT_READY && dwLastErrorSrc != ERROR_NOT_READY) break; } return TRUE; } HICON GetDriveInfo(int nDrive, LPTSTR pszName) { SHFILEINFO shfi; TCHAR szRoot[4]; PathBuildRoot(szRoot, nDrive); if (SHGetFileInfo(szRoot, FILE_ATTRIBUTE_DIRECTORY, &shfi, sizeof(shfi), SHGFI_ICON | SHGFI_SMALLICON | SHGFI_DISPLAYNAME)) // | SHGFI_USEFILEATTRIBUTES { lstrcpy(pszName, shfi.szDisplayName); return shfi.hIcon; } else { lstrcpy(pszName, szRoot); return NULL; } } int AddDriveToListView(HWND hwndLV, int nDrive, int nDefaultDrive) { TCHAR szDriveName[64]; LV_ITEM item; HICON hicon = GetDriveInfo(nDrive, szDriveName); HIMAGELIST himlSmall = ListView_GetImageList(hwndLV, LVSIL_SMALL); Assert(himlSmall); if (hicon) { item.iImage = ImageList_AddIcon(himlSmall, hicon); DestroyIcon(hicon); } else item.iImage = 0; item.mask = nDrive == nDefaultDrive ? LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE : LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM; item.stateMask = item.state = LVIS_SELECTED | LVIS_FOCUSED; item.iItem = 26; // add at end item.iSubItem = 0; item.pszText = szDriveName; item.lParam = (LPARAM)nDrive; return ListView_InsertItem(hwndLV, &item); } int GetSelectedDrive(HWND hwndLV) { LV_ITEM item; item.iItem = ListView_GetNextItem(hwndLV, -1, LVNI_SELECTED); if (item.iItem >= 0) { item.mask = LVIF_PARAM; item.iSubItem = 0; ListView_GetItem(hwndLV, &item); return (int)item.lParam; } // implicitly selected the 0th item ListView_SetItemState(hwndLV, 0, LVIS_SELECTED, LVIS_SELECTED); return 0; } void InitSingleColListView(HWND hwndLV) { LV_COLUMN col = {LVCF_FMT | LVCF_WIDTH, LVCFMT_LEFT}; RECT rc; GetClientRect(hwndLV, &rc); col.cx = rc.right; // - GetSystemMetrics(SM_CXVSCROLL) // - GetSystemMetrics(SM_CXSMICON) // - 2 * GetSystemMetrics(SM_CXEDGE); ListView_InsertColumn(hwndLV, 0, &col); } #define g_cxSmIcon GetSystemMetrics(SM_CXSMICON) void CopyDiskInitDlg(HWND hDlg, DISKINFO *pdi) { int iDrive; HWND hwndFrom = GetDlgItem(hDlg, IDD_FROM); HWND hwndTo = GetDlgItem(hDlg, IDD_TO); HIMAGELIST himl; SetWindowLong(hDlg, DWL_USER, (UINT)pdi); SendMessage(hDlg, WM_SETICON, 0, (LPARAM)LoadImage(GetWindowInstance(hDlg), MAKEINTRESOURCE(IDI_DISKCOPY), IMAGE_ICON, 16, 16, 0)); SendMessage(hDlg, WM_SETICON, 1, (LPARAM)LoadIcon(GetWindowInstance(hDlg), MAKEINTRESOURCE(IDI_DISKCOPY))); pdi->hdlg = hDlg; InitSingleColListView(hwndFrom); InitSingleColListView(hwndTo); himl = ImageList_Create(g_cxSmIcon, g_cxSmIcon, ILC_MASK, 1, 4); if (himl) { // NOTE: only one of these is not marked LVS_SHAREIMAGELIST // so it will only be destroyed once ListView_SetImageList(hwndFrom, himl, LVSIL_SMALL); ListView_SetImageList(hwndTo, himl, LVSIL_SMALL); } for (iDrive = 0; iDrive < 26; iDrive++) { if (IsRemovableDrive(iDrive) && !IsCDRomDrive(iDrive)) { AddDriveToListView(hwndFrom, iDrive, pdi->nSrcDrive); AddDriveToListView(hwndTo, iDrive, pdi->nDestDrive); } } } DWORD _inline WaitForThreadDeath(HWND hThread) { MSG msg; if (hThread) { while(TRUE) { DWORD result = MsgWaitForMultipleObjects(1, &hThread, FALSE, 5000, QS_SENDMESSAGE); switch (result) { case WAIT_OBJECT_0: case WAIT_FAILED: return result; case WAIT_TIMEOUT: TerminateThread(hThread, (DWORD)-1); return result; case WAIT_OBJECT_0 + 1: PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); break; } } } } void SetCancelButtonText(HWND hDlg, int id) { TCHAR szText[80]; LoadString(g_hinst, id, szText, sizeof(szText)); SetDlgItemText(hDlg, IDCANCEL, szText); } void DoneWithFormat(DISKINFO* pdi) { int id; EnableWindow(GetDlgItem(pdi->hdlg, IDD_FROM), TRUE); EnableWindow(GetDlgItem(pdi->hdlg, IDD_TO), TRUE); #ifndef WINNT // unlock the drives LockDrive(pdi->nSrcDrive, FALSE, 0 ); if (pdi->nSrcDrive != pdi->nDestDrive) LockDrive(pdi->nDestDrive, FALSE, 0); #endif SendDlgItemMessage(pdi->hdlg, IDD_PROBAR, PBM_SETPOS, 0, 0); EnableWindow(GetDlgItem(pdi->hdlg, IDOK), TRUE); CloseHandle(pdi->hThread); SetCancelButtonText(pdi->hdlg, IDS_CLOSE); pdi->hThread = NULL; if (pdi->bUserAbort) { id = IDS_COPYABORTED; } else { switch (pdi->dwError) { case 0: id = IDS_COPYCOMPLETED; break; default: id = IDS_COPYFAILED; break; } } SetStatusText(pdi, id); SetCancelButtonText(pdi->hdlg, IDS_CLOSE); // reset variables pdi->dwError = 0; pdi->bUserAbort = 0; } #pragma data_seg(".text") const static DWORD aCopyDiskHelpIDs[] = { // Context Help IDs IDOK, IDH_DISKCOPY_START, IDD_FROM, IDH_DISKCOPY_FROM, IDD_TO, IDH_DISKCOPY_TO, IDD_STATUS, NO_HELP, IDD_PROBAR, NO_HELP, 0, 0 }; #pragma data_seg() BOOL CALLBACK CopyDiskDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { DISKINFO *pdi = (DISKINFO *)GetWindowLong(hDlg, DWL_USER); switch (uMsg) { case WM_INITDIALOG: CopyDiskInitDlg(hDlg, (DISKINFO *)lParam); break; case WM_DONE_WITH_FORMAT: DoneWithFormat(pdi); break; case WM_HELP: WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, NULL, HELP_WM_HELP, (DWORD)(LPTSTR) aCopyDiskHelpIDs); return TRUE; case WM_CONTEXTMENU: WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU, (DWORD)(LPVOID) aCopyDiskHelpIDs); return TRUE; case WM_COMMAND: switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: // if there's az hThread that means we're in copy mode, abort // from that, otherwise, it means quit the dialog completely if (pdi->hThread) { pdi->bUserAbort = TRUE; // do a Msgwaitformultiple so that we don't // get blocked with them sending us a message if (WaitForThreadDeath(pdi->hThread) == WAIT_TIMEOUT) DoneWithFormat(pdi); CloseHandle(pdi->hThread); pdi->hThread = NULL; } else EndDialog(hDlg, IDCANCEL); break; case IDOK: { DWORD idThread; SetLastError(0); EnableWindow(GetDlgItem(hDlg, IDOK), FALSE); // set cancel button to "Cancel" SetCancelButtonText(hDlg, IDS_CANCEL); pdi->nSrcDrive = GetSelectedDrive(GetDlgItem(hDlg, IDD_FROM)); pdi->nDestDrive = GetSelectedDrive(GetDlgItem(hDlg, IDD_TO)); pdi->bUserAbort = FALSE; SendDlgItemMessage(hDlg, IDD_PROBAR, PBM_SETPOS, 0, 0); SendDlgItemMessage(pdi->hdlg, IDD_STATUS, WM_SETTEXT, 0, 0); Assert(pdi->hThread == NULL); pdi->hThread = CreateThread(NULL, 0, CopyDiskThreadProc, pdi, 0, &idThread); } break; } break; default: return FALSE; } return TRUE; } int SHCopyDisk(HWND hwnd, int nSrcDrive, int nDestDrive, DWORD dwFlags) { DISKINFO di; memset(&di, 0, sizeof(di)); di.nSrcDrive = nSrcDrive; di.nDestDrive = nDestDrive; return DialogBoxParam(g_hinst, MAKEINTRESOURCE(DLG_DISKCOPYPROGRESS), hwnd, CopyDiskDlgProc, (LPARAM)&di); }