C++Builder: Работа с дисками на низком уровне


Эта статья поможет вам разобраться с механизмом работы с дисками на низком уровне в Windows 9x/NT. На оригинальность статьи не претендую, т. к. нижеизложенный код - немного изменённое переложение части исходника для Visual C++, взятого с codeguru.com, на C++Builder.

В качестве вступления: в Windows линии NT низкоуровневая работа с диском упрощена: достаточно получить дескриптор устройства с помощью функции CreateFile, и с диском можно работать как с файлом, используя стандартные функции ReadFile/WriteFile.

К примеру, чтобы получить дескриптор дисковода A: для чтения, достаточно сделать следующий вызов:
HANDLE hFile=CreateFile("\\\\.\\a:",GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,
			  NULL,OPEN_EXISTING,0,NULL);
//И всё, можно работать
CloseHandle(hFile);

В Windows линии 9x этот подход не реализован: здесь нужно использовать стандартный драйвер vwin32.vxd, функции которого для работы с диском аналогичны соответствующим прерываниям MS-DOS. Дескриптор к драйверу получим с помощью функции CreateFile аналогично получению дескриптора диска в WinNT, для работы с драйвером задействуем функцию DeviceIoControl.

Как всегда, создаём новое приложение. Форма может выглядеть так:

disk_form.jpg; 16,6 kb
Рис 1. Форма будущей программы

Входные данные (номер диска, первый логический сектор, количество секторов для чтения) получаем через поля ввода. С помощью RadioGroup устанавливаем формат вывода считанных данных. Вся информация будет отображаться в компоненте класса TMemo.

Весь код заключён в обработчике нажатия на кнопку:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int drive=StrToIntDef(DriveNumberEdit->Text,-1),
    startinglogicalsector=StrToIntDef(StartingSectorEdit->Text,-1),
    numberofsectors=StrToIntDef(NumberSectorsEdit->Text,-1);

if(drive==-1 || startinglogicalsector==-1 || numberofsectors==-1)
  {
  MessageBox(Handle,"Error input parameters.",NULL,MB_OK|MB_ICONINFORMATION);
  return;
  }

if(!numberofsectors)
  {
  MessageBox(Handle,"You must specify at least 1 number of sectors to read.",
		NULL,MB_OK|MB_ICONINFORMATION);
  return;
  }

PBYTE buff=(PBYTE)malloc(numberofsectors*512); //для чтения секторов

#pragma pack(1)
struct
  {
        DWORD StartingSector;
        WORD NumberOfSectors;
        DWORD pBuffer;
  } ControlBlock;
#pragma pack()

#pragma pack(1)
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;
#pragma pack()

DIOC_REGISTERS reg;

OSVERSIONINFO vi;
vi.dwOSVersionInfoSize = sizeof vi; // это обязательно
GetVersionEx(&vi);
BOOL NT=(vi.dwPlatformId==VER_PLATFORM_WIN32_NT); //проверка на соответствие NT-платформе
HANDLE hFile;

if(!NT)
  {
  //используем драйвер vwin32
  hFile=CreateFile("\\\\.\\vwin32",0,0,NULL,0,FILE_FLAG_DELETE_ON_CLOSE,NULL);
  if(hFile==INVALID_HANDLE_VALUE)
    {
    MessageBox(Handle,"Can't get handle of vwin32.vxd.",NULL,MB_OK|MB_ICONSTOP);
    return;
    }
  ControlBlock.StartingSector=startinglogicalsector;
  ControlBlock.NumberOfSectors=numberofsectors;
  ControlBlock.pBuffer=(DWORD)buff;
  // в SI помещаем: 0 - для чтения или 1 - для записи
  // CX должно быть равно FFFFh для расширения 7305h прерывания int 21h
  // DS:BX -> адрес структуры ControlBlock
  // DL - номер диска (01h=A:, 02h=B: etc)
  reg.reg_ESI=0;
  reg.reg_ECX=-1;
  reg.reg_EBX=(DWORD)&ControlBlock;
  reg.reg_EDX=drive+1;
  reg.reg_EAX=0x7305;
  DWORD cb;
  // 6 == VWIN32_DIOC_DOS_DRIVEINFO - вызываемая функция
  BOOL result=DeviceIoControl(hFile,6,&reg,sizeof reg,&reg,sizeof reg,&cb,0);
  if(!result || (reg.reg_Flags & 0x0001))return; //произошла ошибка
  }
else
  {
  //WinNT
  DWORD bytesread;
  char drive_name[] = "\\\\.\\a:";
  drive_name[4] += drive;
  hFile=CreateFile(drive_name,GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,
			NULL,OPEN_EXISTING,0,NULL);
  if(hFile==INVALID_HANDLE_VALUE)
    {
    MessageBox(Handle,"Can't get handle of disk drive.",NULL,MB_OK|MB_ICONSTOP);
    return;
    }
  SetFilePointer(hFile,512*startinglogicalsector,0,FILE_BEGIN);
  if(!ReadFile(hFile,buff,numberofsectors*512,&bytesread,NULL))return;
  }

CloseHandle(hFile);

Memo1->Clear();
AnsiString str="";
if(RadioGroup1->ItemIndex)
  for(int i=0; i<numberofsectors*512; ++i)
    str=str+IntToHex(buff[i],2)+" ";
else
  {
  MessageBox(Handle,"You selected TEXT output mode.\nAll symbols ASCII 0 will be replaced with spaces.",
		NULL,MB_OK|MB_ICONINFORMATION);
  //для того отобразить в Memo символы, следующие за символами с кодом 0
  for(int i=0; i<numberofsectors*512; ++i)
    if(!buff[i])buff[i]=0x20;
  str=(char*)buff+'\0';
  }
Memo1->Lines->Add(str);

free(buff);
}

Это приложение ориентировано на чтение, но в него несложно добавить возможность записи. Если вы собираетесь записывать что-то на диск, то советую экспериментировать только с дискетами, но ни в коем случае не с жёстким диском - ошибка в программе подобного рода или некорректное применение этой программы может повлечь потерю большей части всех ваших файлов (например, если будут испорчены FAT-таблицы).


Hosted by uCoz