在Windows操作系統下,任何一個(gè)進(jìn)程不允許讀取、寫(xiě)入或是修改另一個(gè)進(jìn)程的數據(包括變量、對象和內存分配等),但是在某個(gè)進(jìn)程內創(chuàng )建的文件映射對象的視圖卻能夠為多個(gè)其他進(jìn)程所映射,這些進(jìn)程共享的是物理存儲器的同一個(gè)頁(yè)面。因此,當一個(gè)進(jìn)程將數據寫(xiě)入此共享文件映射對象的視圖時(shí),其他進(jìn)程可以立即獲取數據變更情況。為了進(jìn)一步提高數據交換的速度,還可以采用由系統頁(yè)文件支持的內存映射文件而直接在內存區域使用,顯然這種共享內存的方式是完全可以滿(mǎn)足在進(jìn)程間進(jìn)行大數據量數據快速傳輸任務(wù)要求的。下面給出在兩個(gè)相互獨立的進(jìn)程間通過(guò)文件映射對象來(lái)分配和訪(fǎng)問(wèn)同一個(gè)共享內存塊的應用實(shí)例。在本例中,由發(fā)送方程序負責向接收方程序發(fā)送數據,文件映射對象由發(fā)送方創(chuàng )建和關(guān)閉,并且指定一個(gè)唯一的名字供接收程序使用。接收方程序直接通過(guò)這個(gè)唯一指定的名字打開(kāi)此文件映射對象,并完成對數據的接收。
在發(fā)送方程序中,首先通過(guò)CreateFileMapping()函數創(chuàng )建一個(gè)內存映射文件對象,如果創(chuàng )建成功則通過(guò)MapViewOfFile()函數將此文件映射對象的視圖映射進(jìn)地址空間,同時(shí)得到此映射視圖的首址??梢?jiàn),共享內存的創(chuàng )建主要是通過(guò)這兩個(gè)函數完成的。這兩個(gè)函數原形聲明如下:
HANDLE CreateFileMapping(HANDLE hFile,
LPSECURITY_ATTRIBUTESlpFileMappingAttributes,
DWORD flProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
LPCTSTR lpName);
LPVOID MapViewOfFile(HANDLEhFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
DWORD dwNumberOfBytesToMap);
CreateFileMapping()函數參數hFile指定了待映射到進(jìn)程地址空間的文件句柄,如果為無(wú)效句柄則系統會(huì )創(chuàng )建一個(gè)使用來(lái)自頁(yè)文件而非指定磁盤(pán)文件存儲器的文件映射對象。很顯然,在本例中為了數據能快速交換,需要人為將此參數設定為INVALID_HANDLE_VALUE;參數flProtect設定了系統對頁(yè)面采取的保護屬性,由于需要進(jìn)行讀寫(xiě)操作,因此可以設置保護屬性PAGE_READWRITE;雙字型參數dwMaximumSizeHigh和dwMaximumSizeLow指定了所開(kāi)辟共享內存區的最大字節數;最后的參數lpName用來(lái)給此共享內存設定一個(gè)名字,接收程序可以通過(guò)這個(gè)名字將其打開(kāi)。MapViewOfFile()函數的參數hFileMappingObject為CreateFileMapping()返回的內存文件映像對象句柄;參數dwDesiredAccess再次指定對其數據的訪(fǎng)問(wèn)方式,而且需要同CreateFileMapping()函數所設置的保護屬性相匹配。這里對保護屬性的重復設置可以確保應用程序能更多的對數據的保護屬性進(jìn)行有效控制。下面給出創(chuàng )建共享內存的部分關(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;
}
}
// 通知接收程序內存文件映射對象的視圖已經(jīng)打開(kāi)
HWND hRecv = ::FindWindow(NULL,DECODE_PROGRAMM);
if (hRecv != NULL)
::PostMessage(hRecv, WM_MAP_OPEN, 0, 0);
數據的傳送實(shí)際是將數據從發(fā)送方寫(xiě)到共享內存中,然后由接收程序及時(shí)從中取走即可。數據從發(fā)送方程序寫(xiě)到共享內存比較簡(jiǎn)單,只需用memcpy()函數將數據拷貝過(guò)去,關(guān)鍵在于能及時(shí)通知接收程序數據已寫(xiě)入到共享內存,并讓其即使取走。在這里仍采取消息通知的方式,當數據寫(xiě)入共享內存后通過(guò)PostMessage()函數向接收方程序發(fā)送消息,接收方在消息響應函數中完成對數據的讀?。?/span>
// 數據復制到共享內存
memcpy(lpData, RecvBuf, sizeof(RecvBuf));
// 通知接收方接收數據
HWND hDeCode = ::FindWindow(NULL, DECODE_PROGRAMM);
if (hDeCode != NULL)
::PostMessage(hDeCode, WM_DATA_READY,(WPARAM)0, (LPARAM)sizeof(RecvBuf));
當數據傳輸結束,即將退出程序時(shí),需要將映射進(jìn)來(lái)的內存文件映射對象視圖卸載和資源的釋放等處理。這部分工作主要由UnmapViewOfFile()和CloseHandle()等函數完成:
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()函數打開(kāi)由名字"DataMap"指定的文件映射對象,如果執行成功,繼續用MapViewOfFile()函數將此文件映射對象的視圖映射到接收應用程序的地址空間并得到其首址:
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;
}
當發(fā)送方程序將數據寫(xiě)入到共享內存后,接收方將收到消息WM_DATA_READY,在響應函數中將數據從共享內存復制到本地緩存中,再進(jìn)行后續的處理。同發(fā)送程序類(lèi)似,在接收程序數據接收完畢后,也需要用UnmapViewOfFile()、CloseHandle()等函數完成對文件視圖等打開(kāi)過(guò)資源的釋放:
// 從共享內存接收數據
memcpy(RecvBuf, (char*)(m_lpbReceiveBuf),(int)lParam);
……
// 程序退出前資源的釋放
UnmapViewOfFile(m_lpbReceiveBuf);
m_lpbReceiveBuf = NULL;
CloseHandle(m_hReceiveMap);
m_hReceiveMap = NULL;
聯(lián)系客服