2017-01-25 2 views
3

나는 프로그램 WPF.exe 응용 프로그램 인 "installer"프로그램을 사용하여 "App A"응용 프로그램의 내용을 프로그래밍 방식으로 삭제하고 바꾸려고합니다. 앱 B를 호출합니다. (특히 중요하지)누군가가 파일을 열어 둔 디렉토리를 삭제하십시오.

GUI 설정
응용 프로그램 B는 사용자에 앱 A를 복사 할 컴퓨터 이름을 선택할 수있는 GUI를 가지고 ("앱 B". 나의 질문에 대한 우려 코드). 파일 선택 도구는 관리자가 "App A.exe"를 클릭하여 로컬 컴퓨터의 소스 디렉토리 경로를 채우는 데 사용합니다. 또한 사용자 이름과 암호에 대한 텍스트 상자가 있으므로 관리자는 App A가 제공 될 대상 파일 서버에 대한 자격 증명을 입력 할 수 있습니다.이 코드는 사용 권한 문제를 방지하기 위해 사용자를 가장합니다. "복사"버튼이 루틴을 시작합니다.

경우, 응용 프로그램 A, 파일 프로세스를 종료하고 Explorer.exe에서뿐만 아니라 도메인의 모든 컴퓨터에 '앱 a.exe를 "프로세스를 죽이는 파일 삭제를
복사 루틴 시작을하는 사람들 App A의 탐색기 폴더를 열었습니다. 분명히 이것은 사후에 이루어질 것이지만 누군가 집에 가기 전에 물건을 열어 놓고 잠글 수 있습니다. 그리고 그것은 정말로 내가 해결하고자하는 문제의 근원입니다.

업데이트 된 파일을 복사하기 전에 전체 이전 디렉토리를 삭제하려고합니다. 디렉토리 (및 그 서브 디렉토리)를 삭제하기 위해서는 디렉토리 내의 각 파일을 삭제해야합니다. 하지만 앱 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(); 
     } 
    } 
} 

그런 다음이 파일을 삭제할 수 있습니다. 모든 것이 잘됩니다.

아래에있는 내 코드에서
, 그것은 파일의 재귀 삭제를하고있는 디렉토리 삭제 실패하고, 잘 그것을 않습니다까지 내가 디렉토리를 삭제하려고 시도하고 있기 때문에, 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

예, 불행히도 Google은 도메인에 수만 명의 사용자를 보유하고 있으며이 애플리케이션의 사용자는 소수의 하위 사용자 일뿐입니다. 나는 explorer.exe를 안전하게 죽일 수 있다고 생각한다. (다시 시작할 것이고 사람들은 쉽게 폴더를 다시 열 수 있기 때문이다.)우리 정책은 모든 사람이 떠날 때 모두 로그 아웃해야하고, 업데이트를 설치할 때 실제로 시스템을 재부팅해야만 도메인 전체에 대한 로그 오프가 가능할 것으로 확신 할 수는 없지만 갈 수는 있습니다. 우리의 데이터베이스에있는 사용자의 테이블을 찾을 수 있습니다. 이런 종류의 선택적인 접근을 할 수 있습니까? 사용자를 로그인 한 컴퓨터에 어떻게 매핑합니까? – vapcguy

+0

기다려 .. 응용 프로그램이 MachineX에서 실행되고 사용자가 MachineX로 실행됩니다. 아니면 UNC가 원격으로 응용 프로그램을 실행합니까? 또는 앱이 클라이언트 컴퓨터에 있고 일부 구성 요소가 MachineX에 있습니까? – SledgeHammer

답변

1

그래서 내가 처리하고 싶었이 개 시나리오가 있습니다 - 모두 폴더가 삭제되는 것을 방지 할 수있다 곳입니다

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#

또는

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)