进程间通信——剪切板
要點(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)題。
- 上一篇: SysFader iexplore.ex
- 下一篇: DM3730 x-loader 分析 一