設計目標
簡化 Windows 共享記憶體(Memory Mapped File)的使用,提供 Server/Client 雙模式 封裝,適合「懶人程式設計師」快速整合跨進程通訊功能。
核心類別架構
| 類別 | 角色 | 關鍵方法 |
CSFMServer | 建立/擁有共享記憶體 | Create(), GetBuffer(), GetSize() |
CSFMClient | 開啟/存取既有共享記憶體 | Open(), GetBuffer(), GetSize() |
使用流程
Server 端:建立共享記憶體
// 建立名為 "_ZZZ_OBJ_"、大小 1 byte 的共享記憶體
CSFMServer m_SFMS;
m_SFMS.Create(NULL, "_ZZZ_OBJ_", 1); // NULL = 系統分頁檔,不建立實體檔
// 寫入資料
char* p = (char*)m_SFMS.GetBuffer();
if (p) strcpy(p, "1234567890");
Client 端:開啟共享記憶體
// 以讀寫權限開啟同名共享記憶體
CSFMClient* pCSFMC = new CSFMClient(FILE_MAP_READ | FILE_MAP_WRITE, "_ZZZ_OBJ_");
// 讀取/寫入資料
char* p = (char*)pCSFMC->GetBuffer();
if (p) strcpy(p, "1234567890");
關鍵實作細節 (SFMMem.h/.cpp)
複製策略模板
// VARIANT 從介面指標複製
template <typename T>
struct _CopyVariantFromAdaptItf {
static HRESULT copy(VARIANT* p1, const CAdapt<CComPtr<T>>& p2) {
HRESULT hr = p2.m_T->QueryInterface(IID_IDispatch, (void**)&p1->pdispVal);
if (SUCCEEDED(hr)) { p1->vt = VT_DISPATCH; }
else {
hr = p2.m_T->QueryInterface(IID_IUnknown, (void**)&p1->punkVal);
if (SUCCEEDED(hr)) p1->vt = VT_UNKNOWN;
}
return hr;
}
static void init(VARIANT* p) { VariantInit(p); }
static void destroy(VARIANT* p) { VariantClear(p); }
};
// 介面指標複製 (AddRef/Release)
template <typename T>
struct _CopyItfFromAdaptItf {
static HRESULT copy(T** p1, const CAdapt<CComPtr<T>>& p2) {
if (*p1 = p2.m_T) return (*p1)->AddRef(), S_OK;
return E_POINTER;
}
static void init(T** p) {}
static void destroy(T** p) { if (*p) (*p)->Release(); }
};
CSFMServer 核心實作
void CSFMServer::Create(char* szFileName, char* szMapName, DWORD dwSize) {
if (m_iCreateFlag) _Destory();
m_pMapName = szMapName ? _strdup(szMapName) : _strdup(DEFAULT_MAPNAME);
m_dwSize = (dwSize > 0) ? dwSize : DEFAULT_MAPSIZE;
// 建立/開啟檔案映射物件
if (szFileName) {
m_hFile = CreateFile(m_pFileName, GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
} else {
m_hFile = (HANDLE)0xFFFFFFFF; // 使用系統分頁檔
}
m_hFileMap = CreateFileMapping(m_hFile, NULL, PAGE_READWRITE,
0, m_dwSize, m_pMapName);
m_lpFileMapBuffer = MapViewOfFile(m_hFileMap, FILE_MAP_ALL_ACCESS,
0, 0, m_dwSize);
m_iCreateFlag = 1;
}
CSFMClient 核心實作
void CSFMClient::Open(DWORD dwAccess, char* szMapName) {
if (m_iOpenFlag) _Destory();
m_pMapName = szMapName ? _strdup(szMapName) : _strdup(DEFAULT_MAPNAME);
m_hFileMap = OpenFileMapping(dwAccess, TRUE, m_pMapName);
if (m_hFileMap) {
m_lpFileMapBuffer = MapViewOfFile(m_hFileMap, dwAccess, 0, 0, 0);
}
m_iOpenFlag = 1;
}
資源清理 (RAII 模式)
void CSFMServer::_Destory() {
if (m_lpFileMapBuffer) { UnmapViewOfFile(m_lpFileMapBuffer); m_lpFileMapBuffer = NULL; }
if (m_hFileMap) { CloseHandle(m_hFileMap); m_hFileMap = NULL; }
if (m_hFile && m_hFile != INVALID_HANDLE_VALUE) { CloseHandle(m_hFile); m_hFile = NULL; }
if (m_pFileName) { free(m_pFileName); m_pFileName = NULL; }
if (m_pMapName) { free(m_pMapName); m_pMapName = NULL; }
_Init();
}
測試環境與適用性
- 測試環境:Win2K + VC6 + 一般應用程式
- 服務程式/ISAPI 等其他應用類型理論上也適用
- 預設映射大小:64KB (0xFFFF + 1)
優缺點與改進建議
| 優點 | 缺點/限制 | 改進方向 |
| 介面極簡單,兩行程式碼完成跨進程共享 | 無同步機制,多進程並發需自行加鎖 | 內建 Critical Section / Mutex 同步 |
| 支援檔案映射與系統分頁檔兩種模式 | Client 端 GetSize() 回傳 0,無法取得大小 | 共享記憶體頭部存放大小資訊 |
| RAII 自動資源管理,防止洩漏 | 缺乏變更通知機制 | 加入事件通知 (CreateEvent + WaitForSingleObject) |
此封裝類大幅降低共享記憶體使用門檻,是進程間通訊 (IPC) 的輕量級選擇。
- 上一篇文章: 二進位檔案隨機存取兩大法:固定記錄號定位 vs 記憶體鏈表重組
- 下一篇文章: VC6 使用 MSComm 控制項實現序列埠通訊:從元件註冊到收發實作
Copyright ©
程式碼之家 All Rights Reserved