「ファイル完全削除ソフトウェア」という便利なソフトウェアを実装しながら、RustでWindowsアプリを作成する方法を解説します。具体的には、ファイルのドラッグ&ドロップ機能やプログレスバー、Windows標準ダイアログを活用して、ユーザーフレンドリーなアプリを設計することを目指します。
この記事で作成している「ファイル完全削除ソフトウェア」は、「完全削除」の原理を解説するものであり、完全なデータセキュリティを保証するものではありません。もし、あなたが企業のデータ管理担当者なのであれば、専門企業の有料サービスを利用することを強く推奨します。
この記事で使用したコードは、以下のリポジトリに公開されています。
このリポジトリはworkspace
となっており、完全削除のコアの部分であるfile_destroy
と、Windowsアプリであるfile_destroyer
の2つを含んでいます。
ファイル完全削除ソフトの作り方
Rustを用いて、ファイルを完全に削除するソフトウェアを作る方法を説明します。
ここでは動作OSを限定せず、コマンドラインで動作するソフトウェアを作ることを目指します。
ファイルを完全に削除することの意味
わざわざ「ファイルを完全に削除する」という表現を使うからには、普通の「削除」は不完全なものであるはずです。
ここでは、いわゆる「ゴミ箱」に入れて消去する場合と、ソフトウェアを用いた「完全削除」の違いについて簡単に解説します。すでに原理を理解している方は、読み飛ばしていただいて構いません。
いわゆる普通の「削除」
用語が非常にわかりにくいですが、コンピュータ上でマウスを用いてポチポチとファイルを削除する手順はすべてこちらに含まれます。たとえば
- ファイルをゴミ箱に入れ、「ゴミ箱を空にする」を選択
- USBメモリのファイルを右クリックから削除して、「完全に削除しますか?」に「はい」を選択
などの動作でも「完全に削除」という表現を使いますが、これらは情報セキュリティの観点からは不完全な削除に過ぎません。その仕組みは以下の通りです。
(または、以下のサイトを参照してください)

ファイルの情報は、記憶メディア(HDD、SSDなど)の中に保存されています。記憶メディアには管理領域と保存領域があり、ファイルを構成する情報(文書なら文字、画像ならピクセル情報など)は保存領域に記録されています。一方、管理領域は、そのファイルが保存領域のどこに記録されているか、という情報を記録しています。
ここで、ファイルを「削除」しても、保存領域のファイルの情報はしばらく消えません。代わりに、管理領域に「このファイルは削除済みです」という情報が追記されます。
こうしてファイルは消えたことになっていますが、保存領域の情報はしばらく残り続けます。さらに、管理領域の情報も消えたわけではなく、「削除済み」と追記されただけで、保存場所の情報は残っています。
したがって、専用のソフトウェアを使用することで、このファイルを簡単に復元することができてしまいます。
ファイル破壊による「完全削除」
ファイルが復元されないようにするためには、情報を「消す」のではなく「破壊する」必要があります。保存領域の情報が壊れていれば、ファイル復元ソフトを使用しても、意味をなさないファイルしか取得することができません。
ファイル削除ソフトウェアでは、ファイルを「削除」する前に、ファイルの内容を何度もランダムな情報で上書きするという方法でファイルを破壊しています。
つまり、公的文書の黒塗りや、モザイク処理と似たような原理です。
Rustでファイルの上書き処理を実装する
コマンドライン上でファイルを完全削除するソフトウェアとして、file_destroy
を作成しました。
以下は、そのコアとなる上書き処理部です。
for _ in 0..quality {
let data: Vec<u8> = (0..file_size).map(|_| rand::random::<u8>()).collect();
let _ = file.seek(SeekFrom::Start(0));
match file.write_all(&data) {
Ok(_) => Ok(()),
Err(e) => {println!("{}", e); Err(DestroyError::DataNotWritten)},
}?;
}
Rustでファイルを上書きするためには、ファイル内容と同じ長さのu8
配列を使用します。これをランダムな値で初期化し、削除したいファイルにwrite_all
で書き込みます。このとき、seek(SeekFrom::Start(0))
で書き込み開始位置をファイルの先頭に持ってくることで、ファイル全体をランダムな情報で置換することができます。
以上の動作を、ユーザーが設定できる変数quality
の回数だけ繰り返します。
Rustでプログレスバーを表示
削除したいファイルのサイズが大きくなると、上書き処理にもそれなりに時間がかかります。ここでは、ユーザーに進行状況を伝えるため、プログレスバーを表示することにします。
Rustでプログレスバーを表示するためには、indicatif
クレートを使用します。以下は、このクレートのProgressBar
とProgressStyle
を用いて、上書き処理にプログレスバーを追加したコードです。
let pb = ProgressBar::new(quality as u64);
pb.set_style(ProgressStyle::default_bar()
.template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta})")
.unwrap()
.progress_chars("#>-"));
for _ in 0..quality {
let data: Vec<u8> = (0..file_size).map(|_| rand::random::<u8>()).collect();
let _ = file.seek(SeekFrom::Start(0));
match file.write_all(&data) {
Ok(_) => Ok(()),
Err(e) => {println!("{}", e); Err(DestroyError::DataNotWritten)},
}?;
pb.inc(1);
}
ProgressBar
を用いて、長さがquality
のプログレスバーを作成し、詳細なスタイルはProgressStyle
で指定します。
上書き処理の繰り返しが1回終了するごとに、pb.inc(1)
としてプログレスバーを1つ進めることで、進行状況を表示しています。
RustでWindowsアプリを作る
file_destroy
のmain.rs
では、コマンドラインでファイルを完全削除するようにアプリを設計しています。しかし、削除したいファイルの名前を打ち込む必要があったり、ファイルを1つずつしか処理できなかったりするのは面倒です。
ここでは、Windows環境で動作するアプリを作成して使い勝手を向上させます。具体的には、アプリのアイコンにファイルをまとめてドラッグ&ドロップすると、それらのファイルを一括で完全削除するように改良します。このアプリのコードはfile_destroyer
として公開しています。
Rustの実行ファイルをビルドする
実行ファイル(アプリのこと。Windowsならexe
ファイル)を作成するためには、以下のコマンドを実行します。
...\\complete_deletion\\file_destroyer> cargo build --release
--release
を付けるとアプリを高速化することができます。また、--release
を付けた場合には、実行ファイルは
...\\complete_deletion\\target\\release\\file_destroyer.exe
の場所に作成されます。
実行ファイルのアイコンにファイルをドロップしたときの挙動
使い勝手を考えて、この実行ファイルのショートカットをデスクトップ上に作成します。ショートカットにファイルをドラッグ&ドロップすると、それらのファイルを完全削除します。
Rust製のアプリに限った話ではありませんが、たとえばfile_destroyer
のアイコンにA.png
, B.jpg
というファイルをドロップする操作は、コマンドライン上で
> file_destroyer.exe A.png B.jpg
と入力したのと等しくなります。つまり、ドロップしたファイルの名前は、コマンドライン引数として受け取ることができます。
use std::{env, format};
use file_destroy::destroy;
// ...
let args: Vec<String> = env::args().collect();
let n = args.len();
for i in 1..n {
let file_path = args[i].as_str();
match destroy(file_path, 64, true) {
Ok(_) => { println!("✅{} was successfully deleted.", file_path); }
Err(e) => {
eprintln!("❌{} was failed to delete.", file_path);
eprintln!("Error: {}", e);
}
}
}
コマンドライン引数はstd::env::args()
で取得します。コマンドライン引数の先頭args[0]
には、実行ファイルの名前(今回はfile_destroyer.exe
)が入るので、完全削除の対象となるファイル名はargs[1]
以降に存在します。
RustでWindowsのダイアログを表示する
上記のコードでは、ファイルをドロップせずにアプリのアイコンをクリックすると、クラッシュしてしまいます。また、ファイルをドロップした瞬間に完全削除が始まるため、間違えても取返しがつかなくなるので危険です。
そこで、不適切な操作が行われた際はアプリの使い方を説明し、ファイルがドロップされた場合は「本当に削除しても良いか?」を確認することにしましょう。このアプリはWindows上で動作させることを想定しているため、これらのメッセージを表示するためにWindowsダイアログを使用します。
use windows::{core::*, Win32::UI::WindowsAndMessaging::*};
// ...
let args: Vec<String> = env::args().collect();
let n = args.len();
if n <= 1 {
unsafe { MessageBoxW(None,
w!("Please drop the file you want to delete onto the icon."),
w!("No file has been specified."),
MB_OK); }
return
} else {
let message = if n == 2 {
format!("Do you really want to delete '{}'?", args[1])
} else {
format!("Do you really want to delete these {} files?", n - 1)
} + " Deleted files cannot be restored.";
unsafe {
match MessageBoxW(None,
&HSTRING::from(message),
w!("Notice!"),
MB_OKCANCEL) {
MESSAGEBOX_RESULT(1) => {}
MESSAGEBOX_RESULT(_) => { return; }
}
}
}
Windowsの機能をRustから操作するためには、windows
クレートを使用します。
ダイアログの表示にはMessageBoxA
やMessageBoxW
が使えます。これらの違いは、A
がマルチバイト文字(8bit単位)、W
がワイド文字(16bit単位)を表現していることです。ここではワイド文字を使用しています。
表示する文字列の設定
MessageBoxW
の定義は次の通りです。
pub unsafe fn MessageBoxW<P0, P1, P2>(
hwnd: P0,
lptext: P1,
lpcaption: P2,
utype: MESSAGEBOX_STYLE,
) -> MESSAGEBOX_RESULT
where
P0: Param<HWND>,
P1: Param<PCWSTR>,
P2: Param<PCWSTR>,
hwnd
は「所有者ウインドウへのハンドル」というものですが、とりあえずNone
にしておきます。
ダイアログに表示される文章はlptext
とlpcaption
に指定します。ここで、実際のダイアログではlpcaption
の方が上に表示されるので注意してください。
これらの引数はString
や&str
ではなく、ワイド文字をあらわすParam<PCWSTR>
で指定します。この型を取得するためには、w!
マクロを使用することができます(MessageBoxA
に対応するマルチバイト文字列の場合は、s!
マクロを使う)。
ただし、これらのマクロはフォーマッティングに対応していません。そのため、表示内容に変数を用いることができません。
ここで、ファイルが1つだけドロップされた際に、「本当に[ファイル名]を削除してもいいですか?」という警告を発することを考えます。表示する内容はドロップされたファイル名によって変化するため、w!
マクロを使用することができません。
そこでfile_destroyer
のコード中では、HSTRING
を使って対応しています。
let message = format!("Do you really want to delete '{}'? Deleted files cannot be restored.", args[1]);
unsafe {
MessageBoxW(
None,
&HSTRING::from(message),
w!("Notice!"),
MB_OKCANCEL
);
}
HSTRING
はFrom<&str>
やFrom<String>
を実装しています。また、&HSTRING
はPCWSTR
として使用することができるため、非常に便利です。
ダイアログボタンの設定と出力

MB_OKCANCELを指定した場合
ダイアログに表示するボタンはutype
引数に指定します。
たとえばMB_OK
を指定すると「OK」ボタンのみを表示し、MB_OKCANCEL
の場合は「OK」と「キャンセル」のボタンを表示します。
「本当に削除しますか?」の問いに対し、ユーザーの返答によって完全削除の続行と中止を切り替えるためには、MessageBoxW
の出力を利用します。筆者が試したところによると、「OK」が押された場合には1
、「キャンセル」が押されるか、ダイアログが「×」で閉じられた場合には2
が返されるようです。
「何かキーを押すと終了します」の実装
確認ダイアログで「OK」が押されると、完全削除を開始します。処理中は進行状況を表示します。
その後、処理の完了と同時にアプリが閉じてしまうと、最終的な結果を落ち着いて確認することができません(ファイルが別の場所で開かれているなどの原因で、削除に失敗することもあります)。
file_destroyer
では、以下のコードによって、処理完了後にキー入力を待ち受け、「何かキーを押すと終了します」という機能を実装しています。
println!("Press any key to quit.");
let mut word = String::new();
std::io::stdin().read_line(&mut word).ok();
まとめ
Rustの便利なクレートや、Windowsの標準機能を用いて、ユーザーフレンドリーなアプリを作成する方法を解説しました。RustからWindowsの機能を使用する場合、MessageBoxW
などはunsafe
ブロックになってしまうのが難点ですが、うまく利用することで操作性の高いアプリを作ることができます。
もっと知りたいこと、感想を教えてください!