2017-01-25 7 views
3

私はプログラムの削除と、 "App A"というアプリケーションの内容を、カスタムWPF .exeアプリケーションである "installer"プログラムを使って削除しようとしています。 "App B"を呼び出します。 (「アプリケーションB」での私の質問の懸念コードが。)誰かがファイルを開いているディレクトリを削除する

GUIのセットアップ(特に重要ではない)
アプリケーションBは、ユーザーが上にアプリケーションAをコピーするには、コンピュータ名を選ぶことができるGUIを持っています。ファイルピッカーは、管理者が "App A.exe"をクリックしてローカルマシン上のソースディレクトリパスを入力するために使用します。ユーザー名とパスワードのテキストボックスもあるので、管理者は、App Aが提供されるターゲットファイルサーバーの資格情報を入力できます。コードはアクセス許可の問題を防ぐためにユーザーを偽装します。 「コピー」ボタンがルーチンを開始する。

場合に、アプリケーションA、ファイルのプロセスを強制終了し、explorer.exeのと同様に、ドメイン内のすべてのコンピュータ上で「アプリケーションA.EXE」プロセスを殺すことにより、ファイル削除に
コピールーチンが起動をやって彼らApp Aのエクスプローラフォルダを開いていました。明らかにこれは後の時間に行われるだろうが、家に帰る前に誰かがまだ物事を開いてロックしている可能性がある。そして、それは本当に私が解決しようとしている問題の基盤です。

更新されたファイルをコピーする前に、古いディレクトリ全体を削除します。ディレクトリ(およびそのサブディレクトリ)を削除するには、その中の各ファイルを削除する必要があります。 しかし、彼らはApp Aのフォルダからファイルを開いていたと言います。このコードは、ファイルを削除する前に(Eric J.の答えであるHow do I find out which process is locking a file using .NET?のコードを使用して)すべてのファイルのロックプロセスを検出し、実行中のコンピュータでそのプロセスを終了させます。地元の場合は、それだけで使用しています:

public static void localProcessKill(string processName) 
{ 
    foreach (Process p in Process.GetProcessesByName(processName)) 
    { 
     p.Kill(); 
    } 
} 

をリモートの場合は、WMIを使用しています。

public static void remoteProcessKill(string computerName, string fullUserName, string pword, string processName) 
{ 
    var connectoptions = new ConnectionOptions(); 
    connectoptions.Username = fullUserName; // @"YourDomainName\UserName"; 
    connectoptions.Password = pword; 

    ManagementScope scope = new ManagementScope(@"\\" + computerName + @"\root\cimv2", connectoptions); 

    // WMI query 
    var query = new SelectQuery("select * from Win32_process where name = '" + processName + "'"); 

    using (var searcher = new ManagementObjectSearcher(scope, query)) 
    { 
     foreach (ManagementObject process in searcher.Get()) 
     { 
      process.InvokeMethod("Terminate", null); 
      process.Dispose(); 
     } 
    } 
} 

そして、それはファイルを削除することができます。すべては順調です。

Directoryの削除失敗以下の私のコードで
、それはファイルの再帰的な削除を行っている、そしてそれは罰金ない、アップ私はディレクトリを削除しようとしていますので、それは、The process cannot access the file '\\\\SERVER\\C$\\APP_A_DIR' because it is being used by another processを言うだろうDirectory.Delete()、まで私はファイルを開いていましたが(たとえコードが実際に物理ファイルを削除できたとしても、インスタンスはまだ開いています)。

public void DeleteDirectory(string target_dir) 
    { 
     string[] files = Directory.GetFiles(target_dir); 
     string[] dirs = Directory.GetDirectories(target_dir); 
     List<Process> lstProcs = new List<Process>(); 

     foreach (string file in files) 
     { 
      File.SetAttributes(file, FileAttributes.Normal); 
      lstProcs = ProcessHandler.WhoIsLocking(file); 
      if (lstProcs.Count == 0) 
       File.Delete(file); 
      else // deal with the file lock 
      { 
       foreach (Process p in lstProcs) 
       { 
        if (p.MachineName == ".") 
         ProcessHandler.localProcessKill(p.ProcessName); 
        else 
         ProcessHandler.remoteProcessKill(p.MachineName, txtUserName.Text, txtPassword.Password, p.ProcessName); 
       } 
       File.Delete(file); 
      } 
     } 

     foreach (string dir in dirs) 
     { 
      DeleteDirectory(dir); 
     } 

     //ProcessStartInfo psi = new ProcessStartInfo(); 
     //psi.Arguments = "/C choice /C Y /N /D Y /T 1 & Del " + target_dir; 
     //psi.WindowStyle = ProcessWindowStyle.Hidden; 
     //psi.CreateNoWindow = true; 
     //psi.FileName = "cmd.exe"; 
     //Process.Start(psi); 

     //ProcessStartInfo psi = new ProcessStartInfo(); 
     //psi.Arguments = "/C RMDIR /S /Q " + target_dir; 
     //psi.WindowStyle = ProcessWindowStyle.Hidden; 
     //psi.CreateNoWindow = true; 
     //psi.FileName = "cmd.exe"; 
     //Process.Start(psi); 

     // This is where the failure occurs 
     //FileSystem.DeleteDirectory(target_dir, DeleteDirectoryOption.DeleteAllContents); 
     Directory.Delete(target_dir, false); 
    } 

私は上記のコードでコメントしたことを残しました。私はファイルに添付されたプロセスを削除して削除することができますが、は、フォルダに添付されたプロセスを削除して削除する方法がありますか?

私が見たオンラインのすべては、遅延を伴うループチェックを使用してこれを解決しようとしています。これはここでは機能しません。私は開いていたファイルを削除する必要がありますが、それはハンドルがフォルダから解放されるようにして、最後に削除することもできます。これを行う方法はありますか?

私はそれが動作しませんと見なさ別のオプション: 私は、私はちょうど、レジストリで削除のためにそのネットワークフォルダをマークすることによって、「インストール」(コピー)プロセスを凍結し、ファイルサーバのプログラムの再起動をスケジュールするかもしれないと思いました後で再実行します。How to delete Thumbs.db (it is being used by another process)はこれを行うことにより、このコードを提供します:

[DllImport("kernel32.dll")] 
public static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, int dwFlags); 

public const int MOVEFILE_DELAY_UNTIL_REBOOT = 0x4; 

//Usage: 
MoveFileEx(fileName, null, MOVEFILE_DELAY_UNTIL_REBOOT); 

をしかし、それはMOVEFILE_DELAY_UNTIL_REBOOTを使用する場合は、ネットワークが使用可能になる前に遅れた操作が実行されるため、「ファイルは、リモートの共有に存在しないことを文書であ​​ります。 "そして、それはファイル名ではなくフォルダパスを許可していたと仮定していました。 (参考文献:https://msdn.microsoft.com/en-us/library/windows/desktop/aa365240(v=vs.85).aspx)。

+0

あなたはユーザーログオンログオフ可能性があり、誰もがすることを必要とする場合は、ここでいくつかの良い答えへのリンクのカップルです。あなたはすでにexplorer.exeを殺すことによって、ちょっと「無礼」になっています。だから、ユーザをログオフするとあなたの問題はすべて解決します。何らかの理由でサーバーが復帰しないというリスクを実行するので、再起動する方が良いでしょう。 – SledgeHammer

+0

申し訳ありませんが、残念ながら、ドメイン内には何万人ものユーザーがおり、このアプリケーションのユーザーはわずかなサブセットのみになります。私は安全にexplorer.exeを殺すことができると思う(再起動するので、人々は簡単にフォルダを再オープンできる)...私たちのポリシーは誰でもログオフする必要がありますが、アップデートをインストールする際にはマシンを実際に再起動する必要がありますが、ドメイン全体でのログオフが望ましいとは考えていませんが、私たちのデータベースのユーザーのテーブルを見つけることができます。このような選択的アプローチはできますか?ユーザーをログインしているコンピュータにどのようにマッピングしますか? – vapcguy

+0

Wait ..アプリケーションはMachineXで動作し、RDPはMachineXで動作しますか?または、彼らはUNCによってリモートでアプリケーションを実行しますか?または、アプリケーションはクライアントマシン上に存在し、MachineXのコンポーネントポイントはいくつか存在しますか? – SledgeHammer

答えて

1

は、だから私は扱うたかっ2つのシナリオがあります - 両方のフォルダが削除されることが防止されるところです。

1)ユーザーがファイルサーバー上のアプリケーションのフォルダから自分のローカルマシン上のファイルを開いています。

2)管理者には、アプリケーションのフォルダから開いたファイルがあり、そのファイルはサーバーにリモート(RDP)されている間に表示されます。

私は前進する方法で解決しました。

1)の場合、IOExceptionブロックのファイルサーバーのプログラムによる再起動を単にスケジューリングすることによって、「インストール」(コピー)プロセスをフリーズすることができます。は、フォルダを吹き飛ばしたい(理想的ではないかもしれないが、おそらく過剰ですが、この同じ問題を横断して走っているものはこのオプションに触発されるかもしれません)。サーバーを再起動した後、インストーラーを再実行してファイルをコピーする必要があります。

[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 
public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken); 

LogonUser(userName, domainName, password, 
     LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, 
     out safeTokenHandle); 

try 
{ 
    using (WindowsIdentity newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle())) 
    { 
     using (WindowsImpersonationContext impersonatedUser = newId.Impersonate()) 
     { 
      foreach (Computer pc in selectedList) // selectedList is an ObservableCollection<Computer> 
      { 
       string newDir = "//" + pc.Name + txtExtension.Text; // the textbox has /C$/APP_A_DIR in it 
       if (Directory.Exists(newDir)) 
       { 
        DeleteDirectory(newDir); // <-- this is where the exception happens 
       } 
      } 
     } 
    } 
} 
catch (IOException ex) 
{ 
    string msg = "There was a file left open, thereby preventing a full deletion of the previous folder, though all contents have been removed. Do you wish to proceed with installation, or reboot the server and begin again, in order to remove and replace the installation directory?"; 
    MessageBoxResult result = MessageBox.Show(msg, "Reboot File Server?", MessageBoxButton.OKCancel); 
    if (result == MessageBoxResult.OK) 
    { 
     var psi = new ProcessStartInfo("shutdown","/s /t 0"); 
     psi.CreateNoWindow = true; 
     psi.UseShellExecute = false; 
     Process.Start(psi); 
    } 
    else 
    { 
     MessageBox.Show("Copying files..."); 
     FileSystem.CopyDirectory(sourcePath, newDir); 
     MessageBox.Show("Completed!"); 
    } 
} 

参考:How to shut down the computer from C#

OR

2)それを完全に無視して、とにかく、私のコピーを実行します。ファイルは実際には削除されますが、私が書くことができる限り、削除できないフォルダを持つことには問題ありません。これは私が最終的に選んだものです。だから、再び

IOException catchブロックで:

catch (IOException ex) 
{ 
    if (ex.Message.Contains("The process cannot access the file") && 
     ex.Message.Contains("because it is being used by another process")) 
    { 
     MessageBox.Show("Copying files..."); 
     FileSystem.CopyDirectory(sourcePath, newDir); 
     MessageBox.Show("Completed!"); 
    } 
    else 
    { 
     string err = "Issue when performing file copy: " + ex.Message; 
     MessageBox.Show(err); 
    } 
} 

上記のコードは私のそれでNameノードを持つComputerためのモデルと、に基づいている私の偽装クラスの残りの部分を残し彼らがそれを言う方法のいくつかの異なる(しかし同様の)コードブロックの私自身の演出。

Need Impersonation when accessing shared network drive

copy files with authentication in c#

関連:Cannot delete directory with Directory.Delete(path, true)

関連する問題