Windows 進程間通訊 (IPC) 方式眾多:管道、訊息佇列、RPC、Socket、郵件槽、共享記憶體。其中 **共享記憶體 (Memory Mapped File / File Mapping)** 具備:
Mutex/Event/Semaphore 實現生產者-消費者模型。CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size, name)INVALID_HANDLE_VALUE 表示不關聯實體檔案,純記憶體共享。name 為具名物件,跨進程同名即共享同一區塊。HANDLE,GetLastError()==ERROR_ALREADY_EXISTS 表示非首次建立。MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0)LPVOID),各進程位址通常不同,但指向同一實體頁框。CreateMutex (跨進程) 保護關鍵區。UnmapViewOfFile(ptr) → CloseHandle(hMap)。最後一個進程關閉時釋放實體記憶體。
// 共用標頭檔
struct TShareHandle { HANDLE hMap; void* pView; };
// 取得共享記憶體指標 (首次建立者自動清零)
TShareHandle GetShare(const char* name, DWORD size) {
TShareHandle sh = {0,0};
sh.hMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size, name);
if (!sh.hMap) return sh;
sh.pView = MapViewOfFile(sh.hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (sh.pView && GetLastError() != ERROR_ALREADY_EXISTS)
ZeroMemory(sh.pView, size); // 僅建立者初始化
return sh;
}
void ReleaseShare(TShareHandle& sh) {
if (sh.pView) UnmapViewOfFile(sh.pView);
if (sh.hMap) CloseHandle(sh.hMap);
sh.hMap = 0; sh.pView = 0;
}
#pragma once
#define SHARED_MEM_NAME "Global\\MyIpcSharedMem_2024"
#define MUTEX_NAME "Global\\MyIpcMutex_2024"
#define MAX_MSG_LEN 1024
struct TSharedData {
volatile LONG writerPid; // 寫入者 PID
volatile LONG sequence; // 遞增序號
char message[MAX_MSG_LEN]; // 承載資料
// 可擴充:時間戳、命令碼、狀態旗標...
};
#include
#include
#include "SharedDef.h"
int main() {
TShareHandle sh = GetShare(SHARED_MEM_NAME, sizeof(TSharedData));
if (!sh.pView) { puts("建立共享記憶體失敗"); return 1; }
HANDLE hMutex = CreateMutex(NULL, FALSE, MUTEX_NAME);
if (!hMutex) { ReleaseShare(sh); return 1; }
TSharedData* pData = (TSharedData*)sh.pView;
pData->writerPid = GetCurrentProcessId();
for (int i = 1; i <= 10; ++i) {
WaitForSingleObject(hMutex, INFINITE);
pData->sequence = i;
sprintf_s(pData->message, "Message #%d from Writer PID %lu", i, pData->writerPid);
ReleaseMutex(hMutex);
printf("寫入: %s\n", pData->message);
Sleep(1000);
}
ReleaseMutex(hMutex);
CloseHandle(hMutex);
ReleaseShare(sh);
return 0;
}
#include
#include
#include "SharedDef.h"
int main() {
TShareHandle sh = GetShare(SHARED_MEM_NAME, sizeof(TSharedData));
if (!sh.pView) { puts("開啟共享記憶體失敗 (Writer 未啟動?)"); return 1; }
HANDLE hMutex = OpenMutex(SYNCHRONIZE, FALSE, MUTEX_NAME);
if (!hMutex) { ReleaseShare(sh); return 1; }
TSharedData* pData = (TSharedData*)sh.pView;
LONG lastSeq = 0;
while (true) {
WaitForSingleObject(hMutex, INFINITE);
LONG seq = pData->sequence;
if (seq != lastSeq) {
lastSeq = seq;
printf("讀取: Seq=%ld, PID=%ld, Msg=%s\n", seq, pData->writerPid, pData->message);
if (seq >= 10) break; // 示範結束條件
}
ReleaseMutex(hMutex);
Sleep(200); // 輪詢間隔
}
CloseHandle(hMutex);
ReleaseShare(sh);
return 0;
}
適用於單一生產者、單一消費者 (SPSC) 無鎖 (需記憶體屏障) 或輕量鎖場景:
struct TRingBuffer {
volatile LONG head; // 寫入位置
volatile LONG tail; // 讀取位置
volatile LONG mask; // size-1 (size 必須為 2 的冪次)
char data[1]; // 彈性陣列,實際配置 sizeof(TRingBuffer) + size
};
// 寫入 (生產者)
bool RingPush(TRingBuffer* rb, const void* src, DWORD len) {
LONG h = rb->head, t = rb->tail, m = rb->mask;
if ((((h + 1) & m) == t)) return false; // 滿
memcpy(&rb->data[h], src, len);
// MemoryBarrier(); // 確保寫入可見
rb->head = (h + 1) & m;
return true;
}
// 讀取 (消費者)
bool RingPop(TRingBuffer* rb, void* dst, DWORD len) {
LONG h = rb->head, t = rb->tail, m = rb->mask;
if (h == t) return false; // 空
memcpy(dst, &rb->data[t], len);
// MemoryBarrier();
rb->tail = (t + 1) & m;
return true;
}
配合 CreateEvent (資料就緒/空間可用) 實現阻塞式等待,效能遠超管道/訊息佇列。
| 坑點 | 後果 | 解決方案 |
|---|---|---|
| 忘記同步 (Mutex/Event) | 資料撕裂、讀到半更新結構、競態條件 | **永遠**搭配跨進程 Mutex 保護複雜結構讀寫 |
| 名稱衝突/權限不足 | CreateFileMapping 失敗 (ERROR_ACCESS_DENIED) | 使用 Global\\ 前綴需管理員/SeCreateGlobalPrivilege;一般用戶用 Local\\ 或無前綴 (Session 範圍) |
| 大小不一致 | 映射視圖大小與結構不符,導致越界 | 雙方包含同一標頭檔定義 sizeof(TSharedData),統一大小 |
| 指標跨進程傳遞 | 指標僅在當前進程有效,其他進程解引用崩潰 | **嚴禁**在共享區儲存指標,只存 offset/index/handle/純資料 |
| 未釋放資源 | 共享記憶體洩漏,重啟前無法重建同名物件 | RAII 封裝、atexit、進程結束自動清理 (但最好顯式釋放) |
| 64 位元指標截斷 | 32/64 位元混合部署時結構對齊/大小不同 | 避免在共享結構使用指標;固定大小類型 (int32_t, int64_t);#pragma pack 對齊 |
CreateFileMapping 第二參數傳入 SECURITY_ATTRIBUTES 指定 LPSECURITY_DESCRIPTOR,限制特定用戶/群組存取,防止惡意進程竄改/讀取敏感資料。Global\\ 物件。跨完整性等級通訊需明確設定 SACL 或使用 AppContainer 允許的命名空間。PostMessage (需視窗)、WM_COPYDATA、RegisterWindowMessage。CreateNamedPipe)、本地 TCP/Unix Domain Socket (AF_UNIX Win10+)。RPC/COM、WinRT 元件。CreateFileMapping 關聯實體檔案)、資料庫 (SQLite 共享 WAL 模式)。共享記憶體是 Windows 高效能 IPC 的基石。掌握 CreateFileMapping/MapViewOfFile 核心流程、嚴謹套用跨進程同步原語、妥善處理命名空間與權限,即可構建極低延遲、高吞吐的進程間資料交換通道。本文提供的封裝函數與雙進程範例可直接整合至 C++ Builder、Visual C++、Delphi、C# (P/Invoke) 等專案中。