拖放到 Windows 文件夹 - C#
无需使用 Shell Extensions,即可从您的应用程序将文件拖放到 Windows 文件夹。
引言
我能找到的所有关于实现“拖放到 Windows 文件夹”的源代码都使用了 Shell Extensions。最初,我也是这么实现的。它在除 Vista 之外的所有 Windows 版本上都能正常工作。Vista 更改了一些内部接口,导致我的代码无法运行,这促使我寻找另一种不那么依赖操作系统的实现方法。
我使用 Visual Studio 2005 进行开发,并在 Windows XP、Windows 2000、Windows 2003 和 Vista 上进行了测试。
背景
我只专注于获取执行拖放操作的目录路径。您可以使用自己的方法来启动拖放并处理它。我使用了 FileSystemWatcher
来实现这一点。以下是它的工作原理:
当从您的应用程序启动拖放操作时,会在系统的临时目录中创建一个临时文件。我们需要在其中大量创建的文件中识别出这个文件,因此我为其添加了一个应用程序可以检测到的固定前缀。由于它用于标识,请使其具有唯一的模式,以免其他文件具有相同的名称。会设置一个 FileSysemWatcher
来监视系统的Temp文件夹,以检查是否有以该名称创建的文件。当我们启动拖放时,会创建此文件,FileSystemWatcher
会捕获它。我们将此文件设置为拖放源,以便 Windows 将其识别为真实的文件拖放,然后将此文件复制到我们需要下载原始文件的位置。接下来,我为系统中所有逻辑驱动器实现了用于被拖放文件的 FileSystemWatch
。如果文件被拖放到任何 Windows 文件夹或桌面,它将被捕获,并且对驱动器的文件系统监视将被立即移除。如果拖放被取消,监视也会被取消。
使用代码
首先,我们需要监视系统的Temp文件夹,以便在启动拖放时创建的文件能够被捕获。我们必须在启动线程中完成此操作,以下是我在Program.cs中用于此目的的变量:
#region Variables
//A prefix used in filename to identify a drag from the application
internal const string DRAG_SOURCE_PREFIX = "__DragNDrop__Temp__";
//The item which we drag is being stored here
internal static object objDragItem;
//A FileSystemWatcher to monitor the System's Temp Directory for a drag
private static FileSystemWatcher tempDirectoryWatcher;
//A Hashtable to keep multiple FileSystemWatchers
public static Hashtable watchers = null;
#endregion
在 Main()
方法中,我添加了监视:
static void Main()
{
//Sets tempDirectoryWatcher to monitor
//the creation of a file from our application
tempDirectoryWatcher = new FileSystemWatcher();
tempDirectoryWatcher.Path = Path.GetTempPath();
tempDirectoryWatcher.Filter =
string.Format("{0}*.tmp", DRAG_SOURCE_PREFIX);
tempDirectoryWatcher.NotifyFilter = NotifyFilters.FileName;
tempDirectoryWatcher.IncludeSubdirectories = false;
tempDirectoryWatcher.EnableRaisingEvents = true;
tempDirectoryWatcher.Created +=
new FileSystemEventHandler(TempDirectoryWatcherCreated);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new DragNDrop());
}
在此,FIleSystemWatch
将监视 temp 文件夹,以查找创建的、带有“DRAG_SOURCE_PREFIX”前缀和“.tmp”扩展名的文件。
现在,当创建了这样的文件时,我们需要监视系统中所有逻辑驱动器。
private static void TempDirectoryWatcherCreated(object sender, FileSystemEventArgs e)
{
try
{
if (watchers == null)
{
int i = 0;
Hashtable tempWatchers = new Hashtable();
FileSystemWatcher watcher;
//Adding FileSystemWatchers and adding Created event to it
foreach (string driveName in Directory.GetLogicalDrives())
{
if (Directory.Exists(driveName))
{
watcher = new FileSystemWatcher();
watcher.Filter = string.Format("{0}*.tmp", DRAG_SOURCE_PREFIX);
watcher.NotifyFilter = NotifyFilters.FileName;
watcher.Created += new FileSystemEventHandler(FileWatcherCreated);
watcher.IncludeSubdirectories = true;
watcher.Path = driveName;
watcher.EnableRaisingEvents = true;
tempWatchers.Add("file_watcher" + i.ToString(), watcher);
i = i + 1;
}
}
watchers = tempWatchers;
tempWatchers = null;
}
}
catch (Exception ex)
{
//Handle your exception here
}
}
检查目录是否存在可能听起来很荒谬。但在像 Windows 2000 这样的操作系统中,即使没有软盘驱动器,“我的电脑”中也会显示“A:/”条目。我添加了这一项以避免此类异常。现在,如果文件被拖放到具有我们声明的拖放前缀和“.tmp”扩展名的位置,它将被事件处理程序捕获。
我认为我们在Program.cs部分已完成,将继续进行。因此,现在我们需要处理我们为Hashtable
中所有FileSystemWatch
对象创建的事件,这些对象监视文件的创建。
private static void FileWatcherCreated(object sender, FileSystemEventArgs e)
{
try
{
string dropedFilePath = e.FullPath;
if (dropedFilePath.Trim() != string.Empty && objDragItem != null)
{
string dropPath = dropedFilePath.Substring(0,
dropedFilePath.LastIndexOf('\\'));
if (File.Exists(dropedFilePath))
File.Delete(dropedFilePath);
//Use your Download Code here
MessageBox.Show(String.Format("{0} dropped to {1}",
objDragItem.ToString(),dropPath));
}
objDragItem = null;
}
catch (Exception ex)
{
//Handle your exception here
}
}
在这里,我们只需要从路径中删除文件名。我还删除了那里被拖放的临时文件。
还需要一个方法来删除所有逻辑驱动器的文件系统监视,以减少系统开销。
public static void ClearFileWatchers()
{
try
{
if (watchers != null && watchers.Count > 0)
{
for (int i = 0; i < watchers.Count; i++)
{
((FileSystemWatcher)watchers["file_watcher" + i.ToString()]).Dispose();
}
watchers.Clear();
watchers = null;
}
}
catch (Exception ex)
{
//Handle your exception here
}
}
现在,让我们转向您将要启动拖放操作的窗体或控件。在启动拖放之前,存储FileSystemWatcher
的Hashtable
被设置为 null
。您可以通过调用我们在Program.cs中刚刚编写的ClearFileWatchers()
方法来实现这一点。这可以在ListView
的MouseDown
事件中完成,前提是有一个ListViewItem
被点击。在MouseMove
(即启动拖放)时,我们可以在系统的Temp文件夹中创建临时文件,并通过调用DoDragDrop()
方法启动拖放。
private void ItemsListView_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.None)
return;
if (itemDragStart && Program.objDragItem != null)
{
dragItemTempFileName = string.Format("{0}{1}{2}.tmp",
Path.GetTempPath(), Program.DRAG_SOURCE_PREFIX,
ItemsListView.SelectedItems[0].Text);
try
{
Util.CreateDragItemTempFile(dragItemTempFileName);
string[] fileList = new string[] { dragItemTempFileName };
DataObject fileDragData = new DataObject(DataFormats.FileDrop, fileList);
DoDragDrop(fileDragData, DragDropEffects.Move);
ClearDragData();
}
catch (Exception ex)
{
//Handle your exception here
}
}
}
在这里,Util
是一个静态类,通过使用CreateDragItemTempFIle()
方法,我在系统的Temp文件夹中创建了临时文件。在该方法中,请确保Temp文件夹中已经不存在同名的文件。否则,它将被替换,并且监视Temp文件夹的FileSystemWatcher
将不会触发Created
事件。最好在创建新文件之前删除已存在的临时文件。
这就完成了。
关注点
这只会完成拖放文件的临时工作。我仍然认为我需要重新研究 Shell Extensions。如果您打算将文件拖放到单个机器上,这可以正常工作。但是,如果您需要将其拖放到网络上的另一台机器,这将不起作用。这时,只有 Shell 才能帮到您。
历史
- 发布日期:2008 年 1 月 26 日。