開いているファイルを強制的にクローズするWSHスクリプトを書いてみた

【Windowsプログラミング】プログラムから他のプロセスが使っているファイルを削除する方法を教えてください。 (方法はなんでもOkですが、Windows上で動作するのが条件です.. - 人力検索はてな
の件に関連して。JScriptで書いてみた。


実際にはHandle.exeのラッパーオブジェクトHandleとしての実装。Handle.exeをスクリプトファイルと同じディレクトリに放り込んでおく必要がある。Handle v3.42で動作確認。

Handle.exec(options)
    指定された引数でHandle.exeを実行。戻り値はObjectオブジェクトで、errorLevelにエラーレベル、outputに標準出力への実行結果が入っている。
Handle.enumOpenHandles(fileName)
  指定されたファイル名をオープンしているプロセス一覧を取得する。戻り値はOpenHandleInfoオブジェクトの配列で、processNameにオープンしているプロセス名、pidにオープンしているプロセスID、handleにオープンハンドルが入っている。
Handle.closeOne(pid, handle)
指定されたプロセスIDのオープンハンドルをクローズする。
Handle.closeAll(fileName)
指定されたファイル名のすべてのオープンハンドルをクローズする。


クローズは、実行すると有無を言わさず問答無用で強制クローズするので間違ってもシステムが開いているファイルとかを指定しないように。何が起きても知らないよ。実際のところ、handle.exeはフルパスの一部でも受け入れちゃうので、*.txtとか指定しちゃうとあらゆるオープン中のテキストファイルを強制クローズしちゃったりするので要注意。フルパス推奨。
JavaScriptはまだまだhack力が足りないのでかっこいい書き方じゃないのはご容赦。


テストプログラム
引数で指定したファイルを開いているプロセスを一覧表示する。(cscript xxx.js <ファイル名>で実行)


if(WScript.arguments.length == 0){
WScript.echo("引数にファイル名(フルパス)を指定してください。");
WScript.quit(1);
}

fileName = WScript.arguments.item(0);

handle = new Handle();

opener = handle.enumOpenHandles(fileName);
if(opener == null){
WScript.echo(fileName + "はオープンされていません。")
WScript.quit(2);
}

for(i = 0; i < opener.length; i++){
WScript.echo("[" + opener[i].pid + "]" + opener[i].processName + "/" + opener[i].handle);
}

// if(true)にすると強制クローズするサンプル。
if(false){
if(handle.closeAll(fileName)){
WScript.echo(fileName + "のすべてのオープンハンドルを強制クローズしました。");
} else {
WScript.echo(fileName + "のオープンハンドルの強制クローズに失敗しました。");
WScript.quit(3);
}
}

WScript.quit(0);


Handleオブジェクトのソース


function Handle(exePath)
{
var fso = WScript.createObject("Scripting.FileSystemObject");

this.handlePath = fso.getParentFolderName(WScript.scriptFullName) + "\\handle.exe";
if(exePath){
this.handlePath = exePath;
}

fso = null;

this.exec = function(options)
{
var cmdLine;
var result = new Object;
var TemporaryFolder = 2;
var ForReading = 1;
var WshShell = WScript.createObject("WScript.Shell");
var fso = WScript.createObject("Scripting.FileSystemObject");
var tempFolder = fso.getSpecialFolder(TemporaryFolder);
var tempFileName = tempFolder.path + "\\" + fso.getTempName();

// Invoke handle.exe thru the command prompt.
cmdLine = "%COMSPEC% /c \"" + this.handlePath + "\" " + options + " > " + tempFileName;
result.errorLevel = WshShell.run(cmdLine, 7, true);
WshShell = null;

result.output = new Array;
file = fso.openTextFile(tempFileName, ForReading, false);

// Skip header lines
file.skipLine();
file.skipLine(); // Version info
file.skipLine(); // Copyright notice
file.skipLine(); // URL
file.skipLine();

// Read other lines
while(!file.atEndOfStream){
result.output.push(file.readLine());
}

file.close();
file = null;

fso.deleteFile(tempFileName);
fso = null;

return result;
};

this.enumOpenHandles = function(fileName)
{
var file;
var handleInfo;
var handleInfoArray;

result = this.exec(fileName);
if(result.errorLevel != 0){
return null;
}
if(result.output[0] == "No matching handles found."){
return null;
}

handleInfoArray = new Array;
while(result.output.length != 0){
line = result.output.shift();
handleInfo = new OpenHandleInfo();
handleInfo.analyze(line);
handleInfoArray.push(handleInfo);
}

return handleInfoArray;
};

this.closeOne = function(pid, handle)
{
var cmdLine;
var result;

cmdLine = "-c " + handle + " -p " + pid + " -y";
result = this.exec(cmdLine);

return (result.errorLevel == 0);
};

this.closeAll = function(fileName)
{
var opener;
var handleInfo;

opener = this.enumOpenHandles(fileName);
if(!opener){
return false;
}

while(opener.length > 0){
handleInfo = opener.shift();
if(!this.closeOne(handleInfo.pid, handleInfo.handle)){
return false;
}
}

return true;
};

}

function OpenHandleInfo()
{
this.processName = "";
this.pid = "";
this.handle = "";

this.analyze = function(line)
{
var n;

n = line.indexOf("pid:");
this.processName = trim(line.slice(0, n));
line = trim(line.slice(n + 4));

n = line.indexOf(" ");
this.pid = line.slice(0, n);
line = line.slice(n);

n = line.indexOf(":");
this.handle = trim(line.slice(0, n));
};

function trim(str){
return str.replace(/^[ ]+|[ ]+$/g, '');
};

}


所要時間:だいたい3時間