MFC对OLE剪贴板的支持主要集中在两个类上,分别是COleDataSource,COleDataObject。其中,COleDataSource作为操作者,而COleDataObject作为消费者,换句话说,用户需要使用COleDataSource把数据放到OLE剪贴板上,而使用COleDataObject把它取回。
将保存在全局内存中的项目放置在OLE剪贴板上,需要进行如下步骤:1.在堆上(而不是在堆栈上)创建COleDataSource对象。2.调用COleDataSource::CacheGlobalData将HGLOBAL递交给COleDataSource对象。3.调用COleDataSource::SetClipboard将对象放置在 OLE剪贴板上。
下列使用COleDataSource在OLE剪贴板上提供了ANSI文本字符串.
char szText[]="Hello,world";HANDLE hData=::GlobalAlloc(CMEM_MOVEABLB,::lstrlen(szText)+1);::lstrcpy(pData,szText);::GlobalUnlock(hData);
COleDataSource * pods=new COleDataSource;pods->CacheGlobalData(CF_TEXT,hData);pods->SetClipboard();
MFC的COleDataObject提供了从OLE剪贴板获取项目的机制。1.创建COleDataObject对象。2.调用COleDataObject::AttachClipboard将 COleDataObject连接到OLE剪贴板。3.使用COleDataObject::GetGlobalData获取项目。4.释放由GetGlobalData返回的全局内存块。
以下为例: char szTect[]; COleDataObject pdo; pdo.AttachClipboard(); HANDLE hData=pdo.GetGlobalData(CF_TEXT); if(hData!=NULL) LPCSTR pData=(LPCSTR)::Globallock(hData); if(::lstrlen(pData<BUFLEN)) ::lstrcpy(szText,pData); ::GlobalUnlock(hData); ::GlobalFree(hData);
注意,我们以上讨论的OLE剪贴板储存媒介都是内存,下面我们将要讨论储存媒介为非内存的情况。
COleDataSource::CacheGlobalData和COleDataObject::GetGlobalData与全局内存密不可分。但是您可以使用更一般的COleDataSource::CacheData和COleDataObject::GetData函数在其他数据类型的媒介中传送数据。
下面例子说明如何使用文件作为传送媒体,通过前贴板来传送文本字符串。字符串首先复制到临时文件中。然后用描述文件的信息和文件包含的数据初始化FORMATETC和STGMEDIUM结构。最后信息被传送给COleDataSource::CacheData,并用COleDataSource::SetClipboar将数据对象放置在剪贴板上。
char szText[]="Hello,world"TCHAR szPath[Max_path],szFileName[Max_path];::GetTempPath(sizeof(szPath)/sizeof(TCHAR),szPath);::GetTempFileName(szPath,_T("tmp"),0,szFileName);//分配临时文件名称。
CFile file;if(file.Open(szFileName,CFile::modeCreate|CFile::modeWrite)){ file.Write(szText,::lstrlen(szText)+1); file.Close();
LPWSTR pwszFileName=(LPWSTR)::CoTaskMemAlloc(MAX_PATH * sizeof(WCHAR)); #ifdef UNICODE ::lstrcpy(pwszFileName,szFileName); #else ::MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,szFileName,-1,pwszFileName,MAX_PATE); #endif FORMATETC fe={CF_TEXT,NULL,DVASPECT_CONTENT,-1,TYMED_FILE};
STGMEDIUM stgm; stgm.tymed=TYMED_FILE; stgm.lpszFileName=pwszFileName; stgm.pUnkForRelease=NULL; COleDataSource * pods=new COleDataSource; pods->CacheData(CF_TEXT,&stgm,&fe); pods->setClipboard();}
在消费者端,您可以使用COleDataObject::GetData从剪贴板获取字符串char szText[BUFLEN];STGMEDIUM stgm;FORMATETC fe={CF_TEXT,NULL,DVASPECT_CONTENT,-1,TYMED_FILE;}COleDataObject pdo;pdo.AttachClipboard();if(pdo.GetData(CF_TEXT,&stgm,&fe)&&stgm.tymed==TYMED_FILE){TCHAR szFileName|Max_Path;}
#ifdef UNICODE ::lstrcpy(szFileName,stgm,lpszFileName);#else ::WideCharToMultiByte(CP_ACP,0,stgm,lpszf|leName,_T,szFileName,sizeof(szFileName)/sizeof(TCHAR),NULL,NULL);#endif CFile file; if(file.Open(szFileName,CFile::modeRead))|DWORD dwSize=file.GetLength(); if(dwSize<BUFLEN) file.Read(szText,(UNIT)dwSize); file.Close(); } ::ReleaseStgMedium(&stgm);}
在读取OLE剪贴板内容是,用户可以直接使用COleDataObject::GetFileData取代GetData函数。
__________________________________________
一)ChangeClipboardChain将剪贴的连接从一个句柄转到下一个句柄。BOOL ChangeClipboardChain(HWND hWndRemove, // handle to window to removeHWND hWndNewNext // handle to next window);(1)hWndRemove表示第一个窗口的句柄(断开)。(2)hWndNewNext表示第二个窗口的句柄(连接)。注意,在使用之前应该使用SetClipboardViewer事先进行窗口句柄的连接。(二)CloseClipboard关闭剪贴板。BOOL CloseClipboard(VOID)//VOID意思是空白。本函数没有参数,事先应该用OpenClipboard函数打开过剪贴板。(三)CountClipboardFormats不管剪贴板是什么格式,全部转化为数据格式。int CountClipboardFormats(VOID)本函数没有参数。(四)EmptyClipboard清空剪贴板。BOOL EmptyClipboard(VOID)本函数没有参数。(五)EnumClipboardFormats使剪贴板内的格式转变成指定格式。UINT EnumClipboardFormats(UINT format // specifies a known available clipboard format);其中format表示的是将要转化成的格式。该参数的意义可参照后面。(六)GetClipboardData获取剪贴板内的数据。HANDLE GetClipboardData(UINT uFormat // clipboard format);其中format表示的是剪贴板内数据的格式。该参数的意义可参照后面。(七)GetClipboardFormatName获取剪贴板内数据格式的名称。int GetClipboardFormatName(UINT format, // clipboard format to retrieveLPTSTR lpszFormatName, // address of buffer for nameint cchMaxCount // length of name string in characters);(1)format表示的意义同前,应该是不事先规定格式;(2)lpszFormatName表示的是格式名称地址;(3)cchMaxCount剪贴板内数据的长度。(八)GetClipboardOwner获取当前剪贴板是属于哪一个窗口的句柄。HWND GetClipboardOwner(VOID)返回那个窗口的句柄。(九)GetClipboardSequenceNumber返回剪贴板序号。DWORD GetClipboardSequenceNumber(VOID)(十)GetClipboardViewer返回剪贴板属于窗口的句柄。HWND GetClipboardViewer(VOID)(十一)GetOpenClipboardWindow返回打开剪贴板的那个窗口句柄。HWND GetOpenClipboardWindow(VOID)(十二)GetPriorityClipboardFormatint GetPriorityClipboardFormat(UINT *paFormatPriorityList, // address of priority listint cFormats // number of entries in list);(十三)IsClipboardFormatAvailable判断剪贴板的格式。BOOL IsClipboardFormatAvailable(UINT format // clipboard format);其中format表示的是剪贴板内数据的格式。该参数的意义可参照后面。(十四)OpenClipboard打开剪贴板。BOOL OpenClipboard(HWND hWndNewOwner // handle to window opening clipboard);返回剪贴板的句柄。(十五)RegisterClipboardFormat注册新的剪贴板格式。UINT RegisterClipboardFormat(LPCTSTR lpszFormat // address of name string);lpszFormat新的剪贴板格式名称。(十六)SetClipboardData设置剪贴板内的数据。HANDLE SetClipboardData(UINT uFormat, // clipboard formatHANDLE hMem // data handle);uFormat表示的是要放进剪贴板数据的格式;hMem表示数据的地址指针。(十七)SetClipboardViewer将剪贴板内容连接到窗口。HWND SetClipboardViewer(HWND hWndNewViewer // handle to clipboard viewer window);hWndNewViewer表示要连接到的那个窗口句柄。上文中剪贴板格式Format的可选参数如下:CF_BITMAP位图格式;CF_DIBCF_DIBV5CF_DIFCF_DSPBITMAPCF_DSPENHMETAFILECF_DSPMETAFILEPICTCF_DSPTEXTCF_ENHMETAFILECF_GDIOBJFIRSTCF_GDIOBJLASTCF_HDROPCF_LOCALECF_METAFILEPICTCF_OEMTEXTCF_OWNERDISPLAYCF_PALETTECF_PENDATACF_PRIVATEFIRSTCF_PRIVATELASTCF_RIFFCF_SYLKCF_TEXT文本格式;CF_WAVE音乐格式;CF_TIFFCF_UNICODETEXTWindows剪贴板 Windows剪贴板是一种比较简单同时也是开销比较小的IPC(InterProcess Communication,进程间通讯)机制。Windows系统支持剪贴板IPC的基本机制是由系统预留的一块全局共享内存,用来暂存在各进程间进行交换的数据:提供数据的进程创建一个全局内存块,并将要传送的数据移到或复制到该内存块;接受数据的进程(也可以是提供数据的进程本身)获取此内存块的句柄,并完成对该内存块数据的读取。 为使剪贴板的这种IPC机制更加完善和便于使用,需要解决好如下三个问题:提供数据的进程在结束时 Windows系统将删除其创建的全局内存块,而接受数据的进程则希望在其退出后剪贴板中的数据仍然存在,可以继续为其他进程所获取;能方便地管理和传送剪贴板数据句柄;能方便设置和确定剪贴板数据格式。为完善上述功能,Windows提供了存在于USER32.dll中的一组API函数、消息和预定义数据格式等,并通过对这些函数、消息的使用来管理在进程间进行的剪贴板数据交换。 Windows系统为剪贴板提供了一组API函数和多种消息,基本可以满足编程的需要。而且Windows还为剪贴板预定义了多种数据格式。通过这些预定义的格式,可以使接收方正确再现数据提供方放置于剪贴板中的数据内容。 文本剪贴板和位图剪贴板的使用 这两种剪贴板是比较常用的。其中,文本剪贴板是包含具有格式CF_TEXT的字符串的剪贴板,是最经常使用的剪贴板之一。在文本剪贴板中传递的数据是不带任何格式信息的ASCII字符。若要将文本传送到剪贴板,可以先分配一个可移动全局内存块,然后将要复制的文本内容写入到此内存区域。最后调用剪贴板函数将数据放置到剪贴板:注意: 下面代码中: 1、HANDLE hGlobalMemory = GlobalAlloc(GHND, dwLength + 1);分配内存长度必须是dwLength + 1,否则在调用SetClipboardData时会出错;可以通过调用 int i2 = ::GlobalSize(hGlobalMemory );来查看分配的长度。 2、hGlobalMemory不能释放,即不能调用::GlobalFree(hGlobalMemory),因为如果在一个程序中还要粘贴的话就必须不能释放,否则在同一程序中粘贴时获得的指针为NULL,但如果只在别的程序中粘贴则可以释放。 以上两条是在编制HsfBrowserCtl(HOOPS三维浏览控件)时总结出来的。可参看原码中的复制、粘贴部分。 DWORD dwLength = 100; // 要复制的字串长度HANDLE hGlobalMemory = GlobalAlloc(GHND, dwLength + 1); // 分配内存LPBYTE lpGlobalMemory = (LPBYTE)GlobalLock(hGlobalMemory); // 锁定内存for (int i = 0; i 〈 dwLength; i++) // 将"*"复制到全局内存块 *lpGlobalMemory++ = '*'; GlobalUnlock(hGlobalMemory); // 锁定内存块解锁 HWND hWnd = GetSafeHwnd(); // 获取安全窗口句柄 ::OpenClipboard(hWnd); // 打开剪贴板 ::EmptyClipboard(); // 清空剪贴板 ::SetClipboardData(CF_TEXT, hGlobalMemory); // 将内存中的数据放置到剪贴板 ::CloseClipboard(); // 关闭剪贴板 这里以OpenClipboard()打开剪贴板,并在调用了EmptyClipboard()后使hWnd指向的窗口成为剪贴板的拥有者,一直持续到 CloseClipboard()函数的调用。在此期间,剪贴板为拥有者所独占,其他进程将无法对剪贴板内容进行修改。 从剪贴板获取文本的过程与之类似,首先打开剪贴板并获取剪贴板的数据句柄,如果数据存在就拷贝其数据到程序变量。由于GetClipboardData()获取的数据句柄是属于剪贴板的,因此用户程序必须在调用CloseClipboard()函数之前使用它: HWND hWnd = GetSafeHwnd(); // 获取安全窗口句柄::OpenClipboard(hWnd); // 打开剪贴板HANDLE hClipMemory = ::GetClipboardData(CF_TEXT);// 获取剪贴板数据句柄DWORD dwLength = GlobalSize(hClipMemory); // 返回指定内存区域的当前大小LPBYTE lpClipMemory = (LPBYTE)GlobalLock(hClipMemory); // 锁定内存m_sMessage = CString(lpClipMemory); // 保存得到的文本数据GlobalUnlock(hClipMemory); // 内存解锁::CloseClipboard(); // 关闭剪贴板 大多数应用程序对图形数据采取的是位图的剪贴板数据格式。位图剪贴板的使用与文本剪贴板的使用是类似的,只是数据格式要指明为CF_BITMAP,而且在使用SetClipboardData()或GetClipboardData()函数时交给剪贴板或从剪贴板返回的是设备相关位图句柄。下面这段示例代码将把存在于剪贴板中的位图数据显示到程序的客户区: HWND hWnd = GetSafeHwnd(); // 获取安全窗口句柄::OpenClipboard(hWnd); // 打开剪贴板HANDLE hBitmap = ::GetClipboardData(CF_BITMAP); // 获取剪贴板数据句柄HDC hDC = ::GetDC(hWnd); // 获取设备环境句柄HDC hdcMem = CreateCompatibleDC(hDC); // 创建与设备相关的内存环境SelectObject(hdcMem, hBitmap); // 选择对象SetMapMode(hdcMem, GetMapMode(hDC)); // 设置映射模式BITMAP bm; // 得到位图对象GetObject(hBitmap, sizeof(BITMAP), &bm);BitBlt(hDC, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY); //位图复制::ReleaseDC(hWnd, hDC); // 释放设备环境句柄DeleteDC(hdcMem); // 删除内存环境::CloseClipboard(); // 关闭剪贴板多数据项和延迟提交技术 要把数据放入剪贴板,在打开剪贴板后一定要调用EmptyClipboard()函数清除当前剪贴板中的内容,而不可以在原有数据项基础上追加新的数据项。但是,可以在EmptyClipboard()和CloseClipboard()调用之间多次调用SetClipboardData()函数来放置多个不同格式的数据项。例如: OpenClipboard(hWnd);EmptyClipboardData();SetClipboardData(CF_TEXT, hGMemText);SetClipboardData(CF_BITMAP, hBitmap);CloseClipboard(); 这时如果用CF_TEXT或CF_BITMAP等格式标记去调用IsClipboardFormatAvailable()都将返回TRUE,表明这几种格式的数据同时存在于剪贴板中。以不同的格式标记去调用GetClipboardData()函数可以得到相应的数据句柄。 对于多数据项的剪贴板数据,还可以用CountClipboardFormats()和EnumClipboardFormats()函数得到当前剪贴板中存在的数据格式数目和具体的数据格式。EnumClipboardFormats()的函数原型为: UINT EnumClipboardFormats(UINT format); 参数format指定了剪贴板的数据格式。如果成功执行将返回format指定的格式的下一个数据格式值,如果format为最后的数据格式值,那么将返回0。由此不难写出处理剪贴板中所有格式数据项的程序段代码: UINT format = 0; // 从第一种格式值开始枚举OpenClipboard(hWnd);while(format = EnumClipboardFormats(format)){…… // 对相关格式数据的处理}CloseClipboard(); 在数据提供进程创建了剪贴板数据后,一直到有其他进程获取剪贴板数据前,这些数据都要占据内存空间。如在剪贴板放置的数据量过大,就会浪费内存空间,降低对资源的利用率。为避免这种浪费,可以采取延迟提交(Delayed rendering)技术,即由数据提供进程先创建一个指定数据格式的空(NULL)剪贴板数据块,直到有其他进程需要数据或自身进程要终止运行时才真正提交数据。 延迟提交的实现并不复杂,只需剪贴板拥有者进程在调用SetClipboardData()将数据句柄参数设置为NULL 即可。延迟提交的拥有者进程需要做的主要工作是对WM_RENDERFORMAT、WM_DESTORYCLIPBOARD和 WM_RENDERALLFORMATS等剪贴板延迟提交消息的处理。 当另一个进程调用GetClipboardData()函数时,系统将会向延迟提交数据的剪贴板拥有者进程发送WM_RENDERFORMAT消息。剪贴板拥有者进程在此消息的响应函数中应使用相应的格式和实际的数据句柄来调用SetClipboardData()函数,但不必再调用OpenClipboard()和EmptyClipboard()去打开和清空剪贴板了。在设置完数据有也无须调用CloseClipboard()关闭剪贴板。如果其他进程打开了剪贴板并且调用EmptyClipboard()函数去清空剪贴板的内容,接管剪贴板的拥有权时,系统将向延迟提交的剪贴板拥有者进程发送WM_DESTROYCLIPBOARD消息,以通知该进程对剪贴板拥有权的丧失。而失去剪贴板拥有权的进程在收到该消息后则不会再向剪贴板提交数据。另外,在延迟提交进程在提交完所有要提交的数据后也会收到此消息。如果延迟提交剪贴板拥有者进程将要终止,系统将会为其发送一条WM_RENDERALLFORMATS消息,通知其打开并清除剪贴板内容。在调用 SetClipboardData()设置各数据句柄后关闭剪贴板。 下面这段代码将完成对数据的延迟提交,WM_RENDERFORMAT消息响应函数OnRenderFormat()并不会立即执行,当有进程调用GetClipboardData()函数从剪贴板读取数据时才会发出该消息。在消息处理函数中完成对数据的提交: 进行延迟提交: HWND hWnd = GetSafeHwnd(); // 获取安全窗口句柄::OpenClipboard(hWnd); // 打开剪贴板::EmptyClipboard(); // 清空剪贴板::SetClipboardData(CF_TEXT, NULL); // 进行剪贴板数据的延迟提交::CloseClipboard(); // 关闭剪贴板 在WM_RENDERFORMAT消息的响应函数中: DWORD dwLength = 100; // 要复制的字串长度HANDLE hGlobalMemory = GlobalAlloc(GHND, dwLength + 1); // 分配内存块LPBYTE lpGlobalMemory = (LPBYTE)GlobalLock(hGlobalMemory); // 锁定内存块for (int i = 0; i 〈 dwLength; i++) // 将"*"复制到全局内存块*lpGlobalMemory++ = '*';GlobalUnlock(hGlobalMemory); // 锁定内存块解锁::SetClipboardData(CF_TEXT, hGlobalMemory); // 将内存中的数据放置到剪贴板 DSP和自定义数据格式的使用 Windows系统预定义了三个带“DSP”前缀的数据格式:CF_DSPTEXT、CF_DSPBITMAP和 CF_DSPMETAFILEPICT。这是一些伪标准格式,用于表示在程序中定义的私有剪贴板数据格式。对于不同的程序,这些格式的规定是不同的,因此这些格式只针对某一具体程序的不同实例才有意义。 为使用DSP数据格式,必须确保进程本身与剪贴板拥有者进程同属一个程序。可以调用GetClipboardOwner()函数来获取剪贴板拥有者窗口句柄,并调用GetClassName()来获取窗口类名:HWND hClipOwner = GetClipboardOwner();GetClassName(hClipOwner, &ClassName, 255); 如果剪贴板拥有者窗口类名同本进程的窗口类名一致,就可以使用带有DSP前缀的剪贴板数据格式了。除了使用Windows预定义的剪贴板数据格式外,也可以在程序中使用自定义的数据格式。对于自定义的数据格式lpszFormat,可以调用RegisterClipboardFormat()函数来登记,并获取其返回的格式标识值:UINT format = RegisterClipboardFormat(lpszFormat); 对此返回的格式标识值的使用与系统预定义的格式标识是一样的。可以通过GetClipboardFormatName()函数来获取自定义格式的ASCII名。 小结 本文主要对Windows编程中的剪贴板机制作了较为深入的讨论,对其中常用的文本、位图、DSP和自定义数据格式的使用方法以及多数据项和延迟提交等重要技术一并做了阐述。并给出了具体的程序示例代码,使读者能够更好的掌握剪贴板机制的使用。功能实现:复制位图到剪切阪。今日看了下COleDataSource的原码和网上的文章。COleDataSource用内嵌类实现了IDataObject接口//COPY HBITMAP To CilpBoardvoid CMyView::OnEditCopy(){ COLORREF BACKGROUND_COLOR = #ffffff; tagSTGMEDIUM * data; CBitmap * junk; COleDataSource* pData = new COleDataSource; data = new tagSTGMEDIUM; junk = new CBitmap(); CClientDC cdc(this); CDC dc; dc.CreateCompatibleDC(&cdc); CRect client; //replace this with something that calculates //the proper rectangle size GetClientRect(client); junk->CreateCompatibleBitmap(&cdc,client.Width(),client.Height()); dc.SelectObject(junk); CBrush fill(BACKGROUND_COLOR); dc.FillRect(client,&fill); //replace this with something that draws to a CDC OnDraw(&dc); data->tymed = TYMED_GDI; data->hBitmap = HBITMAP(*junk); pData->CacheData( CF_BITMAP, data ); pData->SetClipboard(); delete data; delete junk; //delete pData;}另转载一篇文章,讲的不怎样,但可参看一下利用MFC实现对象拖放 对象拖放是指对某一指定的对象,利用鼠标拖动的方法,在不同应用的窗口之间、同一应用的不同窗口之间或同一应用的同一窗口内进行移动、复制(粘贴)等操作的技术。 利用对象拖放,可以为用户提供方便、直观的操作界面。实现对象拖放技术,需要了解、使用MFC的CView、COleDataSource和COleDropTarget等类,并利用这些类协同工作。本文讨论了对象拖放技术,并研究了如何利用MFC实现该技术。利用MFC实现对象拖放,编程比较容易,代码可读性好。修改稿利用MFC实现对象拖放1.对象拖放概念 对象拖放是指对某一指定的对象,利用鼠标拖动的方法,在不同应用的窗口之间、同一应用的不同窗口之间或同一应用的同一窗口内进行移动、复制(粘贴)等操作的技术。 对象拖放是在操作系统的帮助下完成的。 要开始一次拖动, 首先需要指定或生成被拖动的对象,然后指定整个拖放操作过程所使用的数据格式,并按指定的数据格式提供数据,最后启动对象拖放操作;当对象在某一窗口内落下时,拖放过程结束,接收拖放对象的窗口按指定的数据格式提取有关数据,并根据提取的数据生成对象。2.MFC中用于对象拖放的类MFC(Microsoft Foundation ClassLibrary)为实现对象拖放提供了如下三个类。为便于后边的讨论我们先来熟悉一下这些类。2.1.COleDataSource。用于启动一次拖放操作,并向系统提供拖放对象的数据。类中的成员函数有如下三种:a.设定提供数据的方式和使用的数据格式。提供数据的方式有两种,一种是即时方式,另一种是延迟方式;即时方式需要在拖动开始之前提供数据;延迟方式不需要立即提供数据,当系统请求有关数据时,由OnRenderData()等虚函数提供所需的数据。可以用CacheGlobalData()等函数指定使用即时方式提供数据,也可以用DelayRenderData()等函数指定使用延时方式提供数据。b.响应请求,提供数据。应当重载OnRenderFileData()或其他相应的虚函数,以提供有关数据(后边将详细讨论)。c.实施拖放操作。调用函数DoDragDrop(),开始实施拖放操作。2.2.OleDataTarget。用于准备接收拖放对象的目标窗口;一个窗口要想能够接收拖放对象,必须包含一个COleDataTarget对象,并注册该对象。类中主要成员函数:a.注册。函数Register()注册该对象,以便使窗口能够接收拖放对象。b.响应拖放过程中的动作(虚成员函数) 当鼠标首次进入窗口时系统将调用OnDragEnter(),当鼠标移出窗口时系统将调用OnDragLeave(), 当鼠标在窗口内移动,系统将重复调用调用OnDragOver(),当对象在窗口内落下调用OnDrop()。2.3.OleDataObject.用于接收拖放对象,类中主要成员函数有两种:a.确定可以使用的数据格式。IsDataAvailable()等函数确定指定数据格式是否可用;b.获取数据。GetData()、GetFileData()等函数用于按指定数据格式获得数据。3.利用MFC实现对象拖放 要实现一次对象拖放,需要做三方面的工作:对象所在的窗口准备拖放对象并启拖动操作,接受对象的窗口响应有关拖放消息并接受落下的对象,以及拖放完成时的后期处理。以下分别予以介绍。3.1. 拖动操作的启动。拖放操作一般是从单击鼠标左键开始。在消息WM_LBUTTONDOWN的响应函数OnLButtonDown(...)中,首先要判定是否选定了某一对象,如果未选定或选定多个,则不能进行拖放操作;如果选定了一个对象,则可以进行拖放操作。要启动一次拖放操作,需要先准备一个COleDataSource对象。注意到类COleClientIten和类COleServerItem都是从类COleDataSource上派生的,如果选定的是COleClientItem对象或者是COleServerItem对象,则可以直接使用;否则,需要生成一个COleDataSource对象,值得注意的是:需要象上文中所说的,应该指定使用的数据格式,并按指定格式提供对象的有关数据。下面给出准备数据源的例子:class myDataSource: public COleDataSourcepublic:COLORREF color;CString str;protected:virtual BOOL OnRenderFileData(LPFORMATETC,CFile*);//......};BOOL myDataSource::OnRenderFileData(LPFORMATETC lpFormatEtc,CFile* pFile)if(lpFormatEtc->cfFormat==CF_TEXT)pFile.Write("Test DragDrop",13); //Magic StringpFile.Write(&color,sizeof(COLORREF));int len= str.GetLength();pFile.Write(&len,sizeof(int));pFile.Write(str,len);return TRUE;COleDataSource::OnRenderFileData(lpFormatEtc,pFile);return FALSE; 有了以上数据源之后,就可以在消息WM_LBUTTON的响应函数OnLButtonDown()中,按如下方式,指定使用的数据格式:myDataSource* pItemDragDrop=new myDataSource;pItemDragDrop->str="This string will dragdrop to another place";pItemDragDrop->DelayRenderFileData(CF_TEXT,NULL); 指定好使用的数据格式之后,调用此对象的成员函数DoDragDrop(...),启动对象拖放操作。需要注意的是,函数DoDragDrop(...)并不立即返回,而是要等到鼠标按钮弹起之后。3.2. 拖放对象的接收。缺省情况下,一般的窗口是不能接收拖放对象的;要使窗口可以接收拖放对象,需要在窗口类定义中加入成员对象COleDropTarget,并在生成窗口时调用函数COleDataTarget::Register()。例如:Class myView : public CScrollViewprivate:COleDropTarget oleTarget;protected:virtual int OnCreate(LPCREATESTRUCT);//......int myView::OnCreate(LPCREATESTRUCT lpCreateStruct) //...... dropTarget.Register(this); return 0;为实现拖放对象的接收,还应重载CView或COleDropTarget的虚函数:COnDragMove()、OnDragEnter()和OnDrop()等。函数OnDragEnter()、OnDragMove()应根据鼠标在窗口中的位置,返回以下数值:DROPEFFECT_MOVE---表明可以把对象复制到现在的窗口、现在的位置;DROPEFFECT_COPY---表明可以把对象从原来的窗口、原来的位置移到现在的窗口、现在的位置;DROPEFFECT_NONE---表明不能在该窗口的该位置放下。下例只允许移动对象,而不允许复制对象:DROPEFFECT myView::OnDragEnter(......) return DROPEFFECT_MOVE;DROPEFFECT myView::OnDragOver(......)return DROPEFFECT_MOVE;函数OnDrop()应处理拖动对象放下后的工作。该函数的参数pDataObjec指向一个COleDataObject对象,利用指针,可以获取有关数据。该函数的一般实现是:a.检查对象的数据格式: 利用函数COleDataObject::IsDataAvailable();b.按指定的格式获取数据:利用COleDataObject::GetFileData()等函数;c.建立对象(可能与原对象相同,也可能不建立对象仅使用对象中的数据):利用以上步骤得到的数据建立对象。例如:char magic_string[13];COLORREF color;CString str;int len;myDataSource* pMyData;if(IsDataAvailable(CF_TEXT))CFile file=GetFileData(CF_TEXT);file.Read(magic_string,13);if(strncmp(magic_string,"Test DragDrop",13)==0)file.Read(&color,sizeof(COLORREF));file.Read(&len,sizeof(int));file.Read(str,len);CClientDC dc(this);dc.SetTextColor(color);dc.SetBkMode(TRANSPARENT);dc.TextOut(100,50,str,len);pMyData=new myDataSource;pMyData->color=color;pMyData->str=str;对于COleClientItem或COleServerItem对象,可以按以下方法很容易地重建对象:COleClient* pItem=GetDocument()->CreateNewItem();pItem->CreateFrom(pDataObject);3.3. 拖放操作的结束函数DoDragDrop()返回时,拖放过程结束。函数DoDragDrop()的返回值,表明了对象的拖放结果。DROPEFFECT_MOVE:对象被放到他处,需删除原对象DROPEFFECT_COPY:对象被复制到他处,不删除原对象DROPEFFECT_NONE:未能实现拖放,无需删除原对象例如:int DragEffect=pItemTracking->DoDragDrop(......);switch(DragEffect)case DROPEFFECT_MOVE:delete pItemTracking;GetDocument()->UpdateAllItems(NULL);GetDocument()->UpdateAllViews(NULL);break;case DROPEFFECT_COPY:case DROPEFFECT_NONE:default:break;*********************想构建一个自己的粘贴版需要Hook的函数写粘贴板EmptyClipboardData();//清空自己的粘贴板SetClipboardData(CF_TEXT, hGMemText);SetClipboardData(CF_BITMAP, hBitmap);读粘贴版GetClipboardData(CF_TEXT,);GetClipboardData(CF_BITMAP);