Select 用于在 SharpShell 上下文菜单中重命名的文件
Select a file for renaming in SharpShell context menu
我正在使用 SharpShell 编写一个新的 shell 上下文菜单项 将当前选定的文件复制到一个新的子文件夹,然后提示用户输入目录的新名称.
搜索 Whosebug,我找到了 this 答案。但是,我想在 SharpShell 中做同样的事情。
我将不得不以某种方式向它开火 SVSI_EDIT
,我可以在 SharpShell.Interop
的深处找到它,但我不确定它是如何工作的。我找不到任何文档或代码示例。
(编辑:我认为找出如何从文件名中获取 Pidl
是一个好的开始,但也许我根本不需要它?)
使用问题中 cited example 中的 SelectItemInExplorer
功能,very 基本实现如下所示。在可能的情况下,任何 P/Invoke 功能都被重写以尽可能多地使用 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);
}
}
}
您可以开始使用 SharpShell 创建一个项目来注册一个新的 shell 上下文菜单,就像这个 tutorial.
在这里,我们必须定义一个class实现SharpContextMenu
。为简单起见,我们将为任何文件类型创建菜单并始终显示它:
[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()
{
...
}
}
但我相信你已经完成了所有这些,这里的问题是实现 CopyFiles()
方法。
一种方法是显示一个对话框,询问文件夹的名称,如下所示:
然后,像这样实现 CopyFiles()
:
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);
}
}
}
}
在上面的代码中,我们询问了文件夹的名称,然后创建文件夹,最后将 selected 文件移动到该文件夹。
但是,如果你想在Windows Explorer[=]中使用重命名命令来完成57=] 我们可以从导入一些需要的 Win32 函数开始:
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
允许我们从文件名中获取 PIDL。
SHOpenFolderAndSelectItems
允许我们 select 文件并发送重命名命令。
ILFree
释放非托管 PIDL
创建。
使用这些 Win32 函数我们可以定义 CopyFiles()
如下:
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);
}
}
我们不能使用 SharpShell.Interop.Shell32
,因为此 class 中唯一可用的方法是 ShellExecuteEx()
,它用于启动新进程。
我正在使用 SharpShell 编写一个新的 shell 上下文菜单项 将当前选定的文件复制到一个新的子文件夹,然后提示用户输入目录的新名称.
搜索 Whosebug,我找到了 this 答案。但是,我想在 SharpShell 中做同样的事情。
我将不得不以某种方式向它开火 SVSI_EDIT
,我可以在 SharpShell.Interop
的深处找到它,但我不确定它是如何工作的。我找不到任何文档或代码示例。
(编辑:我认为找出如何从文件名中获取 Pidl
是一个好的开始,但也许我根本不需要它?)
使用问题中 cited example 中的 SelectItemInExplorer
功能,very 基本实现如下所示。在可能的情况下,任何 P/Invoke 功能都被重写以尽可能多地使用 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);
}
}
}
您可以开始使用 SharpShell 创建一个项目来注册一个新的 shell 上下文菜单,就像这个 tutorial.
在这里,我们必须定义一个class实现SharpContextMenu
。为简单起见,我们将为任何文件类型创建菜单并始终显示它:
[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()
{
...
}
}
但我相信你已经完成了所有这些,这里的问题是实现 CopyFiles()
方法。
一种方法是显示一个对话框,询问文件夹的名称,如下所示:
然后,像这样实现 CopyFiles()
:
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);
}
}
}
}
在上面的代码中,我们询问了文件夹的名称,然后创建文件夹,最后将 selected 文件移动到该文件夹。
但是,如果你想在Windows Explorer[=]中使用重命名命令来完成57=] 我们可以从导入一些需要的 Win32 函数开始:
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
允许我们从文件名中获取 PIDL。SHOpenFolderAndSelectItems
允许我们 select 文件并发送重命名命令。ILFree
释放非托管PIDL
创建。
使用这些 Win32 函数我们可以定义 CopyFiles()
如下:
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);
}
}
我们不能使用 SharpShell.Interop.Shell32
,因为此 class 中唯一可用的方法是 ShellExecuteEx()
,它用于启动新进程。