他アプリケーションによる使用中で、削除できないファイルを、強制的に削除するには、どうしたらよいのでしょうか?
よくある強制削除ツールの場合ですと、こんな感じです。
http://yebisuya.dip.jp/WinTips/tips4.html
OSの管理下では難しいと思います、てか見たこと無いです。
monaaさん、御回答どうもありがとうございます。
アドレス先も勉強になりますが、やはり、再起動せずに消せればと思いまして・・・
たとえば、ベクターのフリーソフトでも、強制削除ツールがいくつかありまして、普通は捨てられないロックがかかったファイルも、ゴミ箱に捨てる感覚で、捨てられたりします。
どなたかお知恵をくだされば幸いです・・・
世に出回ってる強制削除ツールは再起動必須ですよ。
再起動不要のアプリケーションが存在するなら教えてください。
こんなの使って、ロック解除してやればいいのでは?
unlocker
ttp://ccollomb.free.fr/unlocker/
・・・ロック解除する方法を知りたいってことのような気がするけど、それは知りません(^^;)
>ダメ吉さん
簡単に言えば、その「他アプリケーション」を終了させるか、
終了させないまでも、そのプロセスが開いてるハンドルを閉じてもらう必要があります。
どのプロセスがファイルをロックしているかはZwQuerySystemInformationという非公開APIを使います。
あとは「ZwQuerySystemInformation 強制削除」で検索をかければサンプルが出てくると思いますが、
最終的にはTerminateProcessで開いてるプロセスを強制終了するか、
CreateRemoteThreadで相手プロセス内で強制的にCloseHandleを呼ぶことになります。
しかしあまり安全ではないので、他の方法で回避できるならそれに越したことはないです。
また、ロックプロセスの特定から強制削除までは非公開APIのオンパレードですので、
ある程度のスキルが求められますし、逆に知識のない状態で使うのは危険です。
(プロセスの暴走などを招いてしまう恐れがあるので)
>monaa
そんなことないですよ。
上記の通り方法がありますし、それを実装したソフトもいくつかあります。
いずれも再起動は不要です。
もちろん安全に削除するには再起動する方法が一番だと思います。
すいません、非公開なのはZw系APIじゃなくてNt系APIでした^^;
(NtQueryInformationFileなど)
このNtQueryInformationFileなどの非公開APIは、
ドライバ開発に用いるDDKでZwQueryInformationFile等として宣言されていて、
どちらも名前が違うだけで引数などは同じなので、説明はZw系を参照するといいと思います。
また、それらのAPIをDelphiで使用するには、JEDIで公開されているユニットを使うと楽です。
ファイルを掴んでいるプロセスを強制終了と
ファイルを強制削除では意味が違います。
>jazzinさん
もちろん強制終了でも構いませんが、
私の言いたいところはそれがOSであれアプリケーションであれ、
ロックしたファイルを持つプロセスを一度閉じる必要があるということです。(再起動って単語が直前のリンクと絡んでOSの再起動のみを意味するように見えますね)
どんな低レベルAPIでもその手順を飛ばすことはできないと思います、
これが可能だとOS起動中にOS管理下のファイルが書き換えられるようになるので、重大なセキュリティホールとなります。
それにしても、今晩は某国からのネットテロで某巨大掲示板が使えない…
カナシス。
FATなら FAT と Directory をいじれば削除可能だと思うけど(NTFSは知らない)
仮に FAT と Directory を削除したとしても
そのままファイルを書き込み続けるかもしれないし、
FileClsoeした時点で
ディレクトリィ情報を書き込まれてしまうかもしれない。
もしかしたらディスク上にゴミが残ってしまうかもしれない
(Diretory上に開始位置が記録されていない FAT 情報)
>monaaさん
上の書き込みでは「さん」を付け忘れてしまい申し訳ありません。
強制終了以外にもうひとつ、上記のCreateRemoteThreadを使う方法があるのですが、
こちらはプロセスを閉じる必要はありません。
この方法では相手のプロセス内に新しいスレッドを作って、その中でCloseHandleを呼びます。
相手のプロセスがWriteFileなどの戻り値を正しく処理していれば問題も起きません。
OSのファイルロックは排他処理のためで特にセキュリティを意識したものではないので、
OS管理下のファイルも書き換えは可能です。そういうウイルスもあります。
(ただ、多くのウイルスはカーネルパッチと言って、ファイルではなくメモリ上のコードを書き換えます。
これはウイルスだけではなく、セキュリティツールなども行っている処理です)
さらに言えば、一度コードを実行できる環境を得られさえすれば、
ドライバをロードしてカーネルモードに入ってしまうことで文字通り「何でも」できます。
>KHE00221さん
最近はフィルタドライバでファイルシステムへの命令を簡単に書き換えられるので、
それを使うことで安全に書き込み処理を止められますね。
アンチウイルス系のソフトがこの方法を使っていたと思います。
まあ、フィルタドライバとなるとさすがにDelphiの範疇ではないのですが…。
皆様、御回答、誠にありがとうございます。
jazzinさん、私の望む仕組みを御理解くださって、ありがとうございます。
プロセスを停止してから組むのだと、わかりやすく丁寧に御説明いただき、とてもありがたいです。
御指示どおり、ZwQuerySystemInformation等の検索をして、模索中でありますが、頑張りたいと思います。
いずれ、出来上がった際には、「解決」として、このページにコードを乗られればと思いますが、私にはハードルが高く、いつになるかわかりません・・・
今は、不本意ですが、とりあえずの「解決」とさせていただきます。
jazzinさん、皆さん本当にありがとうございます。
>jazzinさん
そういう蘊蓄はいいです。
難しい話は分かりませんが、とにかくロックは回避できません。
そう決まってるんです。分かりますか?
偽IDが出たのでもうこのIDは使いません。
さよなら。
なんか同じ人とは思えないけどとりあえず、
http://qanda.rakuten.ne.jp/qa5474345.html
っとご本人登場かな(リロード忘れました)
違うIDでも構いませんので、
また情報の共有できれば幸いです。
自信たっぷりに「できません」と言いながらあっさり論破されてしまって
自分の無知がばれてしまったので、偽物が出たふりして逃げるようにしか見えないけど
まあ間違った情報を広める人がいなくなるのは良いことですね
偽ID、ねえ さん
>まあ間違った情報を広める人がいなくなるのは良いことですね
間違いでも、かまいません、それは見た人が検証する事です。
間違っていけないなら、私なんか簡単に発言できません。
なにしろ間違いだらけですから。
これからも間違がえる事も有るかも知れませんが、笑って許して下さい。
monaaさん、出来ればそのままのIDで発言して下さい。
>相手のプロセスがWriteFileなどの戻り値を正しく処理していれば問題も起きません。
エラー対処しているから問題が起きないってことはない
正常な内容が書き込まれていなければ その事自体 問題なわけで
>難しい話は分かりませんが、とにかくロックは回避できません。
CloseHandle という正式な動作をしないと駄目なんだから・・
ロック回避できているのは違う
実際に解決法を提示した人がいて、それで質問者も納得しているのに、
どうでもいい揚げ足とりをして何の答えも出さないゴミって生きてる価値あるのかな…
きっとmonaaと一緒で自分の無知が晒されたから悔しくて必死なんだろうけどね
そんなみっともないことするくらいなら頑張って勉強したら?
>実際に解決法を提示した人がいて、それで質問者も納得しているのに、
>どうでもいい揚げ足とりをして何の答えも出さないゴミって生きてる価値あるのかな…
>きっとmonaaと一緒で自分の無知が晒されたから悔しくて必死なんだろうけどね
>そんなみっともないことするくらいなら頑張って勉強したら?
書くこと出来ないゴミって生きてる価値あるのかな…
せっかく名前変えたのに初心者マーク外し忘れて丸分かりですよ
気をつけましょうね
このような応酬は無意味なことだと思いませんか?
書き込んだ者が誰であるかを特定することなんて管理人以外は不可能である
ことは分かるでしょう?
いいかげんな根拠で相手がだれであるか決め付けてもそれが当たっている保証などまるでなし。
たとえ書き込みにカチンときても無視するパスする燃えないゴミに出すのが一番。
>実際に解決法を提示した人がいて、それで質問者も納得しているのに、
>どうでもいい揚げ足とりをして何の答えも出さないゴミって生きてる価値あるのかな…
>きっとmonaaと一緒で自分の無知が晒されたから悔しくて必死なんだろうけどね
>そんなみっともないことするくらいなら頑張って勉強したら?
削除しても元々ファイル掴んでいる側になんの問題の起きない削除の方法を
教えてくれ
君自身なにも答えを出してはいないと思うが?
初心者にしていて、名前を適当に書いているのは全部俺か
面白い理論だな
ランクはデフォルトで なし になっているから
>せっかく名前変えたのに初心者マーク外し忘れて丸分かりですよ
>気をつけましょうね
外し忘れなんてことはない
もう解決(?)してしまっているのでナンですが。
もし見ていればダメ吉さんに質問です。
そもそも何で、他アプリケーションが使用中のファイルを削除しなければならないんでしょうか?
安全に強制削除可能かどうかよりも、そのようなシチュエーションになる事の方が
問題だと思うのですが。
>削除しても元々ファイル掴んでいる側になんの問題の起きない削除の方法を教えてくれ
強制削除しなければ問題は起きないとでも思われてるようですけど、
ディスク容量が足りなくなったり、物理的に取り外されても書き込みは失敗しますよ。
そういうことを想定して戻り値によって適切な判断をすればいいだけで、
問題が起きないようにするというのは前提からして間違ってると思います。
失礼ですがちょっとおかしいこと言ってますよ。もう少し冷静になりましょう。
ダメ吉さんのケースとは少しちがいますが、
私の場合は、データーベースにParadoxを使い、ネットワーク上で共有しています。
通常は、ロックファイルであるPDOXUSRS.LCKはDBを閉じた後削除されますが、
たまにタイムアウトかネットワークの遅延か何かわかりませんが、
何かの原因でPDOXUSRS.LCKが残ったままになり、他の端末からDBを開くのが
極端に遅くなったり、最悪の場合は開けなくなったりすることがあります。
PDOXUSRS.LCKを削除すれば元に戻るのですが、当然「使用中のため削除
できません」となり削除できません。ファイルサーバーを再起動すれば、
起動後削除できるのですが、さすがにサーバーの再起動はできないので、
「unlocker」 ttp://ccollomb.free.fr/unlocker/
を使って強制削除しています。あまり、いい方法だとは思いませんが、
私の場合は、重宝しています。
もっとも、Paradoxを多端末で使うのに無理があると言えばそれまでですが。
>「今更ですが」さん
そういうケースもありますね。
まぁ、その場合はファイルをロックしていたヤツも
すでに正常ではなくなっているハズなので強制削除も仕方ないところですが。
状況によっては、まったく別の解決方法があるかもしれないので
それが分かればあるいは・・・と思ったのですが。
>君自身なにも答えを出してはいないと思うが?
じゃあ答えを出してる私が言ってあげますよ。
自分が答えられないことを他人が答えると悔しいようですけど、
そうやって他人の言葉尻を捕らえて揚げ足を取ってる姿は見苦しいを超えて滑稽です。
monaaさんを擁護する声はあってもあなたを擁護する声がないことに気づいてますか?
みんな心の中で笑ってるんですよ。文句しか言ってないあなたを。
そもそも「君自身なにも答えを出してはいない」なんてよく人に言えますね。
あなたなんて人の発言の否定しかしてないじゃないですか。
いい加減難癖をつけることしか能がない初心者は消えて下さい。
やっぱ偽善IDはJazinだったか・・・・
>強制削除しなければ問題は起きないとでも思われてるようですけど、
>ディスク容量が足りなくなったり、物理的に取り外されても書き込みは失敗しますよ。
>そういうことを想定して戻り値によって適切な判断をすればいいだけで、
>問題が起きないようにするというのは前提からして間違ってると思います。
>失礼ですがちょっとおかしいこと言ってますよ。もう少し冷静になりましょう。
本当にそう思ってるのか??
たとえばpagefile.sysとかもしOSがわがエラー対処しておけば
問題起きないと思うか?
>何かの原因でPDOXUSRS.LCKが残ったままになり、他の端末からDBを開
残ったままの LCK ファイルを消した場合問題は起きないだろうが
そいでないときの LCK ファイルを消したらどうなる???
問題おきないか???
問題おきないのなら LCK ファイル作る必要ないだろ
まぁまぁ、お二人ともそんなにカッカしないで、
頭を冷やして冷静な議論を。
コーヒーよりも、おいしいお茶などいかがですか?( ^-^)_旦""
もっといい例があった
>ディスク容量が足りなくなったり、物理的に取り外されても書き込みは失敗しますよ。
強制ファイル削除は
おそらく状況的には USBメモリなどに書き込み中にUSBメモリを抜く
に近いのかな
USBにアプリケーションをインストールしています。
USBを抜きました。
当然インストーラーはエラーを出し中止します
FileWriteあたりでエラー判定いれていれば
インストーラーとしては処理としての問題はないだろう
USBを抜く行為には問題はありませんか?
>monaaさんを擁護する声はあってもあなたを擁護する声がないこと
>に気づいてますか?
これって本当にJazinさんの発言でしょうか。
一応発言して置きます、KEH00221さんの発言でおかしく思う所は
私はありません。
以前にも遠回しで発言した事がありますが、この際要望して置きます。
KEH00221さんは初心者ではありません、上級者、もしくは常連です
初心者はできればこの際止めて下さい。
>これって本当にJazinさんの発言でしょうか。
なんとなくそうじゃない気もしているんだけど
にせIDとは同一に間違いないだろうけど
>KEH00221さんは初心者ではありません、上級者、もしくは常連です
>初心者はできればこの際止めて下さい。
ここって結構 平日に書き込みに来る人が多いんだよね
金曜に返答しても土日には返答書かずなぜか平日
で予想でたぶん仕事でしてるのかなと・・・・・
で自分は日曜大工タイプなので 初心者
プロの方でも初心者は初心者、KEH00221さんは初心者ではありません。
重ねて要望します、初心者が嫌でしたら、常連にして下さい。
>重ねて要望します、初心者が嫌でしたら、常連にして下さい。
重ねて要望します、初心者を止めるのが嫌でしたら、常連にして下さい。
失礼しまた、訂正します。
あれから、ときどきこちらへ・・・
サンプルコードでも・・・と期待して見てます。
(なかなか、作ることができなくて。。。)
Questさんの御質問ですが、某ソフトがログを出力続けてまして、それを必要時にコピー&削除したいと望みがあります。
ログはロックされてますが、フリーソフトでも削除は可能でしたし、消したら、新たにログを自動作成するようで、強制削除の影響もありませんでした。
でも、自動化もさせて、ログを自由に操れればといいなと思いました。
それで、難易度が高いと知らず、無謀にも自作したいと思った次第です・・・(苦笑)
僕も、みなさんと同じくデルファイが好きです。
だから、できたら自分の手でと・・・考えました。
そのログを出力し続けている「某ソフト」は、ログファイルをロックしっぱなしなんでしょうか?
放っておくと、永遠に1つのファイルにログを溜め続けるのでしょうか?
でも、他から読めなければ、ログとしての意味が無いですよね。
もしかしたら、ロック中でもリードのみなら、他アプリからログをオープンできるかもしれません。
さらには、ログに書き込む瞬間だけファイルをロックしていて、その書き込みの間を狙えば
普通に削除できてしまったりとか・・・。
ログ出力の頻度って、どのくらいの間隔なんでしょう?
2010/03/06(土) 15:29:19の発言は私ではないです。念のため^^;
ですがそれ以前の私の発言が今回の火種になったようですし、責任も感じています。すみません。
>ダメ吉さん
サンプルコードは書いてみても良いのですが、そちらの環境はどうなってますでしょうか?
(DelphiのバージョンやOSなど)
それから、そういった事情ならば、ロックをどうにかするよりも、
その某ソフトのCreateFileをフックして引数を書き換えて動作自体を変えた方がスマートかもしれません。
APIフックといいまして、こちらも原理自体はややこしいですが、(手法にもよりますが)行うのは容易いです。
某ソフトというのが何か分かれば、こちらもサンプルなど用意できるのですが…。
>KHE00221さん
私も仕事でプログラミングをしたことのない日曜プログラマなので、
ランクを選択するとしたら恐らく初心者を選んでしまうと思います^^;
アマチュアだと他と比べられませんし、立ち位置の判断が難しいですよね。
反応すればするほどあらしくんの思い通りになると思いますよ?
ほかの板でも荒らし回ってたみたいですし・・・。
ほかの方が罪悪感を感じることはないかと思います。
>ダメ吉さん
>無謀にも自作したいと思った次第です
私も自作したいと思いましたが、無理でしたので、
>残ったままの LCK ファイルを消した場合問題は起きないだろうが
>そいでないときの LCK ファイルを消したらどうなる???
>問題おきないか???
にならないよう、ロックファイルが作られて一定時間経過したのを確認し
CommandLineで使用できるunlockerをTimerで起動して、削除しています。
ロックされたままにならないようプログラムするのが正道でしょうが、
緊急避難的にこの方法で運用しています。
>できたら自分の手で
完成したら、是非教えて下さい。
jazzinさん、御返信ありがとうございます。
自分で「解決済み」としながらも、回答をいただけることに、とてもうれしく、また少し恐縮です・・・
サンプルコードの御話、ありがとうございます!
ぜひ、お願いします。。。
Delhiのバージョンは引き出しを開ければ、7,2005,2007,2009とあります。
OSも、XP,vista,7と、jazzinさんの御手間にならぬよう、環境にあわせらればと思います。
また、「今更ですが」さんも同機能を望まれているようですし、他の方も活用されそうなので、「某ソフト」の動作自体はそのままに・・・できましたら、最初の方法で、お願いできませんでしょうか?
お忙しいと思いますが、よろしくお願いいたします。。。
KHE00221さんが問題問題と繰り返しているのを見て違和感を持っていたのですが、
ひょっとしてエラーが起きることが悪いことだと思っていませんか?
そりゃユーザーから見ればエラーが起きて良い気はしないとは思いますが、
プログラム上でのエラーというのはあくまで状態を示す値に過ぎません。
USB機器が抜かれてエラーが出たとしても、そういう状態になったというだけです。
プログラム内でのエラーと、それをユーザーがどう感じるかは全く別の「問題」です。
それに今回は質問者の方が自らの意志で強制削除をしようとしています。
書き込まれないことが問題だ、USB機器を抜いてしまうことが問題だ、というのは、
一般論としては正しいかもしれませんが、論点をすり替えているようにしか見えません。
それでも問題だというのなら、書き込まれないことによって具体的にどういうことが起きるのかだとか、
それの対処法、もしくは代替方法などを提示すべきではないですか?
頭ごなしに「問題だ」と言っても、何も解決しないと思います。
お久しぶりです、偽ID対策でID変えてみました、もうこれで偽IDは出てこないでしょう。
見た目は似てますが、管理人さんにこの掲示板に対してIPアドレスの提示をお願いしました。
明らかななりすまし行為の場合はIPアドレスの開示に今後は協力してくださるそうです。
抑止的な意味も込めてあえてこの事を公開させて頂きます。
で、本題ですが、
ぽむぽむさんの紹介にあるURLですが、
IEが安全でないサイトと強調するので残念ながらダウンロードして動作確認することができません。
調べるとUnlockerはトロイの木馬入りらしいです。
http://social.answers.microsoft.com/Forums/ja-JP/msescanja/thread/d6671d29-ae08-470f-b04e-74516e433e81
これじゃ流石に使えません、というか危険ですよ。
上でなじられてますので一応、
私は論より証拠派ですので、実際にそういうソフトもしくはソースが手に入ればできる派になります。
というか、ダメ吉の納得のいくソースを誰かが書いてくれれば、それで構いません。
ソース無しでも、ダメ吉さんが解決したと言えば、
私自身それほど興味のある話題でもないのでそれ以上口出しするつもりはありませんし、
技術的に有意性を持たない捨てハンの発言に回答するつもりはありません。
ですが最低限のマナーは守りましょうね、今回の偽IDはこの掲示板ではやりすぎです。
>実際にそういうソフトもしくはソースが手に入ればできる派になります
とのことなので、どうぞ。
http://www.vector.co.jp/soft/winnt/util/se399372.html
>ひょっとしてエラーが起きることが悪いことだと思っていませんか?
>そりゃユーザーから見ればエラーが起きて良い気はしないとは思いますが、
>プログラム上でのエラーというのはあくまで状態を示す値に過ぎません。
>USB機器が抜かれてエラーが出たとしても、そういう状態になったというだけです。
プログラム上のエラーを問題にいつしましたか????
プログラムでエラー対処しているから問題が起きない
といっているから
結界に問題が起きることがあるよと言っているわけですが????
また初心者にしたな・・・・
結界->結果ね
>ログはロックされてますが、フリーソフトでも削除は可能でしたし、消したら、新たにログを自動作成するようで、強制削除の影響もありませんでした。
これを見たとき結構変わってることしているなと思ったりしたりしたんんだけど
普通
if FileWrite (FileHandle,Buffer,SizeOf(Buffer)) = -1 then
begin
if FileExists(FileName) = False then
begin
FileHandle := FileCreate(FileName);
end;
end;
見たいな事するのかね???
http://www.vector.co.jp/soft/winnt/util/se399372.html
これはファイル掴んでるスレッドでクローズしてるね。
なので
procedure LogWrite (Text:String);
var
I: Integer;
Buffer: array[0..255] of Char;
begin
if FileHandle <> -1 then
begin
StrPCopy(Buffer,Text);
I := FileWrite (FileHandle,Buffer,Length(Text));
if (I = -1 or I) <> (I <> Length(Text)) then
begin
//エラー発生
FileClose(FileHandle);
FileHandle := -1;
end;
end;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
FileClose(FileHandle);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
FileHandle := FileCreate('C:\AAAA.TXT');
end;
みたいにログ再作成を出来るようにしていないと
その時点でログの出力は行われなくなる
忘れ去られた頃に名前が出てきて、ちょっと笑いましたが
monaanさん 2010/03/09(火) 02:07:17
> IEが安全でないサイトと強調するので残念ながらダウンロードして動作確認することができません。
> 調べるとUnlockerはトロイの木馬入りらしいです。
自分でも使っているので、ちょっと調べてみましたよ。
.1.8.5以前は大丈夫
・1.8.7で混入、でもわざとではないし、1.8.8では修正
・NIS2010では、1.8.8は重大な脅威で報告のくせに、1.8.7はスルー
とか、いろいろあって、なにがほんとかわかりません。
「Unlocker トロイの木馬」で検索したらみつかります。
あとは自己責任でどうぞ。
さすがにいきなりトロイ認定されたらびびる。
今更ですが さんも使っているようだし、大丈夫ですか?
しつこいけどもう一個
Unlockerに仕込まれたトロイの木馬の正体
ttp://yoshiiz.blog129.fc2.com/blog-entry-16.html
Unlockerはシロという調査結果です。
1.8.6から導入されたeBay関連で出てくるものだそうです。
(前発言に挿入したい)
>ぽむぽむさん
ありがとうございました。
Unlockerのインストール時には、複数のアンチウイルスソフトで
確認したのですが、一瞬焦ってしまいました。
続報を見て安心しました。
コマンドラインで使えるので重宝していますが、できれば、プログラム中で
ロック解除するのが長年の夢ですが、いつになる事やら...
(Paradoxの使用をあきらめる方が早そうですが)
>結界に問題が起きることがあるよと言っているわけですが????
ですから、その結果というのはあくまでもユーザーにとっての結果ですよね?
プログラムからすれば、書き込まれないというのは問題ではなく正常な動作結果です。
書き込みできなかったのにエラーが出ないことの方が異常であり問題です。
KHE00221さんはプログラムの正常動作とユーザーの期待する正常動作を混同されています。
全くの別物です。
あくまでもプログラムにおいては書いたとおりに動くことが問題のない動作です。
それから、もうひとつの主張は読んで頂けなかったようですが、
今回は強制削除というイレギュラーな動作をわざと行おうとしています。
一般論で言えば問題かもしれませんが、今回はそれが前提ですよね。
それに対して「書き込まれないことが問題だ」と言うことに何の意味があるのですか?
問題だと言うのなら、何か代替案があるのですか?
>今回は強制削除というイレギュラーな動作をわざと行おうとしています。
それに対してリスクの説明をしているのに・・・・
>それに対して「書き込まれないことが問題だ」と言うことに何の意味があるのですか?
意味ありませんか??・
http://qanda.rakuten.ne.jp/qa5474345.html
を元にとりあえずある程度はできたけど不明な部分
>(4)ZwQueryObject()でOBJECT_BASIC_INFORMATIONを取る。
よくわからない
>(5)ZwQueryObject()でOBJECT_TYPE_INFORMATIONを取り、ハンドルがファイルである事を確認する。
TOBJECT_TYPE_INFORMATION の Name (UNICODE) に "File" と返ってくる
ので多分これ (KEY とか ほかにもある)
だけどDevice\TCP とかでも "File" が返ってくるのがなぞ
AReturnLength が TOBJECT_TYPE_INFORMATION のサイズと異なるのも不明
またサイズがいくつかパターンがある感じ
>(6)ZwQueryObject()でOBJECT_NAME_INFORMATIONを取り、縛りを解除したいファイル名(フルパス)と比較する。
OBJECT_NAME_INFORMATION を取ったときに 名前が
\Device\HardDiskVolume1\ のような形で返ってくるが
通常のパスに直す方法がよく分からない
でも一応ファイルを掴んでるプロセスに入り込んでクローズさせる事
は可能
>ダメ吉さん
お待たせしました。
今回はファイルを掴んでいるプロセスが判明してるとのことなので、
通常のロック解除処理より簡略化(手抜きとも言う^^;)してあります。
また、以下のサンプルはプロセスIDとファイル名からロックを解除するものなので、
プロセスIDの取得の仕方などは別途検索などして下さい。
>KHE00221さん
>>(4)ZwQueryObject()でOBJECT_BASIC_INFORMATIONを取る。
>よくわからない
OBJECT_BASIC_INFORMATIONはOBJECT_TYPE_INFORMATIONのサイズを知るために取得します。
>だけどDevice\TCP とかでも "File" が返ってくるのがなぞ
>AReturnLength が TOBJECT_TYPE_INFORMATION のサイズと異なるのも不明
>またサイズがいくつかパターンがある感じ
Fileというのはカーネルオブジェクトの種類としてですね。
ProcessExplorerなどで見てもそうなります。
AReturnLengthが異なるのはファイル名分が含まれるからです。
ですので物によって変動します。
>通常のパスに直す方法がよく分からない
ジャンクションなどで別名がつけられてることもあり、
正確な比較ができないこともあるので通常はこのままデバイス名で比較するのですが、
どうしても直したい場合はRtlNtPathNameToDosPathNameというAPIを使います。
次にソースを貼ります。
サンプルはDelphi2010で動作確認しました。
他のバージョンでも恐らく動作可能だと思います。
ただし2009以降はUnicode化していて、JEDIのNativeAPIユニットが対応していないので、
JwaWinType.pasのGetProcedureAddress(一番下にある)を以下のように書き換えます。
procedure GetProcedureAddress(var P: Pointer; const ModuleName, ProcName: string);
var
ModuleHandle: HMODULE;
begin
if not Assigned(P) then
begin
ModuleHandle := GetModuleHandle(PAnsiChar(AnsiString(ModuleName)));
if ModuleHandle = 0 then
begin
ModuleHandle := LoadLibrary(PAnsiChar(AnsiString(ModuleName)));
if ModuleHandle = 0 then
raise EJwaLoadLibraryError.CreateFmt(RsELibraryNotFound, [ModuleName]);
end;
P := Pointer(GetProcAddress(ModuleHandle, PAnsiChar(AnsiString(ProcName))));
if not Assigned(P) then
raise EJwaGetProcAddressError.CreateFmt(RsEFunctionNotFound, [ModuleName, ProcName]);
end;
end;
以下のコードをコンパイルすると、
引数にプロセスIDとファイル名を渡すことでロック解除するツールになります。
http://jedi-apilib.sourceforge.net/
からwin32apiとntapiをダウンロードしてパスを通しておいて下さい。
program Project1;
{$APPTYPE CONSOLE}
uses
Windows, SysUtils, JwaNative, JwaNtStatus, JwaWinType;
type
TByteArray = array of Byte;
function EnableDebugPrivilege: Boolean;
var
hToken: Cardinal;
tokenPriv: TTokenPrivileges;
luidDebug: TLargeInteger;
len: Cardinal;
begin
Result := False;
if OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken) then
begin
if LookupPrivilegeValue(nil, 'SeDebugPrivilege', luidDebug) then
begin
tokenPriv.PrivilegeCount := 1;
tokenPriv.Privileges[0].Luid := luidDebug;
tokenPriv.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
Result := AdjustTokenPrivileges(hToken, False, tokenPriv,
SizeOf(tokenPriv), nil, len);
end;
end;
end;
function GetHandleTable: TByteArray;
var
size: ULONG;
ret: NTSTATUS;
begin
size := $1000;
repeat
size := size * 2;
SetLength(Result, size);
ret := ZwQuerySystemInformation(SystemHandleInformation, PVOID(Result), size, nil);
until ret <> STATUS_INFO_LENGTH_MISMATCH;
if ret <> STATUS_SUCCESS then
Result := nil;
end;
procedure CloseRemoteHandle(hProcess, hFile: THandle);
var
hRemoteThread: THandle;
threadId: Cardinal;
begin
hRemoteThread := CreateRemoteThread(hProcess, nil, 0,
GetProcAddress(GetModuleHandle('kernel32.dll'), 'CloseHandle'), Pointer(hFile), 0, threadId);
if hRemoteThread <> 0 then
try
WaitForSingleObject(hRemoteThread, INFINITE);
finally
CloseHandle(hRemoteThread);
end;
end;
function GetObjectName(hObject: THandle; var ObjName: string; NameLen: Cardinal = 0): Boolean;
var
bytes: TByteArray;
info: PObjectNameInformation;
status: NTSTATUS;
begin
Result := False;
if NameLen = 0 then
NameLen := MAX_PATH*2;
SetLength(bytes, NameLen);
status := ZwQueryObject(hObject, ObjectNameInformation, Pointer(bytes), Length(bytes), @NameLen);
if status = STATUS_BUFFER_OVERFLOW then
begin
SetLength(bytes, NameLen);
status := ZwQueryObject(hObject, ObjectNameInformation, Pointer(bytes), Length(bytes), @NameLen);
if status <> STATUS_SUCCESS then Exit;
end
else if status <> STATUS_SUCCESS then Exit;
info := PObjectNameInformation(bytes);
ObjName := info.Name.Buffer;
Result := True;
end;
function GetDeviceFileName(const FileName: string; var DeviceFileName: string): Boolean;
var
hFile: THandle;
begin
Result := False;
hFile := CreateFile(PChar(FileName), 0, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if hFile <> INVALID_HANDLE_VALUE then
try
Result := GetObjectName(hFile, DeviceFileName);
finally
CloseHandle(hFile);
end;
end;
procedure UnlockFile(ProcessId: Cardinal; const FileName: string);
var
devName: string;
bytes, bytes1: TByteArray;
num: Cardinal;
info: PSystemHandleInformation;
i: Integer;
s: string;
hProcess: THandle;
hObject: THandle;
obi: TObjectBasicInformation;
len: Cardinal;
begin
if not GetDeviceFileName(FileName, devname) then Exit;
bytes := GetHandleTable;
if bytes = nil then Exit;
num := PCardinal(bytes)^;
info := PSystemHandleInformation(@bytes[4]);
for i := 0 to num-1 do
try
if info.ProcessId <> ProcessId then Continue;
hProcess := OpenProcess(PROCESS_ALL_ACCESS, False, info.ProcessId);
if hProcess = 0 then Continue;
try
if ZwDuplicateObject(hProcess, info.Handle, GetCurrentProcess, @hObject,
0, 0, DUPLICATE_SAME_ACCESS) = STATUS_SUCCESS then
try
if ZwQueryObject(hObject, ObjectBasicInformation, @obi, SizeOf(obi), @len) <> STATUS_SUCCESS then Continue;
SetLength(bytes1, obi.TypeInformationLength+2);
FillChar(Pointer(bytes1)^, Length(bytes1), 0);
if ZwQueryObject(hObject, ObjectTypeInformation, Pointer(bytes1), Length(bytes1), @len) <> STATUS_SUCCESS then Continue;
if PObjectTypeInformation(bytes1).Name.Buffer <> 'File' then Continue;
if GetObjectName(hObject, s, obi.NameInformationLength) then
begin
if SameText(s, devname) then
begin
CloseRemoteHandle(hProcess, info.Handle);
end;
end;
finally
CloseHandle(hObject);
end;
finally
CloseHandle(hProcess);
end;
finally
Inc(info);
end;
end;
var
pid: Integer;
fname: string;
begin
if ParamCount <> 2 then Exit;
if not TryStrToInt(ParamStr(1), pid) then Exit;
fname := ParamStr(2);
if not EnableDebugPrivilege then Exit;
UnlockFile(pid, fname);
end.
細かい補足など。
当初ファイル名のみを渡せば動くコードにしようと思っていたのですが、
例えばZwQueryObjectを一部システムの保持するハンドルに呼び出すと
二度と戻ってこなくなると言うOSのバグがあったりと何かと複雑なのでプロセスIDを必須にしました。
その名残でうっかりEnableDebugPrivilegeを消し忘れましたが、
これは特権を要求するもので恐らく必要ないです^^;
戻り値の最低限のチェックは行っていますが、
エラー時は手続きや関数を抜けるだけですので失敗したかどうかわからないかもしれません。
ですが失敗したら代替手段などないので、どうしようもないというのも事実です^^;
また、動作OSを書き忘れましたが、確認したのはXPです。
非公開APIを駆使してる関係上、Vistaで動くかどうかは保証できません。
不明点などがあればまた質問して下さい。
(とても長くなったので新しく質問を立てた方がいいかもしれませんが…)
すばらしい〜!
見事にプロセスが切れ、何事もないように、ファイルを捨てることもできます。
ちなみに、D2009+Win7x64で試しました。
そのためなのか・・・
JwaWinType.pasのGetProcedureAddressは、以下のようにしたらコンパイルできました。
procedure GetProcedureAddress(var P: Pointer; const ModuleName, ProcName: string);
var
ModuleHandle: HMODULE;
begin
if not Assigned(P) then
begin
ModuleHandle := GetModuleHandle(PChar(AnsiString(ModuleName)));
if ModuleHandle = 0 then
begin
ModuleHandle := LoadLibrary(PChar(AnsiString(ModuleName)));
if ModuleHandle = 0 then
raise EJwaLoadLibraryError.CreateFmt(RsELibraryNotFound, [ModuleName]);
end;
P := Pointer(GetProcAddress(ModuleHandle, PAnsiChar(AnsiString(ProcName))));
if not Assigned(P) then
raise EJwaGetProcAddressError.CreateFmt(RsEFunctionNotFound, [ModuleName, ProcName]);
end;
end;
僕の訂正だと、なんか問題ありそうですが・・・
とりあえず、コンパイル通したくて。。。
これで、もし、使用されているファイルだけ指定して、jazzinさんのコードでプロセスを切るとなると、以下の手順なのでしょうね。
1.どうにかしたいロック中ファイル選ぶ
2.全プロセス列挙
3.それぞれのプロセスが使用しているファイルを順次列挙
4.1のファイルと3のファイルが一致したら、そのプロセスIDと、ソフト名を今回のプログラムに引数として与えて実行。
・・・かな?と・・・思って、この方向で調べてみます。
もし、僕の方向性に間違いがあったら、どなたか御指摘いただけたら、幸いです。
jazzinさん、皆様、ありがとうございました!
あ、訂正を。
<FONT COLOR="#CC8000">>>4.1のファイルと3のファイルが一致したら、そのプロセスIDと、ソフト名を今回のプログラムに引数として与えて実行。</FONT>
ソフト名は必要ないですね、プロセスIDだけでした〜!
皆さんと同様に、見やすく、Fontの色を変えようとしたら失敗しました・・・
やっぱり、「ダメ」吉でした(涙
あのサンプルは「全てのロックされているファイル」を列挙します。
(正しくはファイルだけではないですが…)
ですので2と3は不要です。
さらに言えば、
if info.ProcessId <> ProcessId then Continue;
という行で対象プロセスか判定しているので、そこをコメントアウトするだけで
ファイルだけを指定してロックを解除する処理になります。…理論上は^^;
実際にはやってみるとわかりますが、上記補足のようなバグのせいで
ZwQueryObjectを全てのオブジェクトに対して呼ぶことができません(デッドロックします)
条件はそこまで多くないですが、いちいちZwQueryObjectをコールしてもいいか判定する必要もありますし、
間違えて呼んでしまったらOSを強制終了するはめになりますので、
手抜きと安全策を兼ねてプロセスIDを指定する形にしています。
汎用的なツールにするとしたら、このコードが何をしているかしっかり把握する必要がありますね。危険ですし。
>皆さんと同様に、見やすく、Fontの色を変えようとしたら失敗しました・・・
>やっぱり、「ダメ」吉でした(涙
色分かってるか???
念のために突っ込んでおくが・・・
http://jedi-apilib.sourceforge.net/
のことじゃないよな・・・
>hRemoteThread := CreateRemoteThread(hProcess, nil, 0,
GetProcAddress(GetModuleHandle('kernel32.dll'), 'CloseHandle'), >Pointer(hFile), 0, threadId);
こんなことしなくてもハンドルクローズするだけならこれでいいのか・・・
つまり Param の値を Push して Addres を Call してるのか
type
TWriteMemory = packed record
PushEAX : Byte; //1 $50;
Push : Byte; //1 $68
Handle : Integer; //4 ハンドル
MovEAX : Byte; //1 $B8
Address : Pointer; //4 アドレス
CallEax : WORD; //2 $FFD0;
PopEax : Byte; //1 $58
Ret : Byte; //1 $C3
end;
BaseAddress := GetProcAddress(GetModuleHandle(PChar('kernel32.dll')), PChar('CloseHandle'));
WriteMemory.PushEax := $50;
WriteMemory.Push := $68;
WriteMemory.MovEAX := $B8;
WriteMemory.Address := BaseAddress;
WriteMemory.CallEax := $D0FF;
WriteMemory.PopEax := $58;
WriteMemory.Ret := $C3;
//ファイルを掴んでいるプロセス上でコードを実行する
Address := VirtualAllocEx(hProcess, nil, SizeOf(WriteMemory) , MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if Address <> nil then
begin
WriteMemory.Handle := Info^.Information[I].Handle;
if WriteProcessMemory(hProcess, Address , @WriteMemory , SizeOf(WriteMemory) , WriteSize) = true then
begin
ThreadHandle := CreateRemoteThread(hProcess, nil, 0, Address , nil , 0 , ThreadID);
WaitForSingleObject(ThreadHandle, INFINITE);
end;
end;
VirtualFreeEx(hProcess,Address,0,MEM_RELEASE);
jazzinさん、御返信ありがとうございます!
そうですね・・・きちんとjazzinさんのコードを理解していないとダメですね・・・反省します。
KHE00221さん、御指摘ありがとうございます!
御察しのとおり、単なる文字色変えるだけのことに、失敗しました・・・
お恥ずかしいかぎりです。。。
先頭に > をつける とオレンジ
リンクを張ると 青(リンク) になるだけだぞ????
すいません。。。試しにさせてください。
>あ
あ、ホントになった!(笑
すみません・・・
この場で、こんなことまで教えていただいて恐縮です・・・
KHE00221さん、ありがとうございます。
ツイート | ![]() |