Window2000/Xp下的小巧磁盘编辑器
提起磁盘编辑器,我们就会想到Winhex,Advanced Disk Catalog ,Edittool等常用软件.如果能自己动手制作一个类似的磁盘工具,那将是很有趣的.这里就使用Dephi7在Window2000/2003/Xp环境下设计一款小巧的磁盘编辑器,可以在Fat32,Ntfs文件系统在正常工作.可以实现磁盘扇区的读取,修改,复制等常用功能.程序运行效果如图1所示.
在Dephi新建一个项目,在窗体上放置一个stringgrid控件,用来显示和修改磁盘扇区的内容.两个按钮,分别用来读写相应扇区.两个spinedit控件,用来设置起始扇区和读写扇区的数目.一个DriveComboBox控件,用来选择磁盘.statusbar状态条用来显示当前状态.
程序的关键是利用CreateFile(drive, GENERIC_ALL, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING,0, 0)函数来打开需要读写的驱动器,然后利用ReadFile,WriteFile来进行磁盘读写。程序的核心是如何动态显示和修改磁盘扇区的内容.这里使用了两个整数型的数组seca和secb分别用来记录扇区的原始内容和修改后的内容.不管是否修改了扇区内容,在对磁盘进行写操作时,把数组secb作为数据源来提供扇区写入所需的数据.当然不点击"写扇区"按钮的话,一切操作都是安全的.
程序中用来在stringgrid中显示扇区内容的代码如下:
procedure tform1.BytesToGrid; //此过程将扇区内容显示在stringgrid中.这里的stringgrid的option属性应包括:goFixedVertLine, goFixedHorzLine, goRangeSelect, goEditing, goTabs, goAlwaysShowEditor.
var
i,j,k:Integer;
c: Char;
fbuf:pchar;
begin
n:=nsectors.Value; //读写的扇区数
s:=startsector.Value;//起始扇区数
setlength(seca,n*bytepersectors);
setlength(secb,n*bytepersectors); //动态设置数组的长度,seca和secb分别记录原始和变化后的扇区数据
StatusBar1.Panels[1].Text := '';
hDeviceHandle := CreateFile(driver, GENERIC_ALL, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING,0, 0);//打开磁盘
if (hDeviceHandle<> INVALID_HANDLE_VALUE) then //是否打开成功
begin
fbuf:=allocmem(n*bytepersectors);
FileSeek(hDevicehandle,s*bytepersectors,0); //定位扇区
if FileRead(hDevicehandle,fbuf[0],n*bytepersectors)<>n*bytepersectors then
raise exception.create('读磁盘错误!');
stringgrid1.Rowcount:=n*32+1;//根据选择的扇区数动态的改变网格的行数
for i:=0 to ((n*bytepersectors) div 16)-1 do
StringGrid1.Cells[0,i+1] := IntToHex(i*16,4)+':'; //格式化显示stringgrid的0行表头信息
for i:=0 to ((n*bytepersectors) div 16)-1 do
for j:=1 to 16 do
begin
seca[j-1+16*i]:=integer(fbuf[j-1+16*i]);//将扇区数据转换为整型
secb[j-1+16*i]:=seca[j-1+16*i]; //初始扇区数组数据
end;
for i:=0 to ((n*bytepersectors) div 16)-1 do begin
StringGrid1.Cells[0,i+1] := IntToHex(i*16,4)+':'; //格式化显示stringgrid的0列内容
for j:=1 to 16 do begin
K := seca[j-1+16*i];
StringGrid1.Cells[j,i+1]:=format('%.2x',[integer(fbuf[j-1+16*i])]);//在stringgrid的1至16列格式化数据显示
C :=chr(k);
if c<' ' then c:='.';
StringGrid1.Cells[j+17,i+1] := c; //在stringgrid18至33列显示对应的asc码
StatusBar1.Panels[1].Text := Format('逻辑磁盘 '+DriveComboBox1.Drive+'第 %D 扇区起连续'+inttostr(n)+'个扇区',[s]);
end;
end;
freemem(fbuf);//释放内存
closehandle(hDeviceHandle); //关闭句柄
end;
end;
程序的完整源代码如下:
unit diskedirunt;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Spin, shellapi,FileCtrl, Grids, ComCtrls;
type
TForm1 = class(TForm)
DriveComboBox1: TDriveComboBox;
Button1: TButton; //写扇区按钮
startsector: TSpinEdit;
nsectors: TSpinEdit;
Label1: TLabel;
Label2: TLabel;
StatusBar1: TStatusBar;
StringGrid1: TStringGrid;
Button2: TButton; //读扇区按钮
Label3: TLabel;
procedure FormShow(Sender: TObject);
procedure DriveComboBox1Change(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure StringGrid1KeyPress(Sender: TObject; var Key: Char);
procedure StringGrid1GetEditText(Sender: TObject; ACol, ARow: Integer;
var Value: String);
procedure StringGrid1Exit(Sender: TObject);
procedure StringGrid1SelectCell(Sender: TObject; ACol, ARow: Integer;
var CanSelect: Boolean);
procedure StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
procedure Button2Click(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure nsectorsChange(Sender: TObject);
procedure startsectorChange(Sender: TObject);
private
driver:pchar; //驱动器名称
bytepersectors:integer; //每扇区字节数,默认为512
s,n:integer;
seca,secb:array of integer; //分别保存原始和变化的扇区数据
procedure BytesToGrid;
procedure OnFocusChange;
{ Private declarations }
public
{ Public declarations }
end;
var
ColOld: Integer = 1; //根据用户的在stringgrid的选择改变光标所在的行列数
RowOld: Integer = 1;
CellKeyPress: Integer; //判断在stringgrid中的按键
CharSellsCount:integer =17;
hdevicehandle:thandle; //文件句柄
Form1: TForm1;
implementation
uses wlt;
{$R *.dfm}
procedure TForm1.OnFocusChange; //在stringgrid里改变光标位置时动态刷新数组的内容
var
I: Integer;
C: Char;
begin
I := ColOld-1+(RowOld-1)*16;
with StringGrid1 do
if Length(Cells[ColOld,RowOld])>2 then begin //没有修改则显示seca中的原始数据
Cells[ColOld,RowOld] := IntToHex(seca[i],2);
end else
try
secb[i] := StrToInt('$'+Cells[ColOld,RowOld]); //保存修改后的扇区数据到secb数组中
C := Chr(secb[i]);
if c<' ' then c:='.';
Cells[ColOld+17,RowOld] := C;
except; end;
end;
procedure tform1.BytesToGrid; //在stringgrid中动态显示扇区的内容
var
i,j,k:Integer;
c: Char;
fbuf:pchar;
begin
n:=nsectors.Value;
s:=startsector.Value;
setlength(seca,n*bytepersectors);
setlength(secb,n*bytepersectors);
StatusBar1.Panels[1].Text := '';
hDeviceHandle := CreateFile(driver, GENERIC_ALL, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING,0, 0);
if (hDeviceHandle<> INVALID_HANDLE_VALUE) then
begin
fbuf:=allocmem(n*bytepersectors);
FileSeek(hDevicehandle,s*bytepersectors,0);
if FileRead(hDevicehandle,fbuf[0],n*bytepersectors)<>n*bytepersectors then
raise exception.create('读磁盘错误!');
stringgrid1.Rowcount:=n*32+1;
for i:=0 to ((n*bytepersectors) div 16)-1 do
StringGrid1.Cells[0,i+1] := IntToHex(i*16,4)+':';
for i:=0 to ((n*bytepersectors) div 16)-1 do
for j:=1 to 16 do
begin
seca[j-1+16*i]:=integer(fbuf[j-1+16*i]);
secb[j-1+16*i]:=seca[j-1+16*i];
end;
for i:=0 to ((n*bytepersectors) div 16)-1 do begin
StringGrid1.Cells[0,i+1] := IntToHex(i*16,4)+':';
for j:=1 to 16 do begin
K := seca[j-1+16*i];
StringGrid1.Cells[j,i+1]:=format('%.2x',[integer(fbuf[j-1+16*i])]);
C :=chr(k);
if c<' ' then c:='.';
StringGrid1.Cells[j+17,i+1] := c;
StatusBar1.Panels[1].Text := Format('逻辑磁盘 '+DriveComboBox1.Drive+'第 %D 扇区起连续'+inttostr(n)+'个扇区',[s]);
end;
end;
freemem(fbuf);
closehandle(hDeviceHandle);
end;
end;
procedure TForm1.FormShow(Sender: TObject);
begin
driver:=pchar('\\.\'+drivecombobox1.Drive+':');//在formshow中设置初始驱动器值
BytesToGrid;
end;
procedure TForm1.DriveComboBox1Change(Sender: TObject);
begin
Button2Click(self);//改变驱动器时,刷新网格显示
end;
procedure TForm1.FormCreate(Sender: TObject);
var
i:integer;
begin
startsector.Value:=0;
nsectors.Value:=1;
bytepersectors:=512;
with StringGrid1 do begin //网格分两部分,一边以16进制显示扇区数据,一边显示相应的asc值.
ColWidths[0] := 50;
for i := 17 to 33 do begin
TabStops[i] := False;
ColWidths[i] := 11; //设置网格列的宽度
end;
for i := 1 to 16 do Cells[i,0] := IntToHex(i-1,2); //设置表头信息
for i := 18 to 33 do Cells[i,0] := IntToHex(i-18,1);
Cells[0,0] := '偏移';
end;
end;
procedure TFor