2015-07-16 7 views
12

Sto usando SharpShell per scrivere una piccola nuova voce di menu di contesto shell che copia i file attualmente selezionati in una nuova sottocartella, quindi richiede all'utente il nuovo nome della directory.Selezionare un file per rinominare nel menu di scelta rapida di SharpShell

Ricerca in StackOverflow, ho trovato la risposta this. Tuttavia, mi piacerebbe fare lo stesso in SharpShell.

In qualche modo dovrò sparare SVSI_EDIT a esso, che posso trovare sepolto in profondità nel SharpShell.Interop, ma non sono sicuro di come tutto ciò funzioni. Non riesco a trovare alcuna documentazione o esempi di codice.

(Edit: Credo che trovare il modo di ottenere un Pidl da un nome di file sarebbe un buon inizio, ma forse non ho davvero bisogno di questo a tutti?)

+1

Quando si crea una nuova cartella utilizzando Explorer, esso denomina la cartella "Nuova cartella", quindi entra nella modalità di rinomina. Quando stai digitando il nome della cartella e poi premi invio, stai effettivamente rinominando la cartella. Cosa succede se l'utente rinomina la cartella, quindi preme Invio prima che i file finiscano di copiare? Probabilmente il rinominare fallirà perché i file vengono ancora copiati nella cartella. Penso che questo approccio non sia buono e che tu stia meglio mostrando un modulo con una casella di testo all'utente. – Ove

risposta

4

È possibile iniziare a creare un progetto con SharpShell per registrare un nuovo menu contestuale shell come in questo tutorial.

Qui, dobbiamo definire una classe che implementa SharpContextMenu. Per semplicità creeremo il menu per qualsiasi tipo di file e mostra sempre:

[ComVisible(true)] 
[COMServerAssociation(AssociationType.AllFiles)] 
public class CopyFilesExtension : SharpContextMenu 
{ 
    protected override bool CanShowMenu() 
    { 
     return true; 
    } 

    protected override ContextMenuStrip CreateMenu() 
    { 
     var menu = new ContextMenuStrip(); 

     var copyFiles = new ToolStripMenuItem { Text = "Copy Files To Folder..." }; 

     copyFiles.Click += (sender, args) => CopyFiles(); 
     menu.Items.Add(copyFiles); 

     return menu; 
    } 

    private void CopyFiles() 
    { 
     ... 
    } 
} 

Ma sono sicuro che hai fatto tutto questo, il problema è quello di implementare il metodo CopyFiles().

Un modo per fare questo sta mostrando una finestra di dialogo che chiede per il nome della cartella, qualcosa di simile:

CopyFilesDialog

Poi, implementare CopyFiles() in questo modo:

private void CopyFiles() 
{ 
    using (var dialog = new CopyFileDialog()) 
    { 
     if (dialog.ShowDialog() == DialogResult.OK) 
     { 
      var folder = Path.GetDirectoryName(SelectedItemPaths.First()); 
      var newFolder = Path.Combine(folder, dialog.FolderName); 

      Directory.CreateDirectory(newFolder); 

      foreach (var path in SelectedItemPaths) 
      { 
       var newPath = Path.Combine(newFolder, Path.GetFileName(path)); 
       File.Move(path, newPath); 
      } 
     } 
    } 
} 

In precedenza codice, abbiamo chiesto il nome della cartella, quindi creare la cartella e infine spostare i file selezionati in quella cartella.

Tuttavia, se si vuole farlo utilizzando Rinomina comando nel Esplora risorse possiamo cominciare importando alcuni necessari Win32 funzioni:

class Win32 
{ 
    [DllImport("shell32.dll", CharSet = CharSet.Unicode)] 
    public static extern IntPtr ILCreateFromPath([In, MarshalAs(UnmanagedType.LPWStr)] string pszPath); 

    [DllImport("shell32.dll")] 
    public static extern int SHOpenFolderAndSelectItems(IntPtr pidlFolder, uint cidl, IntPtr[] apidl, int dwFlags); 

    [DllImport("shell32.dll")] 
    public static extern void ILFree(IntPtr pidl); 
} 
  • ILCreateFromPath ci permette di ottenere il PIDL da un nome file.
  • SHOpenFolderAndSelectItems consentici di selezionare il file e inviare il comando di rinomina.
  • ILFree libero non gestito PIDL creato.

Con queste Win32 funzioni possiamo definisce CopyFiles() come segue:

private void CopyFiles() 
{ 
    var folder = Path.GetDirectoryName(SelectedItemPaths.First()); 
    var newFolder = Path.Combine(folder, "New Folder"); 
    Directory.CreateDirectory(newFolder); 

    foreach (var path in SelectedItemPaths) 
    { 
     var newPath = Path.Combine(newFolder, Path.GetFileName(path)); 
     File.Move(path, newPath); 
    } 

    RenameInExplorer(newFolder); 
} 


private static void RenameInExplorer(string itemPath) 
{ 
    IntPtr folder = Win32.ILCreateFromPath(Path.GetDirectoryName(itemPath)); 
    IntPtr file = Win32.ILCreateFromPath(itemPath); 

    try 
    { 
     Win32.SHOpenFolderAndSelectItems(folder, 1, new[] { file }, 1); 
    } 
    finally 
    { 
     Win32.ILFree(folder); 
     Win32.ILFree(file); 
    } 
} 

Non possiamo usare SharpShell.Interop.Shell32 poiché l'unico metodo disponibile in questa classe è ShellExecuteEx() che viene utilizzato per lanciare nuovi processi.

3

Utilizzando la funzionalità SelectItemInExplorer dal cited example nella domanda, un'implementazione di base di nella versione base sarebbe simile alla seguente. Ove possibile, qualsiasi funzionalità P/Invoke è stata riscritta per utilizzare il maggior numero possibile di dichiarazioni esistenti di SharpShell.

using SharpShell.Attributes; 
using SharpShell.Interop; 
using SharpShell.SharpContextMenu; 
using System; 
using System.IO; 
using System.Linq; 
using System.Runtime.InteropServices; 
using System.Windows.Forms; 

namespace SendToFolderRename 
{ 
    [ComVisible(true)] 
    [COMServerAssociation(AssociationType.AllFiles)] 
    public class SendToFolderRename : SharpContextMenu 
    { 
     [DllImport("ole32.dll")] 
     private static extern int CreateBindCtx(int reserved, out IntPtr ppbc); 

     [DllImport("shell32.dll")] 
     private static extern int SHOpenFolderAndSelectItems(IntPtr pidlFolder, uint cidl, IntPtr[] apidl, int dwFlags); 

     protected override bool CanShowMenu() 
     { 
      return true; 
     } 

     protected override ContextMenuStrip CreateMenu() 
     { 
      var menu = new ContextMenuStrip(); 
      var itemCountLines = new ToolStripMenuItem 
      { 
       Text = "Copy files to subfolder" 
      }; 

      itemCountLines.Click += CopyFilesToSubfolder; 
      menu.Items.Add(itemCountLines); 
      return menu; 
     } 

     private void CopyFilesToSubfolder(object sender, EventArgs e) 
     { 
      //System.Diagnostics.Debugger.Break(); 
      string firstSelectedFile = SelectedItemPaths.FirstOrDefault(); 

      if (string.IsNullOrEmpty(firstSelectedFile)) 
       return; 

      string currentDirPath = (new FileInfo(firstSelectedFile)).DirectoryName; 
      string newDirName = Path.GetRandomFileName(); 
      string newDirPath = Path.Combine(currentDirPath, newDirName); 
      DirectoryInfo newDir = Directory.CreateDirectory(newDirPath); 
      foreach (string filePath in SelectedItemPaths) 
      { 
       FileInfo fileInfo = new FileInfo(filePath); 
       string newFilePath = Path.Combine(fileInfo.DirectoryName, newDirName, fileInfo.Name); 
       File.Copy(filePath, newFilePath); 
      } 

      SelectItemInExplorer(IntPtr.Zero, newDirPath, true); 
     } 

     public static void SelectItemInExplorer(IntPtr hwnd, string itemPath, bool edit) 
     { 
      if (itemPath == null) 
       throw new ArgumentNullException("itemPath"); 

      IntPtr folder = PathToAbsolutePIDL(hwnd, Path.GetDirectoryName(itemPath)); 
      IntPtr file = PathToAbsolutePIDL(hwnd, itemPath); 
      try 
      { 
       SHOpenFolderAndSelectItems(folder, 1, new[] { file }, edit ? 1 : 0); 
      } 
      finally 
      { 
       Shell32.ILFree(folder); 
       Shell32.ILFree(file); 
      } 
     } 

     private static IntPtr GetShellFolderChildrenRelativePIDL(IntPtr hwnd, IShellFolder parentFolder, string displayName) 
     { 
      IntPtr bindCtx; 
      CreateBindCtx(0, out bindCtx); 

      uint pchEaten = 0; 
      SFGAO pdwAttributes = 0; 
      IntPtr ppidl; 
      parentFolder.ParseDisplayName(hwnd, bindCtx, displayName, ref pchEaten, out ppidl, ref pdwAttributes); 
      return ppidl; 
     } 

     private static IntPtr PathToAbsolutePIDL(IntPtr hwnd, string path) 
     { 
      IShellFolder desktopFolder; 
      Shell32.SHGetDesktopFolder(out desktopFolder); 
      return GetShellFolderChildrenRelativePIDL(hwnd, desktopFolder, path); 
     } 
    } 
} 
+0

Ciao, grazie mille per la tua risposta. Ci proverò domani (sono stato impegnato) e riferirò se funziona. Sarebbe un po 'triste se non ci fosse un modo più SharpShell-y per farlo, ma è meglio di niente :) – Lynn

Problemi correlati