?? 小巧磁盤編輯器.txt
字號:
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 TForm1.StringGrid1KeyPress(Sender: TObject; var Key: Char);
begin //根據在網格中的按鍵,進行光標的定位
if Key<>#8 then begin
Key := UpCase(Key);
if not (Key in ['0'..'9','A'..'F']) then Key := #0 else //只允許輸入特定字符
with StringGrid1 do
if CellKeyPress>1 then begin
if Length(Cells[Col,Row])>1 then begin
if (Col+1+CharSellsCount)<ColCount then begin
Col := Col+1; //光標換列
CellKeyPress := 1;
end else begin
if (Row+1)<RowCount then begin
Col := 1; Row := Row+1; //光標換行
CellKeyPress := 1;
end else Key := #0;
end;
end;
end else Inc(CellKeyPress);
end;
if Key<>#0 then StatusBar1.Panels[1].Text := Format('修改邏輯磁盤 '+DriveComboBox1.Drive+'第 %D 扇區',[startsector.Value]); //,如進行扇區數據的修改,則在狀態欄中顯示修改信息
end;
procedure TForm1.StringGrid1GetEditText(Sender: TObject; ACol,
ARow: Integer; var Value: String); //根據在網格中選擇的位置改變記錄行列的ColOld ,
RowOld 變量的值.
var
S: String;
L: Integer;
begin
with StringGrid1 do begin
S := Cells[ColOld,RowOld];
L := Length(S);
case L of
0: Cells[ColOld,RowOld] := '00';
1: Cells[ColOld,RowOld] := '0'+S;
end;
end;
ColOld := ACol;
RowOld := ARow;
end;
procedure TForm1.StringGrid1Exit(Sender: TObject);
begin
with StringGrid1 do
if Col<16 then
Col := Col+1
else begin
Col := 1; Row := Row+1; //改變行列的值
end;
OnFocusChange; //當光標所在的行列變化時,刷新網格顯示.
end;
procedure TForm1.StringGrid1SelectCell(Sender: TObject; ACol,
ARow: Integer; var CanSelect: Boolean); //選擇網格數據時動態刷新網格內容
begin
if aCol>16 then CanSelect := False;
CellKeyPress := 0;
OnFocusChange;
end;
procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState); //當修改扇區數據時,對修改的部分進行醒目的顯示
var
i:integer;
begin
with StringGrid1.Canvas do
if (aCol>0) and (aRow>0) then begin
case aCol of
1..16: I := aCol-1+(aRow-1)*16;
18..33: I := aCol-18+(aRow-1)*16;
else I := -1;
end;
if I>=0 then
if seca[I]<>secb[I] then begin //對原始seca數據和變化的secb數據進行對比,不同則表示修改了扇區數據
Brush.Color := clred;
FillRect(Rect);
TextOut(Rect.Left+2,Rect.Top+2,StringGrid1.Cells[aCol,aRow]);
end; //重新以紅色顯示修改的數據
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
driver:=pchar('\\.\'+drivecombobox1.Drive+':');
BytesToGrid; //讀取扇區數據
end;
procedure TForm1.Button1Click(Sender: TObject); //將指定的扇區寫至選擇的邏輯盤的對應扇區位置
var
p:pchar;
i,j:integer;
writesec:string;
begin
if Form2.ShowModal <> mrOk then Exit; //顯示磁盤寫入的窗體
driver:=pchar('\\.\'+form2.DriveComboBox1.Drive+':'); //取得指定邏輯盤
p:=allocmem(n*bytepersectors);
setlength(writesec,n*bytepersectors); //動態設置字符串長度,用以保存扇區數據
writesec:='';
for i:=0 to ((n*bytepersectors) div 16)-1 do
for j:=1 to 16 do
writesec := writesec + chr(secb[j-1+16*i]); //將secb中的保存的扇區數據轉移至特定字符串中
p:=pchar(writesec);//將數據轉化為pchar類型
hDeviceHandle := CreateFile(driver, GENERIC_ALL,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING,0, 0); //打開目標盤
if (hDeviceHandle <> INVALID_HANDLE_VALUE) then
begin
FileSeek(hDevicehandle,form2.SpinEdit1.Value*bytepersectors,0); //扇區定位
if FileWrite(hDevicehandle,p[0],n*bytepersectors)<>n*bytepersectors then //寫入扇區數據,完成扇區寫操作1
raise exception.create('Write錯誤%d');
StatusBar1.Panels[1].Text :='寫邏輯磁盤 '+form2.DriveComboBox1.Drive+'成功!';
closehandle(hDeviceHandle); //關閉句柄
end;
end;
procedure TForm1.nsectorsChange(Sender: TObject);
begin
if nsectors.Value<1 then //改變扇區數量時進行判斷
begin
MessageBox(0, '扇區數不能為0!','小巧磁盤編輯器', MB_OK + MB_ICONERROR + MB_DEFBUTTON2 +
MB_TOPMOST);
nsectors.Value:=1;
end;
Button2Click(self);//改變網格數據
end;
procedure TForm1.startsectorChange(Sender: TObject);
begin
Button2Click(self); //起始扇區改變時,改變網格的內容
end;
end.
寫扇區的源碼如下:
unit wlt;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Spin, FileCtrl;
type
TForm2 = class(TForm) //寫扇區窗體
Label1: TLabel;
Button1: TButton;
Button2: TButton;
Label2: TLabel;
SpinEdit1: TSpinEdit;
DriveComboBox1: TDriveComboBox;
procedure FormShow(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.FormShow(Sender: TObject);
begin
spinedit1.Value:=0; //起始扇區數置0
end;
procedure TForm2.Button1Click(Sender: TObject);
begin
if MessageBox(0, '磁盤扇區寫入可能存在危險,是否繼續?',
'小巧磁盤編輯器', MB_YESNO + MB_ICONERROR + MB_DEFBUTTON2 +
MB_TOPMOST) = IDNO then
close;
end;
end.
當然這只是在windows2000/xp下讀寫扇區的的一般方法.還有一些需要進一步完善的地方.例如判斷磁盤的總扇區數,進行必要的約束等.限于篇幅就不一一列舉了.
測試環境: Dephi7+Windows2000,Ntfs文件系統.
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -