二. Windows 剪贴板

  • Post author:
  • Post category:其他



一、概述

—- 在Windows操作系统中存在两种剪贴板机制:Windows标准剪贴板和OLE剪贴板机

制。

—- 标准的Windows剪贴板是一个被所有Windows应用程序共享的系统服务,因此它并

没有自己的句柄或类。但你可以通过CWnd类的成员函数来管理剪贴板。

—- 自从OLE(Object Linking and Embedding,对象链接和嵌入)诞生之后,

Windows操作系统中便出现了第二种剪贴板机制——OLE剪贴板机制。标准的Windows剪

贴板API(Application Programming Interface,应用程序编程接口)依然可用,但是

他已经被OLE数据传输机制来实现了。OLE支持UDT(Uniform Data Transfer,统一数据

传输),并可以通过拖放操作实现剪贴板的剪切、复制和粘贴等操作。OLE剪贴板除了

拥有标准Windows剪贴板的性能外,还支持传输用户自定义的剪贴板格式,并能够在传

输数据时绑定OLE格式(如字体、字号等)。OLE剪贴板机制将成为更为主要的数据传输

机制。

—- 本文将简要叙述标准Windows剪贴板的实现,并将重点放在讨论如何通过Visual

C++实现OLE剪贴板上。


二、选择适当的剪贴板机制

—- 在选择使用何种剪贴板机制时通常应遵循下面的原则:

—- 如果应用程序在将来又可能具有新的性能(比如现在只需要传输纯文本,但将来

有可能需要另外传输字体等特性),那么使用OLE剪贴板。

—- 如果你正在使用一个OLE应用程序,或者你希望使用任何OLE特性(如拖放等)那

么你应当使用OLE剪贴板机制。

—- 如果你提供了OLE格式(如字体、字号等),那么使用OLE剪贴板机制。

三.

在视图类中为文档创建一个新的剪切板类型。拥有这种剪贴板类型的文档可以粘贴到该剪贴板,但其他类型的文档不行。创建自己的自定义格式,要遵循在注册任何自定义剪贴板格式时,使用相同的过程;将格式名称传递给RegisterDlipboardFormat函数,然后使用其返回值作为格式表示符,具体方法是:

1.    在视图类的声明文档中声明一个静态的自定义剪贴板类型:

Static CLIPFORMAT m_cfDraw;//自定义剪贴板格式

2.    在视图类实现文档中注册自定义剪贴板格式:

CLIPFORMAT CvisDrawView::m_cfDraw = (CLIPFORMAT)::RegisterClipboardFormat(_T(“VISDRAW”));

RegisterClipboardFormat是Win32 API函数,它注册一个新的剪贴板格式,此后这个格式可作为有效的剪贴板格式。RegisterClipboardFormat的原型是:UINT RegisterClipboardFormat( LPCTRSTR lpszFormat //新格式名称);

如果注册格式已经存在,将返回已经存在格式的值而不是注册新的格式。这样做的好处是可以在多个应用程序中使用同一种格式来复制和粘贴数据。

数据源必须由当前对其负责的应用程序销毁。如果将数据源传递给OLE(如调用ColeDataSource::SetClipBoard),则不必考虑如何销毁,因为数据源将由OLE销毁。如果不讲数据源传递给OLE,则与处理典型的C++对象一样,你必须负责销毁数据源:delete pDataSource.

四.

一、文本内容的操作

下面的代码示范了如何将文本内容复制到剪贴板(Unicode编码的先转化为ASCII):

CString source;

//文本内容保存在source变量中

if( OpenClipboard() )

{


HGLOBAL clipbuffer;

char * buffer;

EmptyClipboard();

clipbuffer = GlobalAlloc(GMEM_DDESHARE, source.GetLength()+1);

buffer = (char*)GlobalLock(clipbuffer);

strcpy(buffer, LPCSTR(source));

GlobalUnlock(clipbuffer);

SetClipboardData(CF_TEXT,clipbuffer);

CloseClipboard();

}

下面的代码显示了如何从剪贴板上获得文本内容:

char * buffer = NULL;

//打开剪贴板

CString fromClipboard;

if ( OpenClipboard() )

{


HANDLE hData = GetClipboardData(CF_TEXT);

char * buffer = (char*)GlobalLock(hData);

fromClipboard = buffer;

GlobalUnlock(hData);

CloseClipboard();

}

二、WMF数据的操作

在剪贴板上读写图象数据是非常有用的功能,并且实现起来也很简单。下面的代码显示了如何将扩展图元文件复制到剪贴板:

if(OpenClipboard());

{


EmptyClipboard();

//创建图元文件DC

CMetaFileDC * cDC = new CMetaFileDC();

cDC->CreateEnhanced(GetDC(),NULL,NULL,”the_name”);

//调用绘图例程

//关闭CMetafileDC并获得它的句柄

HENHMETAFILE handle = cDC->CloseEnhanced();

//复制到剪贴板

SetClipBoardData(CF_ENHMETAFILE,handle);

CloseClipboard();

//删除dc

delete cDC;

}

下面的代码演示了从剪贴板获得图元文件并将其绘制到client DC上:

if(OpenClipboard())

{


//获得剪贴板数据

HENMETAFILE handle = (HENMETAFILE)GetClipboardData(CF_ENHMETAFILE);

//显示

CClientDC dc(this);

CRect client(0,0,200,200);

dc.PlayMetaFile(handle,client);

//关闭剪贴板

CloseClipboard();

}


三、位图的操作

位图的操作稍微复杂一点,下面这个例子显示了如何在剪贴板保存位图:

if(OpenClipboard())

{


EmptyClipboard();

CBitmap * junk = new CBitmap();

CClientDC cdc(this);

CDC dc;

dc.CreateCompatibleDC(&cdc);

CRect client(0,0,200,200);

junk->CreateCompatibleBitmap(&cdc,client.Width(),client.Height());

dc.SelectObject(junk);

DrawImage(&dc,CString(“Bitmap”));

//复制数据到剪贴板

SetClipboardData(CF_BITMAP,junk->m_hObject);

CloseClipboard();

delete junk;

}

下面的代码显示了如何从剪贴板上获得位图数据:

if(OpenClipboard())

{


//获得剪贴板数据

HBITMAP handle = (HBITMAP)GetClipboardData(CF_BITMAP);

CBitmap * bm = CBitmap::FromHandle(handle);

CClientDC cdc(this);

CDC dc;

dc.CreateCompatibleDC(&cdc);

dc.SelectObject(bm);

cdc.BitBlt(0,0,200,200,&dc,0,0,SRCCOPY);

CloseClipboard();

}

四、设置并使用自定义格式

使用RegisterClipboardFormat()函数,可以复制和粘贴任何你需要的数据类型。比如我们有以下一个数据类型:

struct MyFormatData

{


long val1;

int val2;

};

我们要把它复制到剪贴板,可以使用如下的代码:

UINT format = RegisterClipBoardFormat(“MY_CUSTOM_FORMAT”);

if(OpenClipboard())

{


MyFormatData data;

data.val1 = 100;

data.val2 = 200;

HGLOBAL clipbuffer;

EmptyClipboard();

clipbuffer = GlobalAlloc(GMEM_DDESHARE, sizeof(MyFormatData));

MyFormatData * buffer = (MyFormatData*)GlobalLock(clipbuffer);

//保存到内存

*buffer = data;

//保存到剪贴板

GlobalUnlock(clipbuffer);

SetClipboardData(format,clipbuffer);

CloseClipboard();

}

读取数据使用以下代码:

UINT format = RegisterClipboardFormat(“MY_CUSTOM_FORMAT”);

MyFormatData data;

if(Openclipboard())

{


HANDLE hData =GetClipboardData(format);

MyFormatData * buffer = (MyFormatData*)GlobalLock(hData);

data = *buffer;

GlobalUnlock(hData);

CloseClipboard();

}

五、感知剪贴板内容的改变

通过Windows消息可以感知剪贴板内容是否发生改变,代码如下:

//In your initialization code call:

SetClipboardViewer(); //将我们的程序添加到剪贴板观察链

//In your message map add:

ON_MESSAGE(WM_DRAWCLIPBOARD, OnClipChange) //添加Message handle

//Which is declared as:

afx_msg void OnClipChange();

Finally implement:

void CDetectClipboardChangeDlg::OnClipChange()

{


CTime time = CTime::GetCurrentTime();

SetDlgItemText(IDC_CHANGED_DATE,time.Format(“%a, %b %d, %Y — %H:%M:%S”));

DisplayClipboardText();

}

六、自动将数据粘贴到另一应用程序窗口

只需获得相应窗口的句柄,并发送一个消息就可以了:

SendMessage(m_hTextWnd, WM_PASTE, 0, 0);

传统Windows剪贴板编程


将数据复制到剪贴板:

1.调用OpenClipboard()设置数据的源窗口.

2.调用EmptyClipboard()清空剪贴板中以前的数据.

3.调用SetClipboardData()将数据存放到剪贴板上.

4.调用CloseClipboard()使别的窗口能访问剪贴板.

取得剪贴板上的数据:

1.调用OpenClipboard()访问剪贴板.

2.调用GetClipboardData()取得数据.

3.调用CloseClipboard()释放剪贴板.

使用延迟供应技术时,源数据方以NULL为数据句柄调用SetClipboardData(),数据使用方GetClipboardData()时,Windows向数据产生者发送WM_RENDERFORMAT和WM_RENDERFORMATS消息,数据提供者响应消息并产生数据.

局限性:使用全局内存来传输,数据量大时系统要使用虚拟内存管理机制来管理,对交换效率有很大影响.

//————————————————————

OLE剪贴板

介于应用程序与标准剪贴板间,从标准剪贴板扩展而来,补充了OLE的数据传输机制,对标准剪贴板向后兼容.

OLE剪贴板使用IDataObject接口进行传输,相关的API:

.OleSetClipboard():在剪贴板上放置一个IDataObject接口指针.

.OleGetClipboard():从剪贴板上取得一个IDataObject接口指针.

.OleFlushClipboard():清空OLE剪贴板,释放上面的IDataObject接口指针.

.OleIsCurrentClipboard():判断指定的对象当前是否在剪贴板上.

OLE剪贴板的工作步骤:

1.数据创造者程序将数据放到剪贴板并实现IDataObject.数据创造者用OleSetClipboard()得IDataObject的一个拷贝.并将其放到剪贴板上.

2.剪贴板上有IDataObject指针时,OLE像普通应用程序一样使用剪贴板.OLE调用OpenClipboard来声明剪贴板的拥有者,OLE剪贴板使用延迟供应模式.OLE剪贴板会创建一个隐藏窗口作为剪贴板的拥有者(OpenClipboard需要HWND参数)–在OleInitialize()中创建.

3.OLE枚举IDataObject的格式,同时对每个在全局句柄中提供数据的格式调用SetClipboard().标准剪贴板不支持文件和结构的传输入,所以只能将全局句柄放在剪贴板上.

4.数据消耗者访问剪贴板.当它不知道OLE的信息时,使用标准方式GetClipboardData()来获取数据–数据是由延迟供应方式提供的.OLE剪贴板查询IDataObject接口,然后调用接口上的GetData()方法取得数据.如数据消耗者支持OLE,它可用OleGetClipboard()取得IDataObject指针,并用GetData()取得数据.

MFC的IDataObject支持

.COleDataSource:一个完全的COM对象,实现了IDataObject接口.常用在数据提供者一方.

.COleDataObject:封装一个IDataObject指针,为开发者提供C++接口.常用在数据消耗者一方.

A.通过剪贴板传输数据

1.将数据放置到剪贴板中.

得到数据指针并创建COleDataSource的一个实例,用这个对象来保存数据.例:

{


LPCTSTR source = GetString() ;

COleDataSource *pCods;

HGLOBAL h = GlobalAlloc(GHND|GMEM_SHARE,(_tcslen(source)+1)*sizeof(TCHAR));

_tcscpy( LPSTR(GloballLock(h)), source ) ;

GlobalUnlock(h);//使用全局内存时这句一定要有.

pCods->CacheGlobalData(CF_TEXT,h) ;

pCods->SetClipboard() ;

//没有释放全局内存?在将数据放到OLE剪贴板的情况下,OLE自己会去释放它.

//不想使用全局内存?那用COleDataSource::CacheData()放入数据源对象,但需要提供两个描绘数据的参数.

}

2.从剪贴板中粘贴数据

声明一个COleDataObject的实例,调用COleDataObject::AttachClipboard().可用ColeDataObject::IsDataAvailable()获取数据格式;但也支持COleDataObject::BeginEnumFormats()/COleDataObject::GetNextFormat()枚举所有格式.

确定格式后,用COleDataObject的以下函数可得到数据:

.COleDataObject::GetDataObject();//最通用的,处理所有格式.

.COleDataObject::GetFileData();//数据的存储介质是文件时;文件由使用者释放.

.COldDataObject::GetGlobalData();//数据在全局内存.

例:

{


COleDataObject odo ;

odo.AttachClipboard() ;

if( odo.IsDataAvailable(CT_TEXT)){


HANDLE h = odo.GetGlobalData(CF_TEXT);

//use data

GlobalUnlock(h) ;

GlobalFree(h) ;

}

odo.Release() ;

}

B.延迟供应.

由COldDataSource中的DelayRenderData(),DelayRenderFileData(),OnRenderData()实现.需要在COleDataSource派生类中实现OnRenderData().类似的函数还有OnRenderFileData,OnRenderGlobalData().

//———————————————————–

OLE拖放

使用3个接口:IDataObject,IDropSource(COleDropSource实现),IDropTarget(COleDropTarget实现).

控制键:[没有]=移动数据;[CONTROL]=复制数据;[CONTROL-SHIFT]=建立快捷方式;[ALT]=移动数据

开起拖放:与将数据放在剪贴板相似,不过是调用COleDataSource:DoDragDrop()而不是COleDataSource::SetClipboard();

结束播放:放置目标通常需要两个COleDropTarget与CView类

1.注册CView为一个COleDropTarget类.在类中声明一个COleDropTarget变量,在OnCreate()中调用COleDropTarget::Register(this);

2.重载CView类的OnDragEnter(),OnDragOver(),OnDragLeave(),OnDrop();



版权声明:本文为hlfkyo原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。