2015-09-02 47 views
9

Quando si rinomina una cartella in C#, System.IO.Directory.Move genera System.IO.IOException (messaggio "accesso negato") se quella cartella o qualsiasi sottocartella è attualmente aperta da un esploratore (Windows 7) finestra. Anche l'utilizzo della riga di comando RENAME non riesce. L'utilizzo di una seconda finestra di Explorer ha esito positivo.Come rinominare una cartella in C# che è attualmente aperta da Windows Explorer

L'errore persiste anche dopo il collasso della cartella principale (o dei suoi genitori). In effetti, la finestra dell'esploratore particolare deve essere chiusa. Quindi l'esploratore sembra creare alcuni blocchi solo per mostrare la struttura della cartella e non li rilascia anche se la cartella attuale non è più visualizzata (che è puro non-sense IMO).

C'è un modo per rinominare una cartella (in programma ad esempio utilizzando C#), che viene visualizzata (o era visibile, vedi sopra) da una finestra di Explorer?

Aggiornamento

trovato un modo come descritto dalla mia risposta a questa domanda (vedi sotto) utilizzando SHFileOperation(). Tuttavia, questa soluzione non è molto fattibile (vedi anche sotto).

+0

Ho provato questo e funziona, probabilmente hai anche un file aperto o non abbastanza autorizzazioni per quella cartella. Ho creato una directory 'D: \ a' e ho navigato su di esso tramite explorer, quando ho eseguito questo codice' Directory.Move ("D: \\ a", "D: \\ b"); ', la cartella nome cambiato automaticamente in Explorer e nella barra degli indirizzi. –

+0

@Wouter: Penso che sia necessario aggiungere un altro livello. Crea 'D: \ a \ b', vai a' D: \ a \ b' tramite explorer e prova a rinominare 'a' (come' x' o qualsiasi altra cosa). Non funziona qui. Ora naviga su 'a' o su' computer' in modo che né 'a' né 'b' siano mostrati in quella finestra (solo le unità sono mostrate). Ancora non funziona. A proposito, nessun file aperto e nessun problema di autorizzazione di sicuro. – user2261015

+0

In parte, quando explorer mostra la sottocartella, lancia la 'IOException', quando explorer mostra l'unità root, funziona. Devo dire che sto testando su Windows 10 che potrebbe spiegare la leggera differenza. –

risposta

2

Così sto rispondendo alla mia domanda dopo qualche ulteriore reasearch ..

La cartella può essere rinominata utilizzando SHFileOperation() come illustrato di seguito: https://msdn.microsoft.com/en-us/library/windows/desktop/bb776887%28v=vs.85%29.aspx (se questo utilizza la 'magia' di cui parla Wouter o meno. ;-)

Ma se è presente un'API Windows/.Net come System.IO.Directory.Move, WTF devo utilizzare Shell? Non parliamo di prestazioni ...

Ad ogni modo, usare SHFileOperation() è un problema nel a .. utilizzando C# dal momento che è necessario dichiarare tutto questo robe p-invoke. E in questo caso particolare è necessario utilizzare diverse strutture per finestre a 32 e 64 bit, poiché l'imballaggio è diverso (vedere http://www.pinvoke.net/default.aspx/shell32.shfileoperation). Questo è molto complicato, come di solito specificarei AnyCPU come destinazione. A questo punto devi necessariamente diramarti in fase di runtime (davvero molto brutto), a seconda che tu sia un processo a 64 o 32 bit o se costruisci in modo diverso per due target diversi, il che è piuttosto di grande impatto solo per aggirare l'insulso esploratore.

saluti

4

Ho utilizzato API Monitor v2 by Rohitab per monitorare le chiamate API di Windows.

Quando si cambia il nome della directory D:\test-D:\abc, questa chiamata è stato registrato:

explorerframe.dll ITransferSource::RenameItem (0x0000000015165738, "abc", TSF_COPY_CREATION_TIME | TSF_COPY_LOCALIZED_NAME | TSF_COPY_WRITE_TIME | TSF_DELETE_RECYCLE_IF_POSSIBLE, 0x00000000150f77d0) 

Scavando ulteriormente nel uscita del monitor mostra alcune chiamate native:

enter image description here

Come si possono vedere, non stanno usando MoveFile, invece, usano NtOpenFile con FILE_OPEN_FOR_BACKUP_INTENT e altri per aprire la directory originale, quindi l NtSetInformationFile con il nuovo nome della directory e la bandiera FileRenameInformation che è documentata here.

Sfortunatamente, queste sono tutte chiamate del kernel.

È possibile ottenere un handle a una directory in C/C++ da modalità utente in questo modo:

HANDLE h = ::CreateFileA("D:\\test", 
    DELETE | FILE_READ_ATTRIBUTES | SYNCHRONIZE, 
    FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, 
    OPEN_EXISTING, 
    FILE_FLAG_BACKUP_SEMANTICS, 
    NULL); 

Ma poi, è ancora bisogno di un alternativa in modalità utente per il NtSetInformationFile-Call.

Alcune opzioni di procedere (in ordine di complessità):

  • Vedere se è possibile utilizzare l'interfaccia della shell ITransferSource::RenameItem o trovare una funzione di shell pronto per l'uso
  • Dig ulteriormente nella soluzione user-mode e prova a trovare un'alternativa a NtSetInformationFile
  • Scrive un driver che contiene un IOCTL che esegue questi elementi in modalità kernel e chiama DeviceIoControl da C#.

Aggiorna

Sembra come la funzione SHFileOperation fa tutto quanto sopra constatata dalla OP.

Lasciare questa risposta in linea, perché potrebbe mostrare agli altri come eseguire il debug di problemi simili e ottenere indicazioni utili.

0

Ho avuto lo stesso problema. Non appena ho aperto una finestra di Esplora risorse e una volta navigato nella cartella da rinominare, il Directory.Move non è riuscito con un "accesso negato" (Windows 7 Professional a 64 bit, applicazione compilata come x86).

È interessante notare che il comando Microsoft.VisualBasic.FileIO.FileSystem.MoveDirectory(...) riesce a spostare il contenuto in una nuova directory, ma non riesce a eliminare la directory precedente se si rimane all'interno di una sottocartella della directory da spostare. Questo può essere risolto rilevando l'eccezione che viene lanciata al primo errore e riprova una seconda volta. Ora anche la cartella sorgente viene rimossa.