65.9K
CodeProject 正在变化。 阅读更多。
Home

拖放到 Windows 文件夹 - C#

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (9投票s)

2008年1月26日

CPOL

4分钟阅读

viewsIcon

75102

downloadIcon

3209

无需使用 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    
    }
}

现在,让我们转向您将要启动拖放操作的窗体或控件。在启动拖放之前,存储FileSystemWatcherHashtable被设置为 null。您可以通过调用我们在Program.cs中刚刚编写的ClearFileWatchers()方法来实现这一点。这可以在ListViewMouseDown事件中完成,前提是有一个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 日。
© . All rights reserved.