日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

进程间通信——剪切板

發(fā)布時(shí)間:2024/1/1 编程问答 59 豆豆
生活随笔 收集整理的這篇文章主要介紹了 进程间通信——剪切板 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

要點(diǎn)總結(jié):

1、剪切板是操作系統(tǒng)維護(hù)的一塊內(nèi)存區(qū)域,本機(jī)所有進(jìn)程都可以訪問(wèn)。 2、剪切板內(nèi)存從哪里來(lái)?不是一開(kāi)機(jī)就分配的,是程序要將數(shù)據(jù)放入剪切板時(shí)分配內(nèi)存的。 3、但:malloc和 new內(nèi)存是在當(dāng)前進(jìn)程的私有地址空間中分配內(nèi)存,并不能被所有進(jìn)程共享。 4、所有:用GlobalAlloc分配內(nèi)存,此內(nèi)存不為任一進(jìn)程私有,而是由操作系統(tǒng)管理。 5、特點(diǎn):只能用于本地進(jìn)程間通信。

引子

由于在啟動(dòng)一個(gè)進(jìn)程后,操作系統(tǒng)會(huì)給這個(gè)進(jìn)程分配 4GB 的私有地址空間,至于為何有 4GB 這么大,

那得考慮進(jìn)程的私有地址空間和實(shí)際物理內(nèi)存地址空間之間的映射以及頁(yè)交換等等細(xì)節(jié)問(wèn)題了,這里不予討論,

從名字就可以知道,既然操作系統(tǒng)給每一個(gè)進(jìn)程分配的是私有地址空間,

自然,這段地址空間也只有這個(gè)進(jìn)程自己才能訪問(wèn)了,不然還稱為私有干嗎呢?

既然這段私有地址空間只能由進(jìn)程本身訪問(wèn),那也就說(shuō)明別的進(jìn)程是不能夠隨意的訪問(wèn)這個(gè)進(jìn)程的地址空間的,

而本篇博文介紹的是進(jìn)程間的通信,而上面又說(shuō)任意兩個(gè)進(jìn)程之間是并能夠互相訪問(wèn)對(duì)方的私有地址空間的,

都不能訪問(wèn)了,那還通信個(gè)屁啊 ?

自然上面的訪問(wèn)對(duì)方進(jìn)程的私有地址空間是行不通了,那應(yīng)該還有其他辦法的 !!!

解決方法:

如果我在物理內(nèi)存中劃分出一塊內(nèi)存,這一塊內(nèi)存不為任何的進(jìn)程所私有,但是任何的進(jìn)程又都可以訪問(wèn)這塊內(nèi)存,

那么?進(jìn)程 A?就可以往這塊內(nèi)存中存放數(shù)據(jù)?Data?,然后?進(jìn)程 B?也是可以訪問(wèn)這塊內(nèi)存的,從而?進(jìn)程 B?就可以訪問(wèn)到數(shù)據(jù)?Data?了,

這樣不就實(shí)現(xiàn)了?進(jìn)程 A?和?進(jìn)程 B?之間的通信了 !!!

而上面的這種思路就是剪貼板了。

當(dāng)然解決進(jìn)程間通信還有好幾種思路,這將會(huì)在后續(xù)博文中介紹,本篇博文暫只介紹利用剪貼板來(lái)實(shí)現(xiàn)進(jìn)程間的通信。

????????????

????????????

剪貼板定義

剪貼板是由操作系統(tǒng)維護(hù)的一塊內(nèi)存區(qū)域,這塊內(nèi)存區(qū)域不屬于任何單獨(dú)的進(jìn)程,但是每一個(gè)進(jìn)程又都可以訪問(wèn)這塊內(nèi)存區(qū)域,

而實(shí)質(zhì)上當(dāng)在一個(gè)進(jìn)程中復(fù)制數(shù)據(jù)時(shí),就是將數(shù)據(jù)放到該內(nèi)存區(qū)域中,

而當(dāng)在另一個(gè)進(jìn)程中粘貼數(shù)據(jù)時(shí),則是從該塊內(nèi)存區(qū)域中取出數(shù)據(jù)。 ? ? ? ? ??? ? ? ? ? ?

剪貼板操作

其實(shí)在剪貼板中也就那么幾個(gè) API 在使用,所以在這里的還是本著 API 介紹為主,

不管三七二十一,先列出常用的 API 再說(shuō)(到后面結(jié)合 Demo 的使用即可)。? ? ? ? ? ? ? ??

剪貼板的打開(kāi) – OpenClipboard

要想把數(shù)據(jù)放置到剪貼板中,則必須先打開(kāi)剪貼板,而這是通過(guò)?OpenClipboard?成員函數(shù)實(shí)現(xiàn):

BOOL? OpenClipboard(HWND??hWndNewOwner?);

第一個(gè)參數(shù)?hWndNewOwner?指向一個(gè)與之關(guān)聯(lián)的窗口句柄,即代表是這個(gè)窗口打開(kāi)剪貼板,

如果這個(gè)參數(shù)設(shè)置為?NULL?的話,則以當(dāng)前的任務(wù)或者說(shuō)是進(jìn)程來(lái)打開(kāi)剪貼板。

如果打開(kāi)剪貼板成功,則該函數(shù)返回非?0?值,如果其他程序已經(jīng)打開(kāi)了剪貼板,

那么當(dāng)前這個(gè)程序就無(wú)法再打開(kāi)剪貼板了,所以會(huì)致使打開(kāi)剪貼板失敗,從而該函數(shù)返回?0?值。

其實(shí)這也好理解,你想啊,剪貼板總共才那么一塊內(nèi)存區(qū)域,你?進(jìn)程 A?要往里面寫數(shù)據(jù),你?進(jìn)程 B?又要往里面寫數(shù)據(jù),那不亂套去,

解決這個(gè)亂套的辦法就是,如果我?進(jìn)程 A?正在往剪貼板里面寫數(shù)據(jù)(可以理解為?進(jìn)程 A?打開(kāi)剪貼板了),那么?進(jìn)程 B?就不能往剪貼板里頭寫數(shù)據(jù)了,

既然要讓?進(jìn)程 B?不能往剪貼板中寫數(shù)據(jù)了,那我就讓?進(jìn)程 B?打開(kāi)剪貼板失敗不就得了。

所以如果某個(gè)程序已經(jīng)打開(kāi)了剪貼板,那么其他應(yīng)用程序?qū)⒉荒苄薷募糍N板,

直到打開(kāi)了剪貼板的這個(gè)程序調(diào)用了?CloseClipboard?函數(shù),

并且只有在調(diào)用了?EmptyClipboard?函數(shù)之后,打開(kāi)剪貼板的當(dāng)前窗口才能擁有剪貼板,

注意是必須要在調(diào)用了?EmptyClipboard?函數(shù)之后才能擁有剪貼板。?? ? ? ? ? ? ? ? ??

剪貼板的清空 - EmptyClipboard

這個(gè)函數(shù)將清空剪貼板,并釋放剪貼板中數(shù)據(jù)的句柄,然后將剪貼板的所有權(quán)分配給當(dāng)前打開(kāi)剪貼板的窗口,

因?yàn)榧糍N板是所有進(jìn)程都可以訪問(wèn)的,

所以應(yīng)用程序在使用這個(gè)剪貼板時(shí),有可能已經(jīng)有其他的應(yīng)用程序把數(shù)據(jù)放置到了剪貼板上,

因此該進(jìn)程打開(kāi)剪貼板之后,就需要調(diào)用?EmptyClipboard?函數(shù)來(lái)清空剪貼板,

釋放剪貼板中存放的數(shù)據(jù)的句柄,并將剪貼板的所有權(quán)分配給當(dāng)前的進(jìn)程,

這樣做之后當(dāng)前打開(kāi)這個(gè)剪貼板的程序就擁有了剪貼板的所有權(quán),因此這個(gè)程序就可以往剪貼板上放置數(shù)據(jù)了。

BOOL?EmptyClipboard(void);

??????????????????????????????

剪貼板的關(guān)閉 - CloseClipboard

如果某個(gè)進(jìn)程打開(kāi)了剪貼板,則在這個(gè)進(jìn)程沒(méi)有調(diào)用?CloseClipboard?函數(shù)關(guān)閉剪貼板句柄之前,

其他進(jìn)程都是無(wú)法打開(kāi)剪貼板的,所以我們每次使用完剪貼板之后都應(yīng)該關(guān)閉剪貼板。

注意,這里的關(guān)閉剪貼板并不代表當(dāng)前打開(kāi)剪貼板的這個(gè)程序失去了對(duì)剪貼板的所有權(quán),

只有在別的程序調(diào)用了?EmptyClipboard?函數(shù)之后,當(dāng)前的這個(gè)程序才會(huì)失去對(duì)剪貼板的所有權(quán),

而那個(gè)調(diào)用?EmptyClipboard?函數(shù)的程序才能擁有剪貼板。

BOOL?CloseClipboard(void);

???????????????????????????

數(shù)據(jù)發(fā)送到剪貼板 - SetClipboardData

可以通過(guò)?SetClipboardData?函數(shù)來(lái)實(shí)現(xiàn)往剪貼板中放置數(shù)據(jù),這個(gè)函數(shù)以指定的剪貼板格式向剪貼板中放置數(shù)據(jù)。

HANDLE? SetClipboardData(UINT uFormat,? HANDLE hMem );

第一個(gè)參數(shù)?uFormat?用來(lái)指定要放到剪貼板上的數(shù)據(jù)的格式,

比如常見(jiàn)的有?CF_BITMAP?CF_TEXT?,CF_DIB?等等(其他格式可以參考?MSDN)。

第二個(gè)參數(shù)?hMem?用來(lái)指定具有指定格式的數(shù)據(jù)的句柄,該參數(shù)可以是?NULL?

如果該參數(shù)為?NULL?則表明直到有程序?qū)糍N板中的數(shù)據(jù)進(jìn)行請(qǐng)求時(shí),

該程序(也就是擁有剪貼板所有權(quán)的進(jìn)程)才會(huì)將數(shù)據(jù)復(fù)制到剪貼板中,也就是提供指定剪貼板格式的數(shù)據(jù),

上面提及的就是延遲提交技術(shù),這個(gè)延遲提交技術(shù)將會(huì)在后面做詳細(xì)的介紹。

??????????????

剪貼板中數(shù)據(jù)格式判斷 – IsClipboardFormatAvaliable

BOOL? IsClipboardFormatAvailable( UINT format );

該函數(shù)用來(lái)判斷剪貼板上的數(shù)據(jù)格式是否為?format?指定的格式。

?????????????

剪貼板中數(shù)據(jù)接收 - GetClipboardData

HANDLE? GetClipboardData( UINT uFormat );

該函數(shù)根據(jù)?uFormat?指定的格式,返回一個(gè)以指定格式存在于剪貼板中的剪貼板對(duì)象的句柄。

?????????????

?????????????

全局內(nèi)存分配 – HGLOBAL

剪貼板中的內(nèi)存從何而來(lái)

從上面的介紹中可以知道剪貼板其實(shí)就是一塊內(nèi)存,那么這塊內(nèi)存是什么時(shí)候分配的呢?

難不成說(shuō)一開(kāi)機(jī),操作系統(tǒng)就給剪貼板分配個(gè)幾?M?的內(nèi)存的吧?

這種方式也太遜色了,你想啊,我的程序要往剪貼板中放置的數(shù)據(jù),我事先又不曉得數(shù)據(jù)長(zhǎng)度,

所以,一開(kāi)機(jī)操作系統(tǒng)究竟要給剪貼板分配多少內(nèi)存呢?很明顯,太不動(dòng)態(tài)了,不可取。

要想動(dòng)態(tài)的話,那有一種方案,就是當(dāng)我的程序要往剪貼板中放置數(shù)據(jù)的時(shí)候來(lái)確定要分配給剪貼板的內(nèi)存的大小,

很明顯,既然我都知道要往剪貼板中放置那些數(shù)據(jù)了,自然我也就知道了這些數(shù)據(jù)的長(zhǎng)度,

那么我就可以以這個(gè)數(shù)據(jù)長(zhǎng)度來(lái)給剪貼板分配內(nèi)存了,這是很動(dòng)態(tài)的了吧,所以這種方案是可取的,

但關(guān)鍵是,當(dāng)我們以前在程序中分配內(nèi)存的時(shí)候,都是使用的標(biāo)準(zhǔn)?C?運(yùn)行庫(kù)中的?malloc?或者是?C++?中的?new?關(guān)鍵字,

(當(dāng)然分配內(nèi)存還有很多其他的函數(shù),比如就有內(nèi)核中的執(zhí)行體中就有很多分配內(nèi)存的函數(shù),這里不討論),

而使用?malloc?或者?new?有一個(gè)問(wèn)題,那就是,用這個(gè)兩個(gè)東西來(lái)分配的內(nèi)存空間都是在當(dāng)前進(jìn)程的私有地址空間上分配內(nèi)存,

也就是它們兩個(gè)東東所分配的內(nèi)存空間為進(jìn)程私有地址空間所有,并不為所有進(jìn)程所共享,

上面提到了,任何進(jìn)程之間都是不能訪問(wèn)對(duì)方的私有地址空間的,你把剪貼板中的內(nèi)存分配到了你當(dāng)前進(jìn)程的私有地址空間上,

而其他進(jìn)程又不能訪問(wèn)你這個(gè)進(jìn)程的私有地址空間,那怎么能夠訪問(wèn)剪貼板呢?

很明顯,不能使用?malloc?和?new?關(guān)鍵字來(lái)分配內(nèi)存給剪貼板。

我們應(yīng)該要使用另外一個(gè)特殊一點(diǎn)的函數(shù)來(lái)分配內(nèi)存給剪貼板,

這個(gè)特殊函數(shù)所分配的內(nèi)存不能夠是在進(jìn)程的私有地址空間上分配,而是要在全局地址空間上分配內(nèi)存,

這樣這個(gè)函數(shù)所分配的內(nèi)存才能夠被所有的進(jìn)程所共享,這樣,剪貼板中的數(shù)據(jù)就可以被其他的進(jìn)程所訪問(wèn)了。

?????????????????????

GlobalAlloc 函數(shù)

GlobalAlloc?函數(shù)是從堆上分配指定數(shù)目的字節(jié),

與其他的內(nèi)存管理函數(shù)相比,全局內(nèi)存函數(shù)的運(yùn)行速度會(huì)稍微慢一些(等下會(huì)解釋為什么會(huì)慢),

但是全局函數(shù)支持動(dòng)態(tài)數(shù)據(jù)交換,同時(shí),其分配的內(nèi)存也不為任何一個(gè)進(jìn)程所私有,而是由操作系統(tǒng)來(lái)管理這塊內(nèi)存,

所以用在給剪貼板分配內(nèi)存空間是很適合的。

這里有讀者可能會(huì)問(wèn):

為什么我們?cè)谧约旱膽?yīng)用程序中不使用?GlobalAlloc?函數(shù)來(lái)分配內(nèi)存,而是要使用?malloc?或者?new??來(lái)實(shí)現(xiàn)?

其實(shí),這個(gè)也只用稍微想想就知道了,你想啊,使用?malloc?或者?new?分配的內(nèi)存是在進(jìn)程的私有地址空間上分配的,

這片私有地址空間都是歸這個(gè)進(jìn)程所擁有,所管理的,自然,在以后對(duì)這塊內(nèi)存的讀寫會(huì)快很多的,

而全局內(nèi)存不屬于這個(gè)進(jìn)程,你下次要去訪問(wèn)全局內(nèi)存的時(shí)候,還得通過(guò)映射轉(zhuǎn)換,這樣肯定是運(yùn)行效率低下一些了,

簡(jiǎn)單點(diǎn)就可以這樣理解,你使用?malloc?或者?new?分配的內(nèi)存和你的進(jìn)程隔得很近,程序要過(guò)去拿數(shù)據(jù) - 得,很近吧,

而是用?GlobalAlloc?函數(shù)分配的內(nèi)存和你的進(jìn)程隔得很遠(yuǎn),程序要過(guò)去拿數(shù)據(jù) - 太遠(yuǎn)了,耗時(shí)。

應(yīng)用程序在調(diào)用了?SetClipboardData?函數(shù)之后,

系統(tǒng)就擁有了?hMem?參數(shù)所標(biāo)識(shí)的數(shù)據(jù)對(duì)象,該應(yīng)用程序可以讀取這個(gè)數(shù)據(jù)對(duì)象,

但是在應(yīng)用程序調(diào)用?CloseClipboard?函數(shù)之前,它都是不能釋放該對(duì)象的句柄的,或者鎖定這個(gè)句柄,

如果?hMem?標(biāo)識(shí)一個(gè)內(nèi)存對(duì)象,那么這個(gè)對(duì)象必須是利用?GMEM_MOVEABLE?標(biāo)識(shí)調(diào)用?GlobalAlloc?函數(shù)為其分配內(nèi)存的。

HGLOBAL? WINAPI? GlobalAlloc( UINT? uFlags,?? SIZE_T? dwBytes );

第一個(gè)參數(shù)?uFlags?用來(lái)指定分配內(nèi)存的方式。其取值如下列表所示

(但是在剪貼板的使用中,由于要實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)交換,所以必須得使用?GHND?或者?GMEM_MOVEABLE):

描述

GHND

即?GMEM_MOVEABLE?和?GMEM_ZEROINIT?的組合。

GMEM_FIXED

分配一塊固定內(nèi)存,返回值是一個(gè)指針。

GMEM_MOVEABLE

分配一塊可移動(dòng)內(nèi)存。

GMEM_ZEROINIT

初始化內(nèi)存的內(nèi)容為 0

GPTR

即?GMEM_FIXED?和?GMEM_ZEROINIT?的組合。

第二個(gè)參數(shù)?dwBytes?用來(lái)指定分配的字節(jié)數(shù)。

???????????????????

GlobalReAlloc 函數(shù)

HGLOBAL? WINAPI? GlobalReAlloc(HGLOBAL hMem,? SIZE_T dwBytes,? UINT uFlags);

該函數(shù)為再分配函數(shù),即在原有的數(shù)據(jù)對(duì)象?hMem?上,為其擴(kuò)大內(nèi)存空間。

第一個(gè)參數(shù)?hMem?代表由?GlobalAlloc?函數(shù)返回的數(shù)據(jù)對(duì)象句柄。

第二個(gè)參數(shù)?dwBytes?指定需要重新分配的內(nèi)存的大小。

第三個(gè)參數(shù)?uFlags?指定分配的方式(可以參考?GlobalAlloc?函數(shù))。

???????????????????

GlobalSize 函數(shù)

SIZE_T? WINAPI? GlobalSize( HGLOBAL? hMem );

該函數(shù)用來(lái)返回內(nèi)存塊的大小。

第一個(gè)參數(shù)?hMem?代表由?GlobalAlloc?函數(shù)返回的數(shù)據(jù)對(duì)象句柄。

???????????????

GlobalLock 函數(shù)

LPVOID? WINAPI? GlobalLock( HGLOBAL? hMem );

該函數(shù)的作用是對(duì)全局內(nèi)存對(duì)象加鎖,然后返回該對(duì)象內(nèi)存塊第一個(gè)字節(jié)的指針

第一個(gè)參數(shù)?hMem?代表由?GlobalAlloc?函數(shù)返回的數(shù)據(jù)對(duì)象句柄。

???????????

GlobalUnLock 函數(shù)

BOOL? WINAPI? GlobalUnlock( HGLOBAL? hMem );

你通過(guò)上面的?GlobalLock?函數(shù)可以獲得這塊全局內(nèi)存的訪問(wèn)權(quán),

加鎖的意思就是你已經(jīng)在使用這塊全局內(nèi)存了,別的程序就不能再使用這塊全局內(nèi)存了,

而如果你一直不解鎖,那也不是個(gè)事啊,別的程序?qū)?huì)一直都使用不了這塊全局內(nèi)存,

那還叫全局內(nèi)存干嗎啊?所以這個(gè)函數(shù)就是用來(lái)對(duì)全局內(nèi)存對(duì)象解鎖。

第一個(gè)參數(shù)?hMem?代表由?GlobalAlloc?函數(shù)返回的數(shù)據(jù)對(duì)象句柄。

?????????????????

GlobalFree 函數(shù)

HGLOBAL? WINAPI? GlobalFree( HGLOBAL? hMem );

該函數(shù)釋放全局內(nèi)存塊。

第一個(gè)參數(shù)?hMem?代表由?GlobalAlloc?函數(shù)返回的數(shù)據(jù)對(duì)象句柄。

???????????????

?????????????

Demo1 – ConsoleClipboard(剪貼板常用手法)

整個(gè)項(xiàng)目結(jié)構(gòu)很簡(jiǎn)單:

ConsoleClipboard.h

#ifndef CONSOLE_CLIP_BOARD_H #define CONSOLE_CLIP_BOARD_H ? #include <Windows.h> #include <iostream> ? using namespace std; ? const char * pStrData = "Zachary"; ? void SetClipBoardData(); ? void GetClipBoardData(); ? ? #endif ConsoleClipboard.cpp #include "ConsoleClipboard.h" ? int main(int argc, char * argv) { SetClipBoardData(); GetClipBoardData(); ? system("pause"); } ? void SetClipBoardData() { //將 OpenClipboard 函數(shù)的參數(shù)指定為 NULL,表明為當(dāng)前進(jìn)程打開(kāi)剪貼板 if(OpenClipboard(NULL)) { char * pDataBuf; ? //全局內(nèi)存對(duì)象 HGLOBAL hGlobalClip; ? //給全局內(nèi)存對(duì)象分配全局內(nèi)存 hGlobalClip = GlobalAlloc(GHND, strlen(pStrData) + 1); //通過(guò)給全局內(nèi)存對(duì)象加鎖獲得對(duì)全局內(nèi)存塊的引用 pDataBuf = (char *)GlobalLock(hGlobalClip); strcpy(pDataBuf, pStrData); //使用完全局內(nèi)存塊后需要對(duì)全局內(nèi)存塊解鎖 GlobalUnlock(hGlobalClip); ? //清空剪貼板 EmptyClipboard(); //設(shè)置剪貼板數(shù)據(jù),這里直接將數(shù)據(jù)放到了剪貼板中,而沒(méi)有使用延遲提交技術(shù) SetClipboardData(CF_TEXT, hGlobalClip); //關(guān)閉剪貼板 CloseClipboard(); ? cout<<"設(shè)置剪貼板為: "<<pStrData<<endl<<endl; } } ? void GetClipBoardData() { if(OpenClipboard(NULL)) { //判斷剪貼板中的數(shù)據(jù)格式是否為 CF_TEXT if(IsClipboardFormatAvailable(CF_TEXT)) { char * pDataBuf; HGLOBAL hGlobalClip; ? //從剪貼板中獲取格式為 CF_TEXT 的數(shù)據(jù) hGlobalClip = GetClipboardData(CF_TEXT); pDataBuf = (char *)GlobalLock(hGlobalClip); GlobalUnlock(hGlobalClip); ? cout<<"從剪貼板中獲取到數(shù)據(jù): "<<pDataBuf<<endl<<endl; } CloseClipboard(); } }

效果展示:

程序運(yùn)行效果:

打開(kāi)記事本進(jìn)行粘貼操作:

???????????

????????????

延遲提交技術(shù)

什么是延遲提交技術(shù)?

當(dāng)把數(shù)據(jù)放入剪貼板中時(shí),一般來(lái)說(shuō)要制作一份數(shù)據(jù)的副本,

也就是要分配全局內(nèi)存,然后將數(shù)據(jù)再?gòu)?fù)制一份,然后再將包含這份副本的內(nèi)存塊句柄傳遞給剪貼板,

對(duì)于小數(shù)據(jù)量來(lái)說(shuō),這個(gè)沒(méi)什么,但是對(duì)于大數(shù)據(jù)量的話,就有問(wèn)題了,

你一使用剪貼板,就往里面復(fù)制個(gè)什么幾百?MB?的數(shù)據(jù),

那這個(gè)數(shù)據(jù)在剪貼板中的數(shù)據(jù)被其他數(shù)據(jù)取代之前都是存放在內(nèi)存中的啊,

這個(gè)方法也太齷齪了,你想啊,要是我就復(fù)制了一個(gè)?500MB?的數(shù)據(jù),然后我一直不再?gòu)?fù)制其他的東西,

那么這個(gè)?500MB?的數(shù)據(jù)就會(huì)一直駐留在內(nèi)存中,咦 . . . 太可怕了 !!!太浪費(fèi)內(nèi)存的使用效率了 !!!

為了解決上面這個(gè)問(wèn)題,就需要通過(guò)使用延遲提交技術(shù)來(lái)避免內(nèi)存的浪費(fèi),

當(dāng)使用延遲提交技術(shù)時(shí),實(shí)際上,直到另一個(gè)程序需要數(shù)據(jù)時(shí),程序才會(huì)提供這份數(shù)據(jù),

也就是,其實(shí)我一開(kāi)始?程序 A?并不往剪貼板中存放真實(shí)的數(shù)據(jù),

而只是告訴剪貼板,我往里面放了數(shù)據(jù)(其實(shí)數(shù)據(jù)還沒(méi)有放進(jìn)去),

而后,如果有其他的?程序 B?訪問(wèn)了剪貼板中的數(shù)據(jù),也就是執(zhí)行了“粘貼”操作,

那么此時(shí)操作系統(tǒng)就會(huì)去檢查數(shù)據(jù)是不是真正的存放在了剪貼板中,

如果剪貼板中存放了數(shù)據(jù),那么直接把數(shù)據(jù)送出去就可以了(這就沒(méi)有使用延遲提交技術(shù)了),

而如果剪貼板中沒(méi)有數(shù)據(jù),那么?Windows?就會(huì)給上次往剪貼板中存放數(shù)據(jù)(盡管沒(méi)有存放實(shí)際的數(shù)據(jù))的程序,

也就是?程序 A發(fā)送消息,

而后,我們的?程序 A?就可以再次調(diào)用?SetClipboardData?來(lái)將真實(shí)的數(shù)據(jù)放入到剪貼板中了,這樣就是延遲提交技術(shù)了。

要實(shí)現(xiàn)延遲提交技術(shù),則在?程序?A?中不應(yīng)該將數(shù)據(jù)句柄傳送給?Windows?

而是在?SetClipboardData?調(diào)用中使用?NULL

然后當(dāng)另外一個(gè)?程序?B?調(diào)用?GetClipboardData?函數(shù)時(shí),

Windows?就會(huì)檢查這種格式的數(shù)據(jù)在剪貼板中的句柄是否為?NULL?

如果為?NULL?,則?Windows?會(huì)給程序?A發(fā)送一個(gè)消息,從而請(qǐng)求到數(shù)據(jù)的實(shí)際句柄,

這個(gè)數(shù)據(jù)的實(shí)際句柄是?程序?A?在響應(yīng)消息的處理函數(shù)中重新調(diào)用?SetClipboardData?來(lái)提供的。

??????????????

延遲提交技術(shù)中涉及的三個(gè)消息:

下面提及的 程序 A 代表剪貼板當(dāng)前擁有者,也就是 程序 A 負(fù)責(zé)往剪貼板中寫入數(shù)據(jù),

而 程序 B 則代表從剪貼板中讀取出數(shù)據(jù),其沒(méi)有對(duì)剪貼板的所有權(quán)。

????????????

WM_RENDERFORMAT?:

當(dāng)?程序?B?調(diào)用?GetClipboardData?時(shí),Windows?將會(huì)給?程序?A?的窗口過(guò)程發(fā)送這個(gè)消息,

其中?wParam?參數(shù)的值是所要求的格式。

在處理這個(gè)消息時(shí),程序 A?就不再需要打開(kāi)或者清空剪貼板了,

也就是不需要再次調(diào)用?OpenClipboard?和?EmptyClipboard?函數(shù)了,

為什么不需要再次調(diào)用這兩個(gè)函數(shù)?

這是因?yàn)?#xff0c;我們一開(kāi)始的時(shí)候已經(jīng)調(diào)用了這兩個(gè)函數(shù)(如果一開(kāi)始沒(méi)有調(diào)用的話,窗口根本就不會(huì)接受到這個(gè)消息),

而此舉已經(jīng)告訴操作系統(tǒng)剪貼板已經(jīng)歸我所有了,而且里面的數(shù)據(jù)已經(jīng)被清空了,

剪貼板所有權(quán)都?xì)w我了,那還去打開(kāi)個(gè)鬼啊,不是浪費(fèi)嘛?

在處理這個(gè)消息時(shí),應(yīng)該為?wParam?所指定的格式創(chuàng)建一個(gè)全局內(nèi)存塊,

然后再把數(shù)據(jù)傳遞到這個(gè)全局內(nèi)存塊中,并要正確的格式和數(shù)據(jù)句柄再一次調(diào)用?SetClipboardData?函數(shù)。

也就是需要將數(shù)據(jù)真實(shí)的復(fù)制到剪貼板中了。

WM_RENDERALLFORAMTS?:

如果?程序?A?在它自己仍然是剪貼板所有者的時(shí)候就要終止運(yùn)行,

并且剪貼板上仍然包含著該?程序?A?用?SetClipboardData?所設(shè)置的?NULL?數(shù)據(jù)句柄(延遲提交技術(shù)),

也就是?程序 A?當(dāng)前還是剪貼板的所有者,但是用戶又單擊了關(guān)閉窗口,

而剪貼板中還沒(méi)有真實(shí)的數(shù)據(jù)存在(因?yàn)槭褂昧搜舆t提交技術(shù)),

即數(shù)據(jù)還沒(méi)有被提交給剪貼板,程序 A?就要死了,則此時(shí)?程 序?A?的窗口過(guò)程將接收到這個(gè)消息,

這個(gè)消息的一般處理為打開(kāi)剪貼板,并且清空剪貼板,然后把數(shù)據(jù)加載到內(nèi)存中,

并為每種格式調(diào)用?SetClipboardData?,然后再關(guān)閉剪貼板即可。

WM_DESTROYCLIPBOARD?:

當(dāng)在?程序?B?中調(diào)用?EmptyClipboard?時(shí),Windows?將會(huì)給?程序?A?的窗口過(guò)程發(fā)送這個(gè)消息。

即通知?程序?A?其已不再是剪貼板的擁有者了。

???????????????

?????????????

Demo2 – MFCClipboard(延遲提交技術(shù)的使用)

整個(gè)項(xiàng)目結(jié)構(gòu)很簡(jiǎn)單:

主界面:

添加 3 個(gè)消息處理:

消息映射函數(shù)聲明: protected: HICON m_hIcon; ? // 生成的消息映射函數(shù) virtual BOOL OnInitDialog(); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() public: afx_msg void OnDestroyClipboard(); afx_msg void OnRenderAllFormats(); afx_msg void OnRenderFormat(UINT nFormat); afx_msg void OnBnClickedBtnWrite(); afx_msg void OnBnClickedBtnRead(); CString m_CStrWrite; CString m_CStrRead; };

消息映射實(shí)現(xiàn):

void CMFCClipboardDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Text(pDX, IDC_EDIT_WRITE, m_CStrWrite); DDX_Text(pDX, IDC_EDIT_READ, m_CStrRead); } ? BEGIN_MESSAGE_MAP(CMFCClipboardDlg, CDialogEx) ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_WM_DESTROYCLIPBOARD() ON_WM_RENDERALLFORMATS() ON_WM_RENDERFORMAT() ON_BN_CLICKED(ID_BTN_WRITE, &CMFCClipboardDlg::OnBnClickedBtnWrite) ON_BN_CLICKED(ID_BTN_READ, &CMFCClipboardDlg::OnBnClickedBtnRead) END_MESSAGE_MAP()

消息映射函數(shù)實(shí)現(xiàn)

? //WM_DESTROYCLIPBOARD 消息處理函數(shù) void CMFCClipboardDlg::OnDestroyClipboard() { //當(dāng)有另外的程序調(diào)用 EmptyClipboard 時(shí), //Windows 將向當(dāng)前窗口過(guò)程發(fā)送 WM_DESTROYCLIPBOARD 消息 MessageBox(TEXT("很抱歉 , 您已失去對(duì)剪貼板的擁有權(quán) ..."), TEXT("提示"), MB_ICONINFORMATION); ? CDialogEx::OnDestroyClipboard(); } ? ? //WM_RENDERALLFORMATS 消息處理函數(shù) void CMFCClipboardDlg::OnRenderAllFormats() { //當(dāng)剪貼板中的數(shù)據(jù)句柄為當(dāng)前程序所擁有,而當(dāng)前程序又將被退出時(shí), //Windows 給該程序窗口發(fā)送 WM_RENDERALLFORMATS 消息 ? OpenClipboard(); EmptyClipboard(); CloseClipboard(); ? CDialogEx::OnRenderAllFormats(); } ? //WM_RENDERFORMAT 消息處理函數(shù) void CMFCClipboardDlg::OnRenderFormat(UINT nFormat) { //當(dāng)有另外的程序訪問(wèn)剪貼板時(shí) //Windows 給該程序窗口過(guò)程發(fā)送 WM_RENDERFORMAT 消息 int dataNum; int dataIndex; char * pDataBuf; HGLOBAL hGlobalClip; ? dataNum = this->m_CStrWrite.GetLength(); ? hGlobalClip = GlobalAlloc(GHND, dataNum + 1); pDataBuf = (char *)GlobalLock(hGlobalClip); for(dataIndex=0;dataIndex<dataNum;dataIndex++) { pDataBuf[dataIndex] = this->m_CStrWrite.GetAt(dataIndex); } GlobalUnlock(hGlobalClip); ? //此時(shí)需要將有效數(shù)據(jù)寫入到剪貼板中 SetClipboardData(CF_TEXT, hGlobalClip); ? CDialogEx::OnRenderFormat(nFormat); } ? ? void CMFCClipboardDlg::OnBnClickedBtnWrite() { UpdateData(); if(this->m_CStrWrite.GetLength() > 0) { if(OpenClipboard()) { EmptyClipboard(); SetClipboardData(CF_TEXT, NULL); CloseClipboard(); MessageBox(TEXT(" 恭喜您 , 設(shè)置剪貼板成功 ..."), TEXT("提示"), MB_ICONINFORMATION); } } } ? ? void CMFCClipboardDlg::OnBnClickedBtnRead() { if(OpenClipboard()) { //判斷剪貼板中的數(shù)據(jù)格式是否為 CF_TEXT if(IsClipboardFormatAvailable(CF_TEXT)) { char * pDataBuf; HGLOBAL hGlobalClip; ? //從剪貼板中獲取到指定格式的數(shù)據(jù) hGlobalClip = GetClipboardData(CF_TEXT); pDataBuf = (char *)GlobalLock(hGlobalClip); this->m_CStrRead = pDataBuf; GlobalUnlock(hGlobalClip); ? UpdateData(FALSE); } CloseClipboard(); } }

效果展示:

設(shè)置剪貼板中數(shù)據(jù):

當(dāng)前程序讀取剪貼板中數(shù)據(jù):

記事本程序讀取剪貼板中數(shù)據(jù):

測(cè)試當(dāng)前進(jìn)程失去剪貼板所有權(quán):

首先單擊當(dāng)前程序設(shè)置好剪貼板中的數(shù)據(jù),

然后打開(kāi)一個(gè)記事本文件,在在其中輸入一些數(shù)據(jù),然后選擇這部分?jǐn)?shù)據(jù),按下復(fù)制:

????????????

?????????????

結(jié)束語(yǔ)

對(duì)于剪貼板的使用呢,也就是那么幾個(gè)?API?在使用而已,熟悉一下就可以了,

關(guān)鍵是延遲提交技術(shù)的使用,同時(shí)還有對(duì)于全局內(nèi)存對(duì)象的理解還是有點(diǎn)難度的,

不過(guò),我相信我解釋的還是比較明白了,大家可以通過(guò)我的解釋再對(duì)照?Demo?來(lái)理解,

這樣理解起來(lái)容易快速一些。

上面介紹的是通過(guò)剪貼板來(lái)實(shí)現(xiàn)進(jìn)程之間的通信,其實(shí)這還是有問(wèn)題的,

因?yàn)槲覀兊募糍N板是位于本地機(jī)器上,所以,利用剪貼板還是無(wú)法實(shí)現(xiàn)本地進(jìn)程與遠(yuǎn)程進(jìn)程通信,

當(dāng)然要想實(shí)現(xiàn)本地進(jìn)程和遠(yuǎn)程進(jìn)程的通信,那也還是有辦法的,這會(huì)在后續(xù)博文中引出的。

然后的話,今天圣誕節(jié)嘛,祝諸位節(jié)日快樂(lè),也不是我崇洋媚外,說(shuō)個(gè)節(jié)日快樂(lè)還是可以的。

總結(jié)

以上是生活随笔為你收集整理的进程间通信——剪切板的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。