如何从控件中拖放文件到资源管理器

时间:2023-02-07 09:08:47
最近在做一个C/S模式的网络U盘,思想是这样的:我们都知道网吧中的电脑都是装了还原机制的,也就是说网民从网上下载了一些文件,如果操作不当电脑突然重启了,那么下载到本地的文件重启后会被删除。为了方便网民在网吧上网的时候能保存自己的私有文件我们在服务器磁盘上开辟一大片空间,网民可以通过客户端软件申请一个账号和密码,服务端会在相应的目录下以这个账号为文件夹名建立这个网民的私有文件的存放位置;网民通过账号和密码可以登陆到服务器查看自己的文件(通过客户端软件的TListView,即文件显示在TListView中),上传文件到服务器时网民可以通过从资源管理器中拖放文件到TListView中,但是下载文件时我们能不能实现从TListView中拖放文件到本地资源管理器呢????改如何实现呢???

30 个解决方案

#1


客户端如下图所示:
如何从控件中拖放文件到资源管理器

#2


  拖动的时候,判断一下窗口的句柄,是否是资源管理器的,如果是,还要判断鼠标资源管理器的当前路径,然后实现复制。思路应该是这样的吧。

#3


如何获得资源管理器的句柄?上面这位大侠能否给出大概的代码?

#4


   去进程里找。

#5


偶的Blog里有:
http://blog.csdn.net/Waiting4you/archive/2007/05/06/1597971.aspx
http://blog.csdn.net/Waiting4you/archive/2007/05/06/1597967.aspx
http://blog.csdn.net/Waiting4you/archive/2007/05/06/1597968.aspx

#6


看了你的blog,有点似懂非懂。首先当我从“网络U盘”中托出文件时要进行网络传输(也就是说要从服务器上下载所拖放的文件,因为客户端TListView中看到的文件是在服务器上的),这个过程如何实现呢?也就是说网络传输的代码该写在什么地方呢?是不是写在重载的DoDragDrop函数里,这样当拖放到资源管理器中是会自动调用DoDragDrop函数进行网络传输?

#7


DoDragDrop是你来调用的,一般在鼠标按下事件里调用.
对文字或图片拖曳来说,一般只是在拖曳开始是准备好数据就可以了,如我上面例子里的DoDragDrop("Hello EveryOne!");, DoDragDrop(bmp->Handle);等.

对于文件来说,最好是等拖曳完成时再准备文件,这时可以这么做:

struct TCusDropSource
: TDropSource{   // 从我的TDropSource类继承
    TCusDropSource::TCusDropSource(TDataObject *Object)
     :TDropSource(),_Object(Object){}
    HRESULT __stdcall QueryContinueDrag(BOOL fEscapePressed,DWORD grfKeyState);
    HRESULT __stdcall GiveFeedback(DWORD dwEffect)
    {
     _dwEffect=dwEffect;
        return TDropSource::GiveFeedback(dwEffect);
    }
private:
TDataObject *_Object;
    DWORD _dwEffect;
};

HRESULT __stdcall TCusDropSource::QueryContinueDrag(BOOL fEscapePressed,DWORD grfKeyState)
{
    HRESULT r=TDropSource::QueryContinueDrag(fEscapePressed,grfKeyState);
    if((r==DRAGDROP_S_DROP) && (_dwEffect==DROPEFFECT_MOVE || _dwEffect==DROPEFFECT_COPY))
    {
        //如果拖曳完成会执行到这里,我们要在这里准备文件和修改_Object
        THDROP_Files Files;
        for(...)
        {
            // 这里可以弄个进度条啥的,参考参考解压软件
            Downloadfile("http://www.abc.com/file1xxx", temppath + "\\file1xxx"); //下载你的文件,建议把它们下载到temp文件夹里(可以用GetTempPath得到)
            Files.push_back(temppath + "\\file1xxx");   
            ...       
        }
        
        _Object->ClearAndRelease();
        _Object->Add(CF_HDROP,TYMED_HGLOBAL,Files.Create_HDROP());        
    }
    return r;
}        
        
TDataObject *pDataObject=new TDataObject;
IDropSource *pDropSource=new TCusDropSource(pDataObject,lv);
THDROP_Files Files;
files.push_back("d:\temp\file1"); //先骗系统说我们要拖d:\temp\file1(这个文件不一定要存在)
pDataObject->Add(CF_HDROP,TYMED_HGLOBAL,Files.Create_HDROP());
DWORD dwEffect;
try{
    DoDragDrop(pDataObject, pDropSource, DROPEFFECT_MOVE, &dwEffect);
}
__finally{
    pDropSource->Release();
    pDataObject->ClearAndRelease();
pDataObject->Release();
}



btw, MFC里有更好的IDataObject实现,没度过不知道能不能在BCB里使用

#8


系统的拖曳功能有个缺点,就是我们不知道文件的最终位置,所以只能先放到一个自己指定的位置(如临时文件夹)
等拖曳完成才由系统帮我们移动这些文件,看看WinRar,7-zip,WinZip等等软件也是这样.

#9


"DoDragDrop是你来调用的,一般在鼠标按下事件里调用."
TListView有OnDragDrop和OnDragOver事件,也有OnMouseDown事件,如果是在鼠标按下事件里调用那就是在OnMouseDown事件里调用了,这样的话OnDragOver和OnDragDrop事件是干什么用的呢?从名字上来看好像这两个事件是用来处理拖放的! 

#10


引用 9 楼 lcfeng1982 的回复:
"DoDragDrop是你来调用的,一般在鼠标按下事件里调用." 
TListView有OnDragDrop和OnDragOver事件,也有OnMouseDown事件,如果是在鼠标按下事件里调用那就是在OnMouseDown事件里调用了,这样的话OnDragOver和OnDragDrop事件是干什么用的呢?从名字上来看好像这两个事件是用来处理拖放的! 

TListView自己的Drag事件是VCL自己的实现,只能在自己进程内部拖曳.

#11


您在博客中提到“在你的线程(注意不是进程)使用DragSource Lib函数之前,都要先调用OleInitialize初始化OLE.”
是不是说在OnMouseDown事件中创建线程,在线程函数中初始化Ole(即OleInitialize),然后调用DoDragDrop函数,最后再OleUnInitialize?

#12


不是,你给我一个email,我发一个演示给你

#13


学习啊

#14


非常感谢,我的邮箱:lcfeng333@sohu.com

另外毛毛大侠是否知道如何判断拖放到的资源管理器的路径??

#15


引用 14 楼 lcfeng1982 的回复:
非常感谢,我的邮箱:lcfeng333@sohu.com 

另外毛毛大侠是否知道如何判断拖放到的资源管理器的路径?? 

不能,它只是作为一种数据类型处理. 接收方并不总是"资源管理器",也可能是压缩软件,Outlook等其它可以接收文件拖曳的软件.

#16


已发

#17


但是winrar却可以把文件从压缩包里直接拖放至目标资源管理器,它是如何判断目标资源管理器的路径的呢??

还有别忘了把演示发到我的邮箱,谢过先,O(∩_∩)O

#18


引用 17 楼 lcfeng1982 的回复:
但是winrar却可以把文件从压缩包里直接拖放至目标资源管理器,它是如何判断目标资源管理器的路径的呢??

还有别忘了把演示发到我的邮箱,谢过先,O(∩_∩)O

已经发给你了呀,你看了就知道了.

#19


看到了,O(∩_∩)O
非常感谢!!!!
用你的方法已经实现了从控件拖放到资源管理器,但是有一个问题:当我用鼠标在TListView中通过划线来圈取要下载的文件时就会调用从资源管理器到控件的拖放,而且拖放的文件竟然是"d:\temp\file1",就是“先骗系统说我们要拖d:\temp\file1(这个文件不一定要存在)”中那个文件。因为这个文件是不存在的,所以不能上传,这个时候就会报错!而我如果把 Files.push_back("d:\temp\file1");注释掉程序就可以正常运行,当通过划线圈取文件的时候也还是会调用从资源管理器到控件的拖放,但这个时候因为没有文件,所以不会报错。不知道为什么圈取的时候会调用从资源管理器到控件的上传代码???

#20


通过调试发现:通过划线圈取的时候会调用下面的代码,然后会运行其中的WMDropFiles(WMD);
void __fastcall TFrm_Main::AppOnMessage(TMsg &msg,bool &Handle)
{
    TWMDropFiles WMD;
    if (msg.message == WM_DROPFILES)
    {
        WMD.Msg = msg.message;
        WMD.Drop = msg.wParam;
        WMD.Unused = msg.lParam;
        WMD.Result = 0;
        WMDropFiles(WMD);
        Handle = true;
    }
}

#21


学习

#22


你把窗口设置为可以接收文件,那么它自然也能接收你窗口本身拖出的文件.(就象我之前说的,接收方并不总是"资源管理器",这次接收方是你自己!)
比较幸运的是,如果你是用DragAcceptFiles定义的窗口,那么它应该不会接收以DROPEFFECT_MOVE定义的拖放操作,而我之前写的那个就是用DROPEFFECT_MOVE定义的.
所以你可以检查以下几个地方:
1. 确保这里是这样写的: DoDragDrop(pDataObject, pDropSource,  DROPEFFECT_MOVE, &dwEffect);
2. 确保在DoDragDrop(pDataObject, pDropSource, DROPEFFECT_MOVE, &dwEffect);的 后面有bMouseDown = false;语句,因为调用DoDragDrop以后,OnMouseUp事件就不会被调用了.
3. 那了保险,直接在if((r==DRAGDROP_S_DROP) && (_dwEffect==DROPEFFECT_MOVE || _dwEffect==DROPEFFECT_COPY)) 里的第一句前插入:if(_dwEffect == DROPEFFECT_COPY) return DRAGDROP_S_CANCEL;

#23


我定义的DragAcceptFiles窗口是:
void __fastcall TFrm_Main::FormCreate(TObject *Sender)
{
    DragAcceptFiles(FileList->Handle,true);
    //DragAcceptFiles(Application->Handle,true);
    Application->OnMessage = AppOnMessage;    
}
其中FileList就是图中显示文件的TTcListView,上面3个地方我都检查了,都是正确的,而且还在if((r==DRAGDROP_S_DROP) && (_dwEffect==DROPEFFECT_MOVE || _dwEffect==DROPEFFECT_COPY))里的第一句前插入:if(_dwEffect == DROPEFFECT_COPY) return DRAGDROP_S_CANCEL; 

但是问题依旧!我调试后发现当我划线圈取的时候,程序调用OnMouseDown事件中的OnDragDrop函数,然后调用HRESULT __stdcall TCusDropSource::QueryContinueDrag(BOOL fEscapePressed,DWORD grfKeyState)函数,但在此函数中并没有运行到if((r==DRAGDROP_S_DROP) && (_dwEffect==DROPEFFECT_MOVE || _dwEffect==DROPEFFECT_COPY))范围内,即这个条件不成立,我调试发现是由于_dwEffect这个变量的值是20,即不等于DROPEFFECT_MOVE也不等于DROPEFFECT_COPY,所以函数直接返回,然后就调用void __fastcall TFrm_Main::AppOnMessage(TMsg &msg,bool &Handle)函数中的WMDropFiles(WMD);

#24


上面有一点陈述错了,OnDragDrop函数是在OnMouseMove事件中调用的!!

#25


有一个官方的方法,就是使用IDropTarget代替DragAcceptFiles. 它有DragEnter,DragOver,DragLeave回调,比较灵活,可以用它用判断是否要接收文件.
不过这个说起来也比较长,你可以去google一下其用法.

不过很奇怪,我在我给你的演示里加了DragAcceptFiles(lvNetFiles->Handle)后,它自己也不会拖给自己呀?

#26


这个问题等我以后再慢慢研究,不管怎么样还是非常感谢毛大侠!!!

现在我的要求升级了(请原谅我的得寸进尺,O(∩_∩)O),能不能实现这样的功能:在TListView中点击文件右键“复制”文件,然后在资源管理器中粘贴文件?

#27


现在拖放 大文件的时候会出现 假死的情况,而且在客户端界面上鼠标一直是拖放的状态(即鼠标是箭头+矩形的形式),而如果把DoDragDrop放在一个子线程中拖放又执行不了(不知道怎么回事),不晓得毛大侠知不知道如何解决??

#28


如果是放到线程里那么线程里也要OleInitialize/OleUninitialize

#29


加了OleInitialize/OleUninitialize,拖放代码写在新线程中后,会执行DoDragDrop,但是不会执行QueryContinueDrag,我在网上查了一下,好像是说新的线程没法接收到鼠标松开的信号,可能是这个原因所以不执行QueryContinueDrag函数。

#30


引用 12 楼 waiting4you 的回复:
不是,你给我一个email,我发一个演示给你

能不能也给我发一个?
KOKYCAN@126.COM

#1


客户端如下图所示:
如何从控件中拖放文件到资源管理器

#2


  拖动的时候,判断一下窗口的句柄,是否是资源管理器的,如果是,还要判断鼠标资源管理器的当前路径,然后实现复制。思路应该是这样的吧。

#3


如何获得资源管理器的句柄?上面这位大侠能否给出大概的代码?

#4


   去进程里找。

#5


偶的Blog里有:
http://blog.csdn.net/Waiting4you/archive/2007/05/06/1597971.aspx
http://blog.csdn.net/Waiting4you/archive/2007/05/06/1597967.aspx
http://blog.csdn.net/Waiting4you/archive/2007/05/06/1597968.aspx

#6


看了你的blog,有点似懂非懂。首先当我从“网络U盘”中托出文件时要进行网络传输(也就是说要从服务器上下载所拖放的文件,因为客户端TListView中看到的文件是在服务器上的),这个过程如何实现呢?也就是说网络传输的代码该写在什么地方呢?是不是写在重载的DoDragDrop函数里,这样当拖放到资源管理器中是会自动调用DoDragDrop函数进行网络传输?

#7


DoDragDrop是你来调用的,一般在鼠标按下事件里调用.
对文字或图片拖曳来说,一般只是在拖曳开始是准备好数据就可以了,如我上面例子里的DoDragDrop("Hello EveryOne!");, DoDragDrop(bmp->Handle);等.

对于文件来说,最好是等拖曳完成时再准备文件,这时可以这么做:

struct TCusDropSource
: TDropSource{   // 从我的TDropSource类继承
    TCusDropSource::TCusDropSource(TDataObject *Object)
     :TDropSource(),_Object(Object){}
    HRESULT __stdcall QueryContinueDrag(BOOL fEscapePressed,DWORD grfKeyState);
    HRESULT __stdcall GiveFeedback(DWORD dwEffect)
    {
     _dwEffect=dwEffect;
        return TDropSource::GiveFeedback(dwEffect);
    }
private:
TDataObject *_Object;
    DWORD _dwEffect;
};

HRESULT __stdcall TCusDropSource::QueryContinueDrag(BOOL fEscapePressed,DWORD grfKeyState)
{
    HRESULT r=TDropSource::QueryContinueDrag(fEscapePressed,grfKeyState);
    if((r==DRAGDROP_S_DROP) && (_dwEffect==DROPEFFECT_MOVE || _dwEffect==DROPEFFECT_COPY))
    {
        //如果拖曳完成会执行到这里,我们要在这里准备文件和修改_Object
        THDROP_Files Files;
        for(...)
        {
            // 这里可以弄个进度条啥的,参考参考解压软件
            Downloadfile("http://www.abc.com/file1xxx", temppath + "\\file1xxx"); //下载你的文件,建议把它们下载到temp文件夹里(可以用GetTempPath得到)
            Files.push_back(temppath + "\\file1xxx");   
            ...       
        }
        
        _Object->ClearAndRelease();
        _Object->Add(CF_HDROP,TYMED_HGLOBAL,Files.Create_HDROP());        
    }
    return r;
}        
        
TDataObject *pDataObject=new TDataObject;
IDropSource *pDropSource=new TCusDropSource(pDataObject,lv);
THDROP_Files Files;
files.push_back("d:\temp\file1"); //先骗系统说我们要拖d:\temp\file1(这个文件不一定要存在)
pDataObject->Add(CF_HDROP,TYMED_HGLOBAL,Files.Create_HDROP());
DWORD dwEffect;
try{
    DoDragDrop(pDataObject, pDropSource, DROPEFFECT_MOVE, &dwEffect);
}
__finally{
    pDropSource->Release();
    pDataObject->ClearAndRelease();
pDataObject->Release();
}



btw, MFC里有更好的IDataObject实现,没度过不知道能不能在BCB里使用

#8


系统的拖曳功能有个缺点,就是我们不知道文件的最终位置,所以只能先放到一个自己指定的位置(如临时文件夹)
等拖曳完成才由系统帮我们移动这些文件,看看WinRar,7-zip,WinZip等等软件也是这样.

#9


"DoDragDrop是你来调用的,一般在鼠标按下事件里调用."
TListView有OnDragDrop和OnDragOver事件,也有OnMouseDown事件,如果是在鼠标按下事件里调用那就是在OnMouseDown事件里调用了,这样的话OnDragOver和OnDragDrop事件是干什么用的呢?从名字上来看好像这两个事件是用来处理拖放的! 

#10


引用 9 楼 lcfeng1982 的回复:
"DoDragDrop是你来调用的,一般在鼠标按下事件里调用." 
TListView有OnDragDrop和OnDragOver事件,也有OnMouseDown事件,如果是在鼠标按下事件里调用那就是在OnMouseDown事件里调用了,这样的话OnDragOver和OnDragDrop事件是干什么用的呢?从名字上来看好像这两个事件是用来处理拖放的! 

TListView自己的Drag事件是VCL自己的实现,只能在自己进程内部拖曳.

#11


您在博客中提到“在你的线程(注意不是进程)使用DragSource Lib函数之前,都要先调用OleInitialize初始化OLE.”
是不是说在OnMouseDown事件中创建线程,在线程函数中初始化Ole(即OleInitialize),然后调用DoDragDrop函数,最后再OleUnInitialize?

#12


不是,你给我一个email,我发一个演示给你

#13


学习啊

#14


非常感谢,我的邮箱:lcfeng333@sohu.com

另外毛毛大侠是否知道如何判断拖放到的资源管理器的路径??

#15


引用 14 楼 lcfeng1982 的回复:
非常感谢,我的邮箱:lcfeng333@sohu.com 

另外毛毛大侠是否知道如何判断拖放到的资源管理器的路径?? 

不能,它只是作为一种数据类型处理. 接收方并不总是"资源管理器",也可能是压缩软件,Outlook等其它可以接收文件拖曳的软件.

#16


已发

#17


但是winrar却可以把文件从压缩包里直接拖放至目标资源管理器,它是如何判断目标资源管理器的路径的呢??

还有别忘了把演示发到我的邮箱,谢过先,O(∩_∩)O

#18


引用 17 楼 lcfeng1982 的回复:
但是winrar却可以把文件从压缩包里直接拖放至目标资源管理器,它是如何判断目标资源管理器的路径的呢??

还有别忘了把演示发到我的邮箱,谢过先,O(∩_∩)O

已经发给你了呀,你看了就知道了.

#19


看到了,O(∩_∩)O
非常感谢!!!!
用你的方法已经实现了从控件拖放到资源管理器,但是有一个问题:当我用鼠标在TListView中通过划线来圈取要下载的文件时就会调用从资源管理器到控件的拖放,而且拖放的文件竟然是"d:\temp\file1",就是“先骗系统说我们要拖d:\temp\file1(这个文件不一定要存在)”中那个文件。因为这个文件是不存在的,所以不能上传,这个时候就会报错!而我如果把 Files.push_back("d:\temp\file1");注释掉程序就可以正常运行,当通过划线圈取文件的时候也还是会调用从资源管理器到控件的拖放,但这个时候因为没有文件,所以不会报错。不知道为什么圈取的时候会调用从资源管理器到控件的上传代码???

#20


通过调试发现:通过划线圈取的时候会调用下面的代码,然后会运行其中的WMDropFiles(WMD);
void __fastcall TFrm_Main::AppOnMessage(TMsg &msg,bool &Handle)
{
    TWMDropFiles WMD;
    if (msg.message == WM_DROPFILES)
    {
        WMD.Msg = msg.message;
        WMD.Drop = msg.wParam;
        WMD.Unused = msg.lParam;
        WMD.Result = 0;
        WMDropFiles(WMD);
        Handle = true;
    }
}

#21


学习

#22


你把窗口设置为可以接收文件,那么它自然也能接收你窗口本身拖出的文件.(就象我之前说的,接收方并不总是"资源管理器",这次接收方是你自己!)
比较幸运的是,如果你是用DragAcceptFiles定义的窗口,那么它应该不会接收以DROPEFFECT_MOVE定义的拖放操作,而我之前写的那个就是用DROPEFFECT_MOVE定义的.
所以你可以检查以下几个地方:
1. 确保这里是这样写的: DoDragDrop(pDataObject, pDropSource,  DROPEFFECT_MOVE, &dwEffect);
2. 确保在DoDragDrop(pDataObject, pDropSource, DROPEFFECT_MOVE, &dwEffect);的 后面有bMouseDown = false;语句,因为调用DoDragDrop以后,OnMouseUp事件就不会被调用了.
3. 那了保险,直接在if((r==DRAGDROP_S_DROP) && (_dwEffect==DROPEFFECT_MOVE || _dwEffect==DROPEFFECT_COPY)) 里的第一句前插入:if(_dwEffect == DROPEFFECT_COPY) return DRAGDROP_S_CANCEL;

#23


我定义的DragAcceptFiles窗口是:
void __fastcall TFrm_Main::FormCreate(TObject *Sender)
{
    DragAcceptFiles(FileList->Handle,true);
    //DragAcceptFiles(Application->Handle,true);
    Application->OnMessage = AppOnMessage;    
}
其中FileList就是图中显示文件的TTcListView,上面3个地方我都检查了,都是正确的,而且还在if((r==DRAGDROP_S_DROP) && (_dwEffect==DROPEFFECT_MOVE || _dwEffect==DROPEFFECT_COPY))里的第一句前插入:if(_dwEffect == DROPEFFECT_COPY) return DRAGDROP_S_CANCEL; 

但是问题依旧!我调试后发现当我划线圈取的时候,程序调用OnMouseDown事件中的OnDragDrop函数,然后调用HRESULT __stdcall TCusDropSource::QueryContinueDrag(BOOL fEscapePressed,DWORD grfKeyState)函数,但在此函数中并没有运行到if((r==DRAGDROP_S_DROP) && (_dwEffect==DROPEFFECT_MOVE || _dwEffect==DROPEFFECT_COPY))范围内,即这个条件不成立,我调试发现是由于_dwEffect这个变量的值是20,即不等于DROPEFFECT_MOVE也不等于DROPEFFECT_COPY,所以函数直接返回,然后就调用void __fastcall TFrm_Main::AppOnMessage(TMsg &msg,bool &Handle)函数中的WMDropFiles(WMD);

#24


上面有一点陈述错了,OnDragDrop函数是在OnMouseMove事件中调用的!!

#25


有一个官方的方法,就是使用IDropTarget代替DragAcceptFiles. 它有DragEnter,DragOver,DragLeave回调,比较灵活,可以用它用判断是否要接收文件.
不过这个说起来也比较长,你可以去google一下其用法.

不过很奇怪,我在我给你的演示里加了DragAcceptFiles(lvNetFiles->Handle)后,它自己也不会拖给自己呀?

#26


这个问题等我以后再慢慢研究,不管怎么样还是非常感谢毛大侠!!!

现在我的要求升级了(请原谅我的得寸进尺,O(∩_∩)O),能不能实现这样的功能:在TListView中点击文件右键“复制”文件,然后在资源管理器中粘贴文件?

#27


现在拖放 大文件的时候会出现 假死的情况,而且在客户端界面上鼠标一直是拖放的状态(即鼠标是箭头+矩形的形式),而如果把DoDragDrop放在一个子线程中拖放又执行不了(不知道怎么回事),不晓得毛大侠知不知道如何解决??

#28


如果是放到线程里那么线程里也要OleInitialize/OleUninitialize

#29


加了OleInitialize/OleUninitialize,拖放代码写在新线程中后,会执行DoDragDrop,但是不会执行QueryContinueDrag,我在网上查了一下,好像是说新的线程没法接收到鼠标松开的信号,可能是这个原因所以不执行QueryContinueDrag函数。

#30


引用 12 楼 waiting4you 的回复:
不是,你给我一个email,我发一个演示给你

能不能也给我发一个?
KOKYCAN@126.COM