引言
進(jìn)程間的數(shù)據(jù)交換和共享是一種非常重要和實(shí)用的技術(shù)。大、中型軟件的開(kāi)發(fā)設(shè)計(jì)多是由眾多程序設(shè)計(jì)人員的合作完成,通常一個(gè)程序設(shè)計(jì)人員只負(fù)責(zé)其中一個(gè)或幾個(gè)模塊的開(kāi)發(fā),這些模塊可以是動(dòng)態(tài)鏈接庫(kù)也可以是應(yīng)用程序或是其他形式的程序組件。這些獨(dú)立開(kāi)發(fā)出來(lái)的程序模塊最終需要作為一個(gè)整體來(lái)運(yùn)行,即組成一個(gè)系統(tǒng),在系統(tǒng)運(yùn)行期間這些模塊往往需要頻繁地進(jìn)行數(shù)據(jù)交換和數(shù)據(jù)共享,對(duì)于動(dòng)態(tài)鏈接庫(kù)同其主調(diào)應(yīng)用程序之間的數(shù)據(jù)交換是非常容易實(shí)現(xiàn)的,但是在兩個(gè)應(yīng)用程序之間或是動(dòng)態(tài)鏈接庫(kù)同其主調(diào)應(yīng)用程序之外的其他應(yīng)用程序進(jìn)行數(shù)據(jù)交換就比較困難了。尤其是在交換數(shù)據(jù)量過(guò)大、交換過(guò)于頻繁的情況下更是難以實(shí)現(xiàn),本文即對(duì)此展開(kāi)討論,并提出了一種通過(guò)共享內(nèi)存來(lái)實(shí)現(xiàn)進(jìn)程見(jiàn)大數(shù)據(jù)量快速交換的一種方法。
通訊方式的比較和選擇
進(jìn)程間通訊的方式有很多,常用的有共享內(nèi)存、命名管道和匿名管道、發(fā)送消息等幾種方法來(lái)直接完成,另外還可以通過(guò)socket口、配置文件和注冊(cè)表等來(lái)間接實(shí)現(xiàn)進(jìn)程間數(shù)據(jù)通訊任務(wù)。以上這幾種方法各有優(yōu)缺點(diǎn),具體到在進(jìn)程間進(jìn)行大數(shù)據(jù)量數(shù)據(jù)的快速交換問(wèn)題上,則可以排除使用配置文件和注冊(cè)表的方法;另外,由于管道和socket套接字的使用需要有網(wǎng)卡的支持,因此也可以不予考慮。這樣,可供選擇的通訊方式只剩下共享內(nèi)存和發(fā)送消息兩種。由于數(shù)據(jù)量比較大,這樣在使用消息進(jìn)行通訊時(shí)就無(wú)法通過(guò)消息參數(shù)將數(shù)據(jù)直接攜帶到接收方,只能以地址傳送的方式進(jìn)行。當(dāng)一個(gè)應(yīng)用程序向另一個(gè)應(yīng)用程序發(fā)送數(shù)據(jù)時(shí)將會(huì)發(fā)出WM_COPYDATA系統(tǒng)消息,因此可以考慮通過(guò)向消息隊(duì)列插入WM_COPYDATA消息的方法來(lái)實(shí)現(xiàn)數(shù)據(jù)在進(jìn)程間的拷貝。
在使用WM_COPYDATA消息時(shí),由第一個(gè)消息參數(shù)指定發(fā)送窗口的句柄,第二個(gè)消息參數(shù)則為一同數(shù)據(jù)相關(guān)的數(shù)據(jù)結(jié)構(gòu)COPYDATASTRUCT的指針,此結(jié)構(gòu)原形聲明如下:
typedef struct tagCOPYDATASTRUCT {
DWORD dwData;
DWORD cbData;
PVOID lpData;
} COPYDATASTRUCT;
其中,只需將待發(fā)送數(shù)據(jù)的首地址賦予lpData、并由cbData指明數(shù)據(jù)塊長(zhǎng)度即可。消息發(fā)出后,接收方程序在WM_COPYDATA消息的響應(yīng)函數(shù)中通過(guò)隨消息傳遞進(jìn)來(lái)的第二個(gè)參數(shù)完成對(duì)數(shù)據(jù)塊的接收。但是在使用WM_COPYDATA消息時(shí),只能用SendMessage()函數(shù)發(fā)送而不能使用PostMessage(),這兩個(gè)函數(shù)雖然功能非常相似都是負(fù)責(zé)向指定的窗口發(fā)送消息,但是SendMessage()函數(shù)發(fā)出消息后不是馬上返回,而是在接收方的消息響應(yīng)函數(shù)處理完之后才能返回,并能夠得到返回結(jié)果。在此期間發(fā)送方程序?qū)⒈蛔枞?,SendMessage()后面的語(yǔ)句不能被繼續(xù)執(zhí)行。而PostMessage()函數(shù)在發(fā)出消息后馬上返回,其后語(yǔ)句能夠被立即執(zhí)行,但是無(wú)法獲取消息的執(zhí)行結(jié)果??梢?jiàn),在交換數(shù)據(jù)量較大的情況下實(shí)現(xiàn)數(shù)據(jù)頻繁而又快速的交換用發(fā)送WM_COPYDATA消息的方法也是不合適的,當(dāng)數(shù)據(jù)傳輸過(guò)于頻繁時(shí)將有可能導(dǎo)致數(shù)據(jù)的丟失。
比之以上幾種進(jìn)程間通訊方法,共享內(nèi)存有著明顯的優(yōu)勢(shì)。共享內(nèi)存是通過(guò)直接操作內(nèi)存映射文件來(lái)進(jìn)行的,而內(nèi)存映射文件又是進(jìn)行單機(jī)數(shù)據(jù)共享的最低層機(jī)制,前面幾種數(shù)據(jù)交換方式在低層都是通過(guò)內(nèi)存映射文件來(lái)進(jìn)行的。因此使用共享內(nèi)存可以以較小的開(kāi)銷獲取較高的性能,是進(jìn)行大數(shù)據(jù)量數(shù)據(jù)快速交換的方案。
共享內(nèi)存的使用
在Windows操作系統(tǒng)下,任何一個(gè)進(jìn)程不允許讀取、寫(xiě)入或是修改另一個(gè)進(jìn)程的數(shù)據(jù)(包括變量、對(duì)象和內(nèi)存分配等),但是在某個(gè)進(jìn)程內(nèi)創(chuàng)建的文件映射對(duì)象的視圖卻能夠?yàn)槎鄠€(gè)其他進(jìn)程所映射,這些進(jìn)程共享的是物理存儲(chǔ)器的同一個(gè)頁(yè)面。因此,當(dāng)一個(gè)進(jìn)程將數(shù)據(jù)寫(xiě)入此共享文件映射對(duì)象的視圖時(shí),其他進(jìn)程可以立即獲取數(shù)據(jù)變更情況。為了進(jìn)一步提高數(shù)據(jù)交換的速度,還可以采用由系統(tǒng)頁(yè)文件支持的內(nèi)存映射文件而直接在內(nèi)存區(qū)域使用,顯然這種共享內(nèi)存的方式是完全可以滿足在進(jìn)程間進(jìn)行大數(shù)據(jù)量數(shù)據(jù)快速傳輸任務(wù)要求的。下面給出在兩個(gè)相互獨(dú)立的進(jìn)程間通過(guò)文件映射對(duì)象來(lái)分配和訪問(wèn)同一個(gè)共享內(nèi)存塊的應(yīng)用實(shí)例。在本例中,由發(fā)送方程序負(fù)責(zé)向接收方程序發(fā)送數(shù)據(jù),文件映射對(duì)象由發(fā)送方創(chuàng)建和關(guān)閉,并且指定一個(gè)的名字供接收程序使用。接收方程序直接通過(guò)這個(gè)指定的名字打開(kāi)此文件映射對(duì)象,并完成對(duì)數(shù)據(jù)的接收。
在發(fā)送方程序中,首先通過(guò)CreateFileMapping()函數(shù)創(chuàng)建一個(gè)內(nèi)存映射文件對(duì)象,如果創(chuàng)建成功則通過(guò)MapViewOfFile()函數(shù)將此文件映射對(duì)象的視圖映射進(jìn)地址空間,同時(shí)得到此映射視圖的首址??梢?jiàn),共享內(nèi)存的創(chuàng)建主要是通過(guò)這兩個(gè)函數(shù)完成的。這兩個(gè)函數(shù)原形聲明如下:
HANDLE CreateFileMapping(HANDLE hFile,
LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
DWORD flProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
LPCTSTR lpName);
LPVOID MapViewOfFile(HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
DWORD dwNumberOfBytesToMap);
CreateFileMapping()函數(shù)參數(shù)hFile指定了待映射到進(jìn)程地址空間的文件句柄,如果為無(wú)效句柄則系統(tǒng)會(huì)創(chuàng)建一個(gè)使用來(lái)自頁(yè)文件而非指定磁盤(pán)文件存儲(chǔ)器的文件映射對(duì)象。很顯然,在本例中為了數(shù)據(jù)能快速交換,需要人為將此參數(shù)設(shè)定為INVALID_HANDLE_VALUE;參數(shù)flProtect設(shè)定了系統(tǒng)對(duì)頁(yè)面采取的保護(hù)屬性,由于需要進(jìn)行讀寫(xiě)操作,因此可以設(shè)置保護(hù)屬性PAGE_READWRITE;雙字型參數(shù)dwMaximumSizeHigh和dwMaximumSizeLow指定了所開(kāi)辟共享內(nèi)存區(qū)的字節(jié)數(shù);最后的參數(shù)lpName用來(lái)給此共享內(nèi)存設(shè)定一個(gè)名字,接收程序可以通過(guò)這個(gè)名字將其打開(kāi)。MapViewOfFile()函數(shù)的參數(shù)hFileMappingObject為CreateFileMapping()返回的內(nèi)存文件映像對(duì)象句柄;參數(shù)dwDesiredAccess再次指定對(duì)其數(shù)據(jù)的訪問(wèn)方式,而且需要同CreateFileMapping()函數(shù)所設(shè)置的保護(hù)屬性相匹配。這里對(duì)保護(hù)屬性的重復(fù)設(shè)置可以確保應(yīng)用程序能更多的對(duì)數(shù)據(jù)的保護(hù)屬性進(jìn)行有效控制。下面給出創(chuàng)建共享內(nèi)存的部分關(guān)鍵代
hRecvMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 1000000, "DataMap");
if (hRecvMap != NULL)
{
lpData = (LPBYTE)MapViewOfFile(hRecvMap, FILE_MAP_WRITE, 0, 0, 0);
if (lpData == NULL)
{
CloseHandle(hRecvMap);
hRecvMap = NULL;
}
}
// 通知接收程序內(nèi)存文件映射對(duì)象的視圖已經(jīng)打開(kāi)HWND hRecv = ::FindWindow(NULL, DECODE_PROGRAMM);
if (hRecv != NULL)
::PostMessage(hRecv, WM_MAP_OPEN, 0, 0);
數(shù)據(jù)的傳送實(shí)際是將數(shù)據(jù)從發(fā)送方寫(xiě)到共享內(nèi)存中,然后由接收程序及時(shí)從中取走即可。數(shù)據(jù)從發(fā)送方程序?qū)懙焦蚕韮?nèi)存比較簡(jiǎn)單,只需用memcpy()函數(shù)將數(shù)據(jù)拷貝過(guò)去,關(guān)鍵在于能及時(shí)通知接收程序數(shù)據(jù)已寫(xiě)入到共享內(nèi)存,并讓其即使取走。在這里仍采取消息通知的方式,當(dāng)數(shù)據(jù)寫(xiě)入共享內(nèi)存后通過(guò)PostMessage()函數(shù)向接收方程序發(fā)送消息,接收方在消息響應(yīng)函數(shù)中完成對(duì)數(shù)據(jù)的讀?。?BR> // 數(shù)據(jù)復(fù)制到共享內(nèi)存
memcpy(lpData, RecVBuf, sizeof(RecvBuf));
// 通知接收方接收數(shù)據(jù)
HWND hDeCode = ::FindWindow(NULL, DECODE_PROGRAMM);
if (hDeCode != NULL)
::PostMessage(hDeCode, WM_DATA_READY, (WPARAM)0, (LPARAM)sizeof(RecvBuf));
當(dāng)數(shù)據(jù)傳輸結(jié)束,即將退出程序時(shí),需要將映射進(jìn)來(lái)的內(nèi)存文件映射對(duì)象視圖卸載和資源的釋放等處理。這部分工作主要由UnmapViewOfFile()和CloseHandle()等函數(shù)完成:
HWND hDeCode = ::FindWindow(NULL, DECODE_PROGRAMM);
if (hDeCode != NULL)
::PostMessage(hDeCode, WM_MAP_CLOSE, 0, 0);
if (lpData != NULL)
{
UnmapViewOfFile(lpData);
lpData = NULL;
}
if (hRecvMap != NULL)
{
CloseHandle(hRecvMap);
hRecvMap = NULL;
}
在接收程序中,在收到由發(fā)送放發(fā)出的WM_MAP_OPEN消息后,由OpenFileMapping()函數(shù)打開(kāi)由名字"DataMap"指定的文件映射對(duì)象,如果執(zhí)行成功,繼續(xù)用MapViewOfFile()函數(shù)將此文件映射對(duì)象的視圖映射到接收應(yīng)用程序的地址空間并得到其首址:
m_hReceiveMap = OpenFileMapping(FILE_MAP_READ, FALSE, "DataMap");
if (m_hReceiveMap == NULL)
return;
m_lpbReceiveBuf = (LPBYTE)MapViewOfFile(m_hReceiveMap,FILE_MAP_READ,0,0,0);
if (m_lpbReceiveBuf == NULL)
{
CloseHandle(m_hReceiveMap);
m_hReceiveMap=NULL;
}
當(dāng)發(fā)送方程序?qū)?shù)據(jù)寫(xiě)入到共享內(nèi)存后,接收方將收到消息WM_DATA_READY,在響應(yīng)函數(shù)中將數(shù)據(jù)從共享內(nèi)存復(fù)制到本地緩存中,再進(jìn)行后續(xù)的處理。同發(fā)送程序類似,在接收程序數(shù)據(jù)接收完畢后,也需要用UnmapViewOfFile()、CloseHandle()等函數(shù)完成對(duì)文件視圖等打開(kāi)過(guò)資源的釋放:
// 從共享內(nèi)存接收數(shù)據(jù)
memcpy(RecvBuf, (char*)(m_lpbReceiveBuf), (int)lParam);
……
// 程序退出前資源的釋放
UnmapViewOfFile(m_lpbReceiveBuf);
m_lpbReceiveBuf = NULL;
CloseHandle(m_hReceiveMap);
m_hReceiveMap = NULL;
小結(jié)
經(jīng)實(shí)際測(cè)試,使用共享內(nèi)存在處理大數(shù)據(jù)量數(shù)據(jù)的快速交換時(shí)表現(xiàn)出了良好的性能,在數(shù)據(jù)可靠性等方面要遠(yuǎn)遠(yuǎn)高于發(fā)送WM_COPYDATA消息的方式。這種大容量、高速的數(shù)據(jù)共享處理方式在設(shè)計(jì)高速數(shù)傳通訊類軟件中有著很好的使用效果。本文所述代碼在Windows 2000下由Microsoft Visual C++ 6.0編譯通過(guò)。
進(jìn)程間的數(shù)據(jù)交換和共享是一種非常重要和實(shí)用的技術(shù)。大、中型軟件的開(kāi)發(fā)設(shè)計(jì)多是由眾多程序設(shè)計(jì)人員的合作完成,通常一個(gè)程序設(shè)計(jì)人員只負(fù)責(zé)其中一個(gè)或幾個(gè)模塊的開(kāi)發(fā),這些模塊可以是動(dòng)態(tài)鏈接庫(kù)也可以是應(yīng)用程序或是其他形式的程序組件。這些獨(dú)立開(kāi)發(fā)出來(lái)的程序模塊最終需要作為一個(gè)整體來(lái)運(yùn)行,即組成一個(gè)系統(tǒng),在系統(tǒng)運(yùn)行期間這些模塊往往需要頻繁地進(jìn)行數(shù)據(jù)交換和數(shù)據(jù)共享,對(duì)于動(dòng)態(tài)鏈接庫(kù)同其主調(diào)應(yīng)用程序之間的數(shù)據(jù)交換是非常容易實(shí)現(xiàn)的,但是在兩個(gè)應(yīng)用程序之間或是動(dòng)態(tài)鏈接庫(kù)同其主調(diào)應(yīng)用程序之外的其他應(yīng)用程序進(jìn)行數(shù)據(jù)交換就比較困難了。尤其是在交換數(shù)據(jù)量過(guò)大、交換過(guò)于頻繁的情況下更是難以實(shí)現(xiàn),本文即對(duì)此展開(kāi)討論,并提出了一種通過(guò)共享內(nèi)存來(lái)實(shí)現(xiàn)進(jìn)程見(jiàn)大數(shù)據(jù)量快速交換的一種方法。
通訊方式的比較和選擇
進(jìn)程間通訊的方式有很多,常用的有共享內(nèi)存、命名管道和匿名管道、發(fā)送消息等幾種方法來(lái)直接完成,另外還可以通過(guò)socket口、配置文件和注冊(cè)表等來(lái)間接實(shí)現(xiàn)進(jìn)程間數(shù)據(jù)通訊任務(wù)。以上這幾種方法各有優(yōu)缺點(diǎn),具體到在進(jìn)程間進(jìn)行大數(shù)據(jù)量數(shù)據(jù)的快速交換問(wèn)題上,則可以排除使用配置文件和注冊(cè)表的方法;另外,由于管道和socket套接字的使用需要有網(wǎng)卡的支持,因此也可以不予考慮。這樣,可供選擇的通訊方式只剩下共享內(nèi)存和發(fā)送消息兩種。由于數(shù)據(jù)量比較大,這樣在使用消息進(jìn)行通訊時(shí)就無(wú)法通過(guò)消息參數(shù)將數(shù)據(jù)直接攜帶到接收方,只能以地址傳送的方式進(jìn)行。當(dāng)一個(gè)應(yīng)用程序向另一個(gè)應(yīng)用程序發(fā)送數(shù)據(jù)時(shí)將會(huì)發(fā)出WM_COPYDATA系統(tǒng)消息,因此可以考慮通過(guò)向消息隊(duì)列插入WM_COPYDATA消息的方法來(lái)實(shí)現(xiàn)數(shù)據(jù)在進(jìn)程間的拷貝。
在使用WM_COPYDATA消息時(shí),由第一個(gè)消息參數(shù)指定發(fā)送窗口的句柄,第二個(gè)消息參數(shù)則為一同數(shù)據(jù)相關(guān)的數(shù)據(jù)結(jié)構(gòu)COPYDATASTRUCT的指針,此結(jié)構(gòu)原形聲明如下:
typedef struct tagCOPYDATASTRUCT {
DWORD dwData;
DWORD cbData;
PVOID lpData;
} COPYDATASTRUCT;
其中,只需將待發(fā)送數(shù)據(jù)的首地址賦予lpData、并由cbData指明數(shù)據(jù)塊長(zhǎng)度即可。消息發(fā)出后,接收方程序在WM_COPYDATA消息的響應(yīng)函數(shù)中通過(guò)隨消息傳遞進(jìn)來(lái)的第二個(gè)參數(shù)完成對(duì)數(shù)據(jù)塊的接收。但是在使用WM_COPYDATA消息時(shí),只能用SendMessage()函數(shù)發(fā)送而不能使用PostMessage(),這兩個(gè)函數(shù)雖然功能非常相似都是負(fù)責(zé)向指定的窗口發(fā)送消息,但是SendMessage()函數(shù)發(fā)出消息后不是馬上返回,而是在接收方的消息響應(yīng)函數(shù)處理完之后才能返回,并能夠得到返回結(jié)果。在此期間發(fā)送方程序?qū)⒈蛔枞?,SendMessage()后面的語(yǔ)句不能被繼續(xù)執(zhí)行。而PostMessage()函數(shù)在發(fā)出消息后馬上返回,其后語(yǔ)句能夠被立即執(zhí)行,但是無(wú)法獲取消息的執(zhí)行結(jié)果??梢?jiàn),在交換數(shù)據(jù)量較大的情況下實(shí)現(xiàn)數(shù)據(jù)頻繁而又快速的交換用發(fā)送WM_COPYDATA消息的方法也是不合適的,當(dāng)數(shù)據(jù)傳輸過(guò)于頻繁時(shí)將有可能導(dǎo)致數(shù)據(jù)的丟失。
比之以上幾種進(jìn)程間通訊方法,共享內(nèi)存有著明顯的優(yōu)勢(shì)。共享內(nèi)存是通過(guò)直接操作內(nèi)存映射文件來(lái)進(jìn)行的,而內(nèi)存映射文件又是進(jìn)行單機(jī)數(shù)據(jù)共享的最低層機(jī)制,前面幾種數(shù)據(jù)交換方式在低層都是通過(guò)內(nèi)存映射文件來(lái)進(jìn)行的。因此使用共享內(nèi)存可以以較小的開(kāi)銷獲取較高的性能,是進(jìn)行大數(shù)據(jù)量數(shù)據(jù)快速交換的方案。
共享內(nèi)存的使用
在Windows操作系統(tǒng)下,任何一個(gè)進(jìn)程不允許讀取、寫(xiě)入或是修改另一個(gè)進(jìn)程的數(shù)據(jù)(包括變量、對(duì)象和內(nèi)存分配等),但是在某個(gè)進(jìn)程內(nèi)創(chuàng)建的文件映射對(duì)象的視圖卻能夠?yàn)槎鄠€(gè)其他進(jìn)程所映射,這些進(jìn)程共享的是物理存儲(chǔ)器的同一個(gè)頁(yè)面。因此,當(dāng)一個(gè)進(jìn)程將數(shù)據(jù)寫(xiě)入此共享文件映射對(duì)象的視圖時(shí),其他進(jìn)程可以立即獲取數(shù)據(jù)變更情況。為了進(jìn)一步提高數(shù)據(jù)交換的速度,還可以采用由系統(tǒng)頁(yè)文件支持的內(nèi)存映射文件而直接在內(nèi)存區(qū)域使用,顯然這種共享內(nèi)存的方式是完全可以滿足在進(jìn)程間進(jìn)行大數(shù)據(jù)量數(shù)據(jù)快速傳輸任務(wù)要求的。下面給出在兩個(gè)相互獨(dú)立的進(jìn)程間通過(guò)文件映射對(duì)象來(lái)分配和訪問(wèn)同一個(gè)共享內(nèi)存塊的應(yīng)用實(shí)例。在本例中,由發(fā)送方程序負(fù)責(zé)向接收方程序發(fā)送數(shù)據(jù),文件映射對(duì)象由發(fā)送方創(chuàng)建和關(guān)閉,并且指定一個(gè)的名字供接收程序使用。接收方程序直接通過(guò)這個(gè)指定的名字打開(kāi)此文件映射對(duì)象,并完成對(duì)數(shù)據(jù)的接收。
在發(fā)送方程序中,首先通過(guò)CreateFileMapping()函數(shù)創(chuàng)建一個(gè)內(nèi)存映射文件對(duì)象,如果創(chuàng)建成功則通過(guò)MapViewOfFile()函數(shù)將此文件映射對(duì)象的視圖映射進(jìn)地址空間,同時(shí)得到此映射視圖的首址??梢?jiàn),共享內(nèi)存的創(chuàng)建主要是通過(guò)這兩個(gè)函數(shù)完成的。這兩個(gè)函數(shù)原形聲明如下:
HANDLE CreateFileMapping(HANDLE hFile,
LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
DWORD flProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
LPCTSTR lpName);
LPVOID MapViewOfFile(HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
DWORD dwNumberOfBytesToMap);
CreateFileMapping()函數(shù)參數(shù)hFile指定了待映射到進(jìn)程地址空間的文件句柄,如果為無(wú)效句柄則系統(tǒng)會(huì)創(chuàng)建一個(gè)使用來(lái)自頁(yè)文件而非指定磁盤(pán)文件存儲(chǔ)器的文件映射對(duì)象。很顯然,在本例中為了數(shù)據(jù)能快速交換,需要人為將此參數(shù)設(shè)定為INVALID_HANDLE_VALUE;參數(shù)flProtect設(shè)定了系統(tǒng)對(duì)頁(yè)面采取的保護(hù)屬性,由于需要進(jìn)行讀寫(xiě)操作,因此可以設(shè)置保護(hù)屬性PAGE_READWRITE;雙字型參數(shù)dwMaximumSizeHigh和dwMaximumSizeLow指定了所開(kāi)辟共享內(nèi)存區(qū)的字節(jié)數(shù);最后的參數(shù)lpName用來(lái)給此共享內(nèi)存設(shè)定一個(gè)名字,接收程序可以通過(guò)這個(gè)名字將其打開(kāi)。MapViewOfFile()函數(shù)的參數(shù)hFileMappingObject為CreateFileMapping()返回的內(nèi)存文件映像對(duì)象句柄;參數(shù)dwDesiredAccess再次指定對(duì)其數(shù)據(jù)的訪問(wèn)方式,而且需要同CreateFileMapping()函數(shù)所設(shè)置的保護(hù)屬性相匹配。這里對(duì)保護(hù)屬性的重復(fù)設(shè)置可以確保應(yīng)用程序能更多的對(duì)數(shù)據(jù)的保護(hù)屬性進(jìn)行有效控制。下面給出創(chuàng)建共享內(nèi)存的部分關(guān)鍵代
hRecvMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 1000000, "DataMap");
if (hRecvMap != NULL)
{
lpData = (LPBYTE)MapViewOfFile(hRecvMap, FILE_MAP_WRITE, 0, 0, 0);
if (lpData == NULL)
{
CloseHandle(hRecvMap);
hRecvMap = NULL;
}
}
// 通知接收程序內(nèi)存文件映射對(duì)象的視圖已經(jīng)打開(kāi)HWND hRecv = ::FindWindow(NULL, DECODE_PROGRAMM);
if (hRecv != NULL)
::PostMessage(hRecv, WM_MAP_OPEN, 0, 0);
數(shù)據(jù)的傳送實(shí)際是將數(shù)據(jù)從發(fā)送方寫(xiě)到共享內(nèi)存中,然后由接收程序及時(shí)從中取走即可。數(shù)據(jù)從發(fā)送方程序?qū)懙焦蚕韮?nèi)存比較簡(jiǎn)單,只需用memcpy()函數(shù)將數(shù)據(jù)拷貝過(guò)去,關(guān)鍵在于能及時(shí)通知接收程序數(shù)據(jù)已寫(xiě)入到共享內(nèi)存,并讓其即使取走。在這里仍采取消息通知的方式,當(dāng)數(shù)據(jù)寫(xiě)入共享內(nèi)存后通過(guò)PostMessage()函數(shù)向接收方程序發(fā)送消息,接收方在消息響應(yīng)函數(shù)中完成對(duì)數(shù)據(jù)的讀?。?BR> // 數(shù)據(jù)復(fù)制到共享內(nèi)存
memcpy(lpData, RecVBuf, sizeof(RecvBuf));
// 通知接收方接收數(shù)據(jù)
HWND hDeCode = ::FindWindow(NULL, DECODE_PROGRAMM);
if (hDeCode != NULL)
::PostMessage(hDeCode, WM_DATA_READY, (WPARAM)0, (LPARAM)sizeof(RecvBuf));
當(dāng)數(shù)據(jù)傳輸結(jié)束,即將退出程序時(shí),需要將映射進(jìn)來(lái)的內(nèi)存文件映射對(duì)象視圖卸載和資源的釋放等處理。這部分工作主要由UnmapViewOfFile()和CloseHandle()等函數(shù)完成:
HWND hDeCode = ::FindWindow(NULL, DECODE_PROGRAMM);
if (hDeCode != NULL)
::PostMessage(hDeCode, WM_MAP_CLOSE, 0, 0);
if (lpData != NULL)
{
UnmapViewOfFile(lpData);
lpData = NULL;
}
if (hRecvMap != NULL)
{
CloseHandle(hRecvMap);
hRecvMap = NULL;
}
在接收程序中,在收到由發(fā)送放發(fā)出的WM_MAP_OPEN消息后,由OpenFileMapping()函數(shù)打開(kāi)由名字"DataMap"指定的文件映射對(duì)象,如果執(zhí)行成功,繼續(xù)用MapViewOfFile()函數(shù)將此文件映射對(duì)象的視圖映射到接收應(yīng)用程序的地址空間并得到其首址:
m_hReceiveMap = OpenFileMapping(FILE_MAP_READ, FALSE, "DataMap");
if (m_hReceiveMap == NULL)
return;
m_lpbReceiveBuf = (LPBYTE)MapViewOfFile(m_hReceiveMap,FILE_MAP_READ,0,0,0);
if (m_lpbReceiveBuf == NULL)
{
CloseHandle(m_hReceiveMap);
m_hReceiveMap=NULL;
}
當(dāng)發(fā)送方程序?qū)?shù)據(jù)寫(xiě)入到共享內(nèi)存后,接收方將收到消息WM_DATA_READY,在響應(yīng)函數(shù)中將數(shù)據(jù)從共享內(nèi)存復(fù)制到本地緩存中,再進(jìn)行后續(xù)的處理。同發(fā)送程序類似,在接收程序數(shù)據(jù)接收完畢后,也需要用UnmapViewOfFile()、CloseHandle()等函數(shù)完成對(duì)文件視圖等打開(kāi)過(guò)資源的釋放:
// 從共享內(nèi)存接收數(shù)據(jù)
memcpy(RecvBuf, (char*)(m_lpbReceiveBuf), (int)lParam);
……
// 程序退出前資源的釋放
UnmapViewOfFile(m_lpbReceiveBuf);
m_lpbReceiveBuf = NULL;
CloseHandle(m_hReceiveMap);
m_hReceiveMap = NULL;
小結(jié)
經(jīng)實(shí)際測(cè)試,使用共享內(nèi)存在處理大數(shù)據(jù)量數(shù)據(jù)的快速交換時(shí)表現(xiàn)出了良好的性能,在數(shù)據(jù)可靠性等方面要遠(yuǎn)遠(yuǎn)高于發(fā)送WM_COPYDATA消息的方式。這種大容量、高速的數(shù)據(jù)共享處理方式在設(shè)計(jì)高速數(shù)傳通訊類軟件中有著很好的使用效果。本文所述代碼在Windows 2000下由Microsoft Visual C++ 6.0編譯通過(guò)。