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