大概是两年前做的一个项目,跟百度云盘的拖拽下载功能基本差不多。最近有人问我,我看了一下以前的代码,感觉应该写点什么记录下来,方便以后可以直接使用。记得当时网上查了好久基本没有,问了好多人,答案大多都好像无法实现。公司当时WPF就我一个人,最后没办法,只有观察琢磨百度云的拖拽下载,观察了一周左右,最后终于自己弄出来了。
说下思路:(注意将拖拽拆分为复制和粘贴,拖起为复制,到达系统中鼠标弹起为粘贴)
1.用户在软件界面拖拽了文件或者点击了复制,如果这时候用户在系统中点击了粘贴或者文件拖拽的到系统中,我们是无法捕捉这个操作的,所以这时候我们想办法找到a.用户什么时候在系统中点击了粘贴,或者拖拽到系统中鼠标弹起了(相当于在系统中点击了粘贴);b.用户在系统中哪个文件夹下,点击了粘贴或者拖拽的鼠标弹起了,即最终的我们要下载的目录。
2.至于如何判断用户的点击粘贴动作或者拖拽的鼠标弹起操作,我们需要借助系统剪切板完成。原因:系统的文件拖拽实际也是复制粘贴过程,我们是否可以用一个中间文件,我们在软件中拖起文件时,给系统一个中间文件,到系统剪切板,后面我称这个文件为标记文件,当标记被粘贴了或者被拖拽到某一文件夹,那不就是我们需要的下载目录和最终的下载时间?
3.具体实现:用户在软件中点击复制,或者拖拽了文件(鼠标没有弹起,仅仅是复制,没有粘贴),这时候,我们需要在系统的剪切板中添加一个文件(标记文件),文件路径随意,不要改动,但是一定要隐蔽,而且不要更换,一般是C盘的AppData\Local\Temp\我们的应用程序文件夹,专业一点的都懂的,这个文件夹是干嘛的。将这个文件放入系统的剪切板,剪切板有个类型选择,选剪切,不要选复制,原因:当用户在系统中点击粘贴或者拖拽的鼠标弹起时,该文件会被切剪到用户指定的文件夹中,这个文件夹就是我们需要的文件夹路径,这个事件发生的时候,就是我们需要干活的时候,我们需要干的,最重要的一步,就是监听这个C盘的AppData\Local\Temp\我们的应用程序文件夹,查看中间文件状态(是否被剪切)。
4.最后一步:监听。获取当前系统中被激活的窗口,如果当前被激活的是文件夹,那么就监听,至于怎么获取被激活的窗体,网上有,要用win32函数,我就不多说了,后续代码也可以参考。
5.被监听的文件夹中如果出现那个标记文件被剪切走(删除了),我们就发起下载过程,同时删掉标记文件,这个下载的目的地址,就是标记文件被剪切到达的文件夹。
2.至于如何判断用户的点击粘贴动作或者拖拽的鼠标弹起操作,我们需要借助系统剪切板完成。原因:系统的文件拖拽实际也是复制粘贴过程,我们是否可以用一个中间文件,我们在软件中拖起文件时,给系统一个中间文件,到系统剪切板,后面我称这个文件为标记文件,当标记被粘贴了或者被拖拽到某一文件夹,那不就是我们需要的下载目录和最终的下载时间?
3.具体实现:用户在软件中点击复制,或者拖拽了文件(鼠标没有弹起,仅仅是复制,没有粘贴),这时候,我们需要在系统的剪切板中添加一个文件(标记文件),文件路径随意,不要改动,但是一定要隐蔽,而且不要更换,一般是C盘的AppData\Local\Temp\我们的应用程序文件夹,专业一点的都懂的,这个文件夹是干嘛的。将这个文件放入系统的剪切板,剪切板有个类型选择,选剪切,不要选复制,原因:当用户在系统中点击粘贴或者拖拽的鼠标弹起时,该文件会被切剪到用户指定的文件夹中,这个文件夹就是我们需要的文件夹路径,这个事件发生的时候,就是我们需要干活的时候,我们需要干的,最重要的一步,就是监听这个C盘的AppData\Local\Temp\我们的应用程序文件夹,查看中间文件状态(是否被剪切)。
4.最后一步:监听。获取当前系统中被激活的窗口,如果当前被激活的是文件夹,那么就监听,至于怎么获取被激活的窗体,网上有,要用win32函数,我就不多说了,后续代码也可以参考。
5.被监听的文件夹中如果出现那个标记文件被剪切走(删除了),我们就发起下载过程,同时删掉标记文件,这个下载的目的地址,就是标记文件被剪切到达的文件夹。
思路证明:把百度云的文件,随意直接往qq好友面板中拖拽,会有类似的一个标记文件存在;或者下载时观察AppData\Local\baidu这个文件夹,也会有这样一个中间文件作为标记文件,在下载的目录中也存在这样一个文件。
好了,思路是思路,最终还是要验证,最简单的验证,上代码:
1.先定义一个文件类,这个没啥好纠结的
class FileName
{
private string _Name;
public string Name
{
get { return _Name; }
set { _Name = value; }
}
public FileName(string name)
{
this.Name = name;
}
}
2.定义一个窗口类,也没啥好多说的
<Grid>
<ListView Name="listView" ItemsSource="{Binding}" MouseMove="listView_MouseMove" >
<ListView.View>
<GridView>
<GridViewColumn Header="文件名" Width="320">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Name,Mode=OneTime}" Visibility="{Binding NameVisible}"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
3.添加监听类,重点来了
class WatchPasteLogManager
{
private static FileSystemWatcher watcherPasteLog;
private static string PastePath = "";
/// <summary>
/// 开始监听临时路径下的标记文件
/// </summary>
/// <param name="path"></param>
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
public static void WatchPasteLog(string path)
{
if (null == path)
{
return;
}
watcherPasteLog = new FileSystemWatcher();
watcherPasteLog.Path = path;
watcherPasteLog.EnableRaisingEvents = true;
watcherPasteLog.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
| NotifyFilters.FileName | NotifyFilters.DirectoryName;
watcherPasteLog.Filter = "Test.temp";
watcherPasteLog.Deleted += new FileSystemEventHandler(OnChanged);
}
/// <summary>
/// 监听事件触发方法
/// </summary>
private static void OnChanged(object source, FileSystemEventArgs e)
{
if (e.ChangeType == WatcherChangeTypes.Deleted)
{
string dstPath = GetForegroundWindowPath() + "Test.temp";
if (File.Exists(dstPath))
{
//这个路径就是我们要找的用户粘贴的路径,这个方法被触发的时间,就是我们要的下载时间
PastePath = GetForegroundWindowPath();
//下载
System.Net.WebClient client = new System.Net.WebClient();
//这个地址可以替换,看具体需要,这里我找不到好的链接,直接用qq的下载链接了
client.DownloadFile("http://dldir1.qq.com/qqfile/qq/QQ6.0/11743/QQ6.0.exe", PastePath + "\\qq.exe");
watcherPasteLog.EnableRaisingEvents = false;
}
}
}
private static string ForegroundWindowPath;
public static string GetForegroundWindowPath()
{
EnumWindows(Report, 0);
return ForegroundWindowPath;
}
#region 获取当前激活的窗口路径
public delegate bool CallBack(int hwnd, int y);
[DllImport("user32.dll")]
public static extern int EnumWindows(CallBack x, int y);
[DllImport("user32.dll")]
public static extern int GetWindowText(int hwnd, StringBuilder lptrString, int nMaxCount);
[DllImport("user32.dll")]
public static extern int GetParent(int hwnd);
[DllImport("user32.dll")]
public static extern bool IsWindowVisible(int hwnd);
[DllImport("user32.Dll ")]
public static extern void GetClassName(IntPtr hwnd, StringBuilder s, int nMaxCount);
[DllImport("user32.dll ")]
public static extern IntPtr FindWindowEx(IntPtr parent, IntPtr childe, string strclass, string FrmText);
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(int hWnd);
[DllImport("user32.dll")]
public static extern IntPtr GetActiveWindow();
private static string GetFormClassName(IntPtr ptr)
{
StringBuilder nameBiulder = new StringBuilder(255);
GetClassName(ptr, nameBiulder, 255);
return nameBiulder.ToString();
}
private static string GetFormTitle(IntPtr ptr)
{
StringBuilder titleBiulder = new StringBuilder(255);
GetWindowText((int)ptr, titleBiulder, 255);
return titleBiulder.ToString();
}
public static bool Report(int hwnd, int lParam)
{
int pHwnds = (int)GetForegroundWindow();
int pHwnd = (int)GetActiveWindow();
if (pHwnd == 0 && hwnd == pHwnds && IsWindowVisible(hwnd) == true)
{
IntPtr cabinetWClassIntPtr = new IntPtr(hwnd);
string cabinetWClassName = GetFormClassName(cabinetWClassIntPtr);
string cabinetWClassTitle = GetFormTitle(cabinetWClassIntPtr);
if (cabinetWClassName.Equals("CabinetWClass", StringComparison.OrdinalIgnoreCase))
{
IntPtr workerWIntPtr = FindWindowEx(cabinetWClassIntPtr, IntPtr.Zero, "WorkerW", null);
IntPtr reBarWindow32IntPtr = FindWindowEx(workerWIntPtr, IntPtr.Zero, "ReBarWindow32", null);
IntPtr addressBandRootIntPtr = FindWindowEx(reBarWindow32IntPtr, IntPtr.Zero, "Address Band Root", null);
IntPtr msctls_progress32IntPtr = FindWindowEx(addressBandRootIntPtr, IntPtr.Zero, "msctls_progress32", null);
IntPtr breadcrumbParentIntPtr = FindWindowEx(msctls_progress32IntPtr, IntPtr.Zero, "Breadcrumb Parent", null);
IntPtr toolbarWindow32IntPtr = FindWindowEx(breadcrumbParentIntPtr, IntPtr.Zero, "ToolbarWindow32", null);
string title = GetFormTitle(toolbarWindow32IntPtr);
int index = title.IndexOf(':');
index++;
string path = title.Substring(index, title.Length - index);
if (Directory.Exists(path))
{
path = path + "\\";
if (path.StartsWith(" "))
{
path = path.Substring(1, path.Length - 1);
}
ForegroundWindowPath = path;
}
}
else if (cabinetWClassName.Equals("Progman", StringComparison.OrdinalIgnoreCase) && cabinetWClassTitle.Equals("Program Manager", StringComparison.OrdinalIgnoreCase))
{
ForegroundWindowPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + "\\";
}
}
return true;
}
#endregion
}
4.添加具体的拖拽逻辑
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<FileName> list = new List<FileName>();
list.Add(new FileName("1111"));
list.Add(new FileName("2222"));
list.Add(new FileName("3333"));
list.Add(new FileName("4444"));
listView.ItemsSource = list;
}
private void listView_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
//FileListItem item = (FileListItem)listView.SelectedItem;
if (!System.IO.Directory.Exists(System.IO.Path.GetTempPath() + "temp\\" + "\\"))
{
System.IO.Directory.CreateDirectory(System.IO.Path.GetTempPath() + "temp\\" + "\\");
}
string localPasteLog = System.IO.Path.GetTempPath() + "temp\\" + "\\" + "Test.temp";
CreateFiles(localPasteLog, DateTime.Now.ToString());
string[] files = new string[1];
files[0] = System.IO.Path.GetTempPath() + "temp\\" + "\\" + "Test.temp";
//@这一步是将我们的标记文件给系统,拖拽鼠标弹起时,系统会将这个文件剪切到指定目录,注意一定要是DragDropEffects.Move,即剪切模式
DragDrop.DoDragDrop(listView, new DataObject(DataFormats.FileDrop, files), DragDropEffects.Move /* | DragDropEffects.Link */);
//监听temp目录下的标记文件,如果被剪切走(删除),就是发生下载的时机
WatchPasteLogManager.WatchPasteLog(localPasteLog.Replace("Test.temp", ""));
}
}
public void CreateFiles(string path, string context)
{
try
{
StreamWriter f = new StreamWriter(path, true);
f.WriteLine(context);
f.Close();
}
catch (Exception ex)
{
return;
}
}
}
完成上面的,拖拽一下,就可以下载一个qq.exe,将qq.exe换成点击的那个ListViewItem代表的路径,这个对能点进来看博客的大爷来说简直就是小儿科了。用户的点击复制,在系统中去粘贴也是一样的操作,不过要修改一下剪切板,没有DragDrop.DoDragDrop可以直接使用。
这里附上剪切板,不具体说明了(copytoClipboard方法取代上面@标记处)
class FileSetToClipboard
{
public static void CopyToClipboard(string[] files, bool cut)
{
if (files == null) return;
IDataObject data = new DataObject(DataFormats.FileDrop, files);
MemoryStream memo = new MemoryStream(4);
byte[] bytes = new byte[] { (byte)(cut ? 2 : 5), 0, 0, 0 };
memo.Write(bytes, 0, bytes.Length);
data.SetData("Preferred DropEffect", memo);
Clipboard.SetDataObject(data, false); //false程序退出时不保存在剪切板
}
}
文采不怎么样,而且有bug和异常没捕捉,但是思路大致已经说清了,基本意思已经到了,大家将就着看吧。
总结:程序设计,你看到的,不一定是真的。很多时候我们无法直接获取到的东西,其实都可以用不同的方法去解决。只要有人做到了,说明必定有方法可以解决,只是你想没有想到!也就是没有做不到,只有想不到!