`

剪贴板和OLE拖放

    博客分类:
  • MFC
阅读更多

一、传统剪贴板

     传统剪贴板的形式非常的简单,其基本思路是当复制时程序复制一个数据副本给全局内存对象,打开剪贴板并且清空剪贴板当中的数据,将全局内存对象复制给剪贴板最后关闭剪贴板;从剪贴板中获取数据的顺序是打开剪贴板获取全局内存对象,锁定全局内存对象后从中复制数据,解锁内存对象并且关闭剪贴板。需要注意的是剪贴板在某个时刻只能被一个程序所打开,如果试图打开一个已经被其他程序打开的剪贴板就会导致API函数调用返回NULL值,由于这个机制要求所有程序在打开剪贴板后在尽可能快的时间内关闭剪贴板。
     下面是复制和粘贴的两个例程,这里把m_string当中的文本复制到剪贴板:
//复制数据
char m_string[] = "Hello,World";
if (::OpenClipboard(this->m_hWnd))
{
    HANDLE hMem = ::GlobalAlloc(GHND,::lstrlen(m_string) + 1);
    char *phandle = (char *)::GlobalLock(hMem);
    lstrcpy(phandle,m_string);
    ::GlobalUnlock(hMem);
    ::EmptyClipboard();
    ::SetClipboardData(CF_TEXT,hMem);
    ::CloseClipboard();
}
//粘贴数据
m_string[BUFFER];
if (::OpenClipboard(this->m_hWnd))
{
    HANDLE hMem = ::GetClipboardData(CF_TEXT);
    char *phandle = GlobalLock(hMem);
    if (::lstrlen(phandle) < BUFFER)
          ::lstrcpy(m_string,phandle);
     GloableUnlock(hMem);
    ::CloseClipboard();
}
     除了使用系统定义的一系列数据类型ID外我们还可以使用::RegisterClipboardFormat(LPCSTR)函数来注册自定义的剪贴板数据ID,这个API函数保证只要参数提供的字符串内容相同,就会返回相同的UINT类型的ID值。
     某些程序需要一个延迟型剪贴板,这种剪贴板的基本思想是:给剪贴板发送一个只包含数据类型ID但不包括全局内存对象的SetClipboardData调用,直至有其他程序需要该剪贴板内容数据时再调用SetClipboardData函数将数据传送给剪贴板。这种程序需要响应WM_RENDERFORMAT和WM_RENDERALLFORMAT两个消息,在处理钱一个消息时我们不必打开剪贴板而只需要直接传送数据即可而后一个消息需要打开剪贴板。
     有时在我们打开剪贴板获取其中的内容之前我们需要知道剪贴板中是否有我们需要的数据,因此我们就需要调用::IsClipboardFomatAvaliable(UINT nID) API函数,这个函数的参数是我们指定的数据类型ID。另外我们可以调用
UINT EnumClipboardFormats(UINT nID)函数来枚举所有的在剪贴板当中的数据类型。调用函数GetPriorityClipboardFormat函数通过提供给参数的一个UINT数组API函数会从中选择一个优先的数据ID予以返回。
     关于传统剪贴板更详细的内容可以参考《WINDOWS程序设计》。
二、OLE类型的剪贴板
     OLE类型的剪贴板提供了比传统剪贴板更为强大的功能,但是其使用也较为复杂。在MFC当中OLE剪贴板被封装在COleDataSource和COleDataObject两个类当中,这两个类抽象了剪贴板的数据提供者和剪贴板的数据使用者。在类的内部使用了COM技术来构建。
1.简单的OLE剪贴板使用
     简单的OLE剪贴板也使用全局内存对象,在提供数据方面其使用步骤为:在堆上声明一个COleDataSource对象,调用CatchGlobalData函数将全局内存对象句柄交给COleDataSource对象,最后调用SetClipboard()成员函数将数据交给剪贴板;在获取数据方面其使用步骤为:在栈上声明一个COleDataObject对象,调用AttachClipboard成员函数将对象与剪贴板连接起来,调用GetGlobalData成员函数获取剪贴板中的数据,使用完全局内存对象后调用::GlobalFree释放全局内存对象。
2.使用其他媒体的OLE剪贴板调用
      OLE剪贴板较传统剪贴板强大的一个方面体现在了OLE剪贴板允许使用多种媒体来传递数据。在使用多种媒体传递数据时,最重要的是FORMATETC和STGMEDIUM两个数据结构。这两个数据结构的原型如下:
typedef struct tagFORMATETC
{
    CLIPFORMAT      cfFormat; //剪贴板数据对象类型,如CF_TEXT等
    DVTARGETDEVICE *ptd;     //目标设备,一般设置为NULL
    DWORD           dwAspect;   //DVASPECT_CONTENT
    LONG            lindex;          //-1
    DWORD           tymed;        //媒体类型,如TYMED_HGLOBAL、TYMED_FILE等
}FORMATETC, *LPFORMATETC;
typedef struct tagSTGMEDIUM
{
    DWORD tymed;               //等同于FORMATETC当中的tymed字段
    [switch_type(DWORD), switch_is((DWORD) tymed)]
    union {                                                             //联合体当中储存了当前数据信息
        [case(TYMED_GDI)]      HBITMAP        hBitmap;
        [case(TYMED_MFPICT)]   HMETAFILEPICT hMetaFilePict;
        [case(TYMED_ENHMF)]    HENHMETAFILE   hEnhMetaFile;
        [case(TYMED_HGLOBAL)] HGLOBAL        hGlobal;
        [case(TYMED_FILE)]     LPWSTR         lpszFileName;
        [case(TYMED_ISTREAM)] IStream        *pstm;
        [case(TYMED_ISTORAGE)] IStorage       *pstg;
        [default] ;
    };
    [unique] IUnknown *pUnkForRelease;
}STGMEDIUM;
typedef STGMEDIUM *LPSTGMEDIUM;
     在使用非全局内存对象传递剪贴板数据当中,最常用的是使用文件来传递数据,这样做的好处在于可以避免内存的过多浪费,其步骤如下:
//提供数据
char string[] = "Hello World";
char TempPath[MAX_PATH],FileName[MAX_PATH];
::GetTempPath(MAX_PATH,TempPath);                //获取临时文件夹路径
::GetTempFileName(TempPath,"tmp",0,FileName);   //设置临时文件名
//打开FileName文件写入string
LPWSTR wszFileName = (LPWSTR)::CoTaskMemAlloc(MAX_PATH * sizeof(TCHAR));
#ifdef UNICODE                 //由于STGMEDIUM当中的lpszFileName要求UNICODE字符串,因此进行转换
     ::lstrcpy(wszFileName,TempFileName)
#else
      ::MulityByteToWideChar(CP_ACP,MB_PRECOMPOSED,szFileName,-1,wszFileName,MAX_PATH);
#endif
FORMATETC fe = {CF_TEXT,NULL,DVASCEPT_CONTENT,-1,TYMED_FILE};
STGMEDIUM stgm;
stgm.tymed = TYMED_FILE;
stgm.lpszFileName = wszFileName;
COleDataSource *ods = new COleDataSource;
ods->CatchData(CF_TEXT,&fe,&stgm);
ods->SetClipboardData();
//获取数据的方法
STGMEDIUM stgm;
FORMATETC fe = {CF_TEXT,NULL,DVASCEPT_CONTENT,-1,TYMED_FILE};
COleDataObject odo;
odo.AttachClipboard();
if (odo.GetData(CF_TEXT,&fe,&stgm) && stgm.tymed == TYMED_FILE)
{
       TCHAR FileName[MAX_PATH];
#ifdef UNICODE
       ::lstrcpy(FileName,stgm.lpszFileName);
#else
       ::WideCharToMulityByte(CP_ACP,0,stgm.lpszFileName,-1,FileName,sizeof (FileName) / sizeof (TCHAR),NULL,NULL);
      //打开文件读取数据
      ReleaseStgMedium(&stgm);
}
     在上面例程当中,只能读取由TYMED_FILE作为媒体的剪贴板数据,更通用的办法是调用COleDataObject::GetFile()成员函数来获取一个CFile指针,利用这个指针来读取数据。其例程如下:
FORMATETC fe = {CF_TEXT,NULL,DVASCEPT_CONTENT,-1,TYMED_FILE |TYMED_HGLOBAL};
STGMEDIUM stgm;
COleDataObject odo;
odo.AttachClipboard()
switch (odo.GetData(CF_TEXT,&fe,&odo))
{
      case TYMED_FILE:
             CFile *pfile = odo.GetFile();
            //读取数据
             delete pfile;
      case TYMED_HGLOBAL:
            //用简单OLE剪贴板方式获取数据
}
      在OLE剪贴板中并没有象传统剪贴板一样对每中数据类型的ID在剪贴板中只允许放入一个数据项,在OLE剪贴板中只要在FORMATETC当中的tymed字段定义的媒体类型不同即使是相同数据类型ID的数据项允许存在多个数据项。
     OLE剪贴板的数据类型存在检查通过调用COleDataObject::IsDataAvaliable成员函数来实现,这个成员函数接受两个参数一个指示数据类型ID的CFFORMAT值和一个指向FORMATETC结构的指针,只要在OLE剪贴板中存在参数指定的数据类型那么就会返回一个非零值。
二、OLE拖放
     在MFC当中实现OLE拖放需要使用两个类:COleDropSource和COleDropTarget,第一个类实现了拖放数据的提供,第二个类实现了拖放数据的获取。在现实的使用过程当中我们并不直接使用COleDropSource类来提供数据,作为替代我们使用COleDataSource来提供数据。COleDataSource类的DoDragDrop成员函数将会创建COleDropSource类对象并且调用其中的成员函数来提供数据。与OLE剪贴板的提供数据不同,在OLE拖放当中被其他函数或者类调用的对象不再是COleDataSource而是COleDropSource,因此COleDataSource不再需要在堆上创建而只需要在栈上创建即可。提供数据的一个简单例程如下:
char szText[] = "Hello world";
HANDLE handle = ::GlobalAlloc(GHND,lstrlen(szText) + 1);
char *phandle = ::GlobalLock(handle);
lstrcpy(phandle,szText);
::GlobalUnlock(handle);
COleDataSource ods;
ods.CatchDataGlobal(handle);
DROPEFFECT de = ods.DoDragDorp(DROPEFFECT_MOVE | DROPEFFECT_COPY); //允许拖放移动和复制两种选项
if (de == DROPEFFECT_COPY)
     //向文档类当中加入新项
else
      //改变文档类当中被拖动项的数据
     在允许OLE拖动的目标程序的视图类当中我们需要加入一个COleDropTarget保护型数据对象,在OnCreate成员函数当中我们需要加入对COleDropTarget::Register成员函数的调用。然后我们需要处理OnDropEnter,OnDropOver,OnDrop三个成员函数的覆盖。这三个成员函数分别对应拖动对象进入窗口,拖动对象在窗口当中移动,拖动对象在窗口中被放下。对这三个成员函数的覆盖的例子如下:
DROPEFFECT CCheckedView::OnDragEnter(COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
{
CScrollView::OnDragEnter(pDataObject, dwKeyState, point);
if (!pDataObject->IsDataAvailable(CF_TEXT))
   return FALSE;
return ((dwKeyState & MK_CONTROL) ? DROPEFFECT_COPY : DROPEFFECT_MOVE);
}
DROPEFFECT CCheckedView::OnDragEnter(COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
{
CScrollView::OnDragOver(pDataObject, dwKeyState, point);
if (!pDataObject->IsDataAvailable(CF_TEXT))
   return FALSE;
return ((dwKeyState & MK_CONTROL) ? DROPEFFECT_COPY : DROPEFFECT_MOVE);
}
BOOL CCheckedView::OnDrop(COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point)
{
CScrollView::OnDrop(pDataObject, dropEffect, point);
if (pDataObject->IsDataAvailable(CF_TEXT))
{
   //读取数据加入到文档中并且刷新视图
   return TRUE;
}
return FALSE;
}
分享到:
评论

相关推荐

    mfc ole拖放 源码

    同时,源码中的剪贴板操作部分,如使用OpenClipboard、EmptyClipboard、SetClipboardData等API,也会涉及到Windows的消息机制和系统剪贴板的管理。 总的来说,这个"mfc ole拖放 源码"是一个实用的学习资源,可以...

    MFC ole拖放 dialog程序

    总的来说,"MFC ole拖放 dialog程序"是一个结合了MFC、OLE拖放技术和Windows剪贴板机制的实例,它展示了如何在对话框中实现文件的拖放操作,以及如何从剪贴板读取并显示文件信息。这样的程序在实际应用中,例如文件...

    inside ole

    OLE规范包括了一系列的技术、特性和服务,涵盖了类型信息、可连接对象、自定义组件、本地/远程透明性、结构化存储、持久化对象、命名和绑定、统一数据传输、可视对象和数据缓存、OLE剪贴板、OLE拖放等方面。...

    使用COleDataSource进行剪贴板和拖放操作时的陷阱

    在C++ MFC环境中,`COleDataSource`类是用于实现剪贴板操作和拖放功能的核心组件。然而,由于Microsoft官方文档对这部分内容的详细解释不足,开发者往往会在实际应用中遇到一些陷阱和难点。以下是对`COleDataSource`...

    MFC Windows程序设计(第2版修订版)--源代码

     第19章 剪贴板和ole拖放  第20章 automation  第21章 activex控件 序言  像我的许多同行一样,我学习Windows编程是从读Petzold的书《Windows程序设计》——一本所有Windows程序员都使用的Windows编程圣经——...

    MFC Windows程序设计(第2版修订版)--详细书签版2卷

     第19章 剪贴板和ole拖放  第20章 automation  第21章 activex控件 序言  像我的许多同行一样,我学习Windows编程是从读Petzold的书《Windows程序设计》——一本所有Windows程序员都使用的Windows编程圣经——...

    MFC Windows程序设计(第2版修订版)--详细书签版1卷

     第19章 剪贴板和ole拖放  第20章 automation  第21章 activex控件 序言  像我的许多同行一样,我学习Windows编程是从读Petzold的书《Windows程序设计》——一本所有Windows程序员都使用的Windows编程圣经——...

    Window下拖放操作Drag & Drop 全解析

    拖放操作有两种类型:OLE 拖放和文件管理器拖放。这两种方式是完全不同的机制。文件管理器拖放只能处理文件名,通过映射目的窗口的 WM_DROPFILES 消息,窗口就可以收到拖放进来的文件名。OLE 拖放则更加通用一些,它...

    Ole Drag and Drop Example.

    它支持复合文档、剪贴板操作、拖放等功能,使得跨应用程序的数据交换变得简单。 2. **控件**:在编程中,控件是用户界面的基本构建块,如按钮、文本框等。在这个示例中,可能会用到支持拖放事件的控件,如ListBox、...

    Inside OLE 2nd edition.doc

    - 第12-13章:涉及OLE剪贴板和拖放操作。 - 第17-23章:深入讨论了OLE文档的各种元素。 - 第3、14和15章:涵盖了OLE自动化,包括类型信息、连接对象(连接点)和调度。 - 第6章:介绍了本地/远程透明传输。 - 第9章...

    Inside OLE2

    4. **拖放和剪贴板**:OLE2扩展了传统的剪贴板功能,支持拖放操作,允许用户将数据从一个应用程序拖放到另一个,同时保持数据的结构和格式。 5. **自动更新**:当链接的对象在原服务器中更改时,OLE2系统会自动更新...

    User clipboard in MFC

    `COleClipboard`类提供了更高级的功能,如支持OLE拖放和链接。 6. **多线程剪贴板使用**: 如果你的程序有多个线程,需要注意剪贴板的线程安全问题。每个线程都应独立打开和关闭剪贴板,避免并发访问冲突。 综上所...

    OLE高级编程

    3. **拖放和剪贴板**:OLE扩展了传统的Windows剪贴板功能,使得对象可以在应用程序之间拖放,提供了更丰富的数据交换方式。 4. **事件和通知**:OLE提供了一种机制,让容器和服务器能够知道对方的状态变化,从而...

    Mastering OLE2 第三章例子

    剪贴板和拖放操作都是数据对象接口的应用实例。 6. **服务器和容器**:OLE2系统中,存在两种主要的角色——服务器和容器。服务器提供可嵌入或链接的对象,而容器则是使用这些对象的应用程序。 7. **注册和反注册**...

    OLE 2 对象链接与嵌入技术高级编程技术

    8. **剪贴板和数据交换 (Clipboard and Data Exchange)**:OLE 2 提供了一种标准机制来在应用程序之间共享数据,通过剪贴板或者数据对象的使用,数据可以在不同程序间复制、粘贴。 9. **持久化 (Persistence)**:...

    visual c++6.0技术内幕 带有NLC的文件查看器

     第26章 统一数据传输:剪贴板传输和OLE拖放  第27章 结构化存储  第28章 OLE嵌入组件和包容器  第29章 活动模板库基础 第五部分 数据库管理  第30章 ATL和ActiveX控件  第31章 MiCrOSOft ODBC数据库管理  第...

    windows剪切板操作例程(MFC)

    COleClipboard提供了诸如DoDragDrop、OnRenderData等方法,使得拖放和剪切板操作更为便捷。TestMFC项目可能是实现这些功能的一个MFC应用程序实例,其中可能包含COleDataSource、COleDataObject等类的使用,这些类是...

    扩展COleDropTarget类来支持任意窗口拖放,文本拖动

    COleDropTarget是一个C++类,它是Windows的消息处理机制与OLE拖放操作的接口。默认情况下,COleDropTarget只能应用于框架窗口(CFrameWnd)或视图(CView)。为了支持任意窗口的拖放,我们需要创建一个派生类,并覆盖其...

    编辑框 Edit DropFile 实现 窗口 编辑框 拖放功能 源代码

    - **剪贴板操作**:结合剪贴板API,实现复制、剪切和粘贴功能。 - **多选拖放**:支持一次拖放多个文件。 6. **学习资源**: - **MSDN文档**:Microsoft官方文档提供了详细的Windows API和OLEDragDrop的使用说明...

Global site tag (gtag.js) - Google Analytics