近期開發中使用 #pragma data_seg 實作跨程序共享變數,發現未初始化的共享變數在 DLL 注入其他程序時失效。經驗證總結鐵律:全域共享變數一定要在定義時初始化,否則無效。本文提供完整可編譯驗證程式碼。
main.c:EXE 入口,建立對話框,載入 DLL 並安裝 Hook。dll.c:DLL 實作,定義共享資料段,安裝 WH_CALLWNDPROC Hook 攔截目標視窗訊息。res.rc / resource.h:對話框資源。#include
#include
// 定義共享資料段
#pragma data_seg(".Shared")
static int haveinit = 0; // 【有初始化】
static int noinit; // 【無初始化】
#pragma data_seg()
#pragma comment(linker, "/section:.Shared,rws")
HINSTANCE g_hinstDll = NULL;
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
g_hinstDll = (HINSTANCE)hModule;
return TRUE;
}
LRESULT WINAPI TestProc(int code, WPARAM wParam, LPARAM lParam)
{
if (code >= 0) {
CWPSTRUCT* msg = (CWPSTRUCT*)lParam;
if (msg->message == WM_NULL) {
char Text[50];
// 賦值測試
haveinit = 100;
noinit = 100;
sprintf(Text, "haveinit=%d noinit=%d", haveinit, noinit);
MessageBox((HWND)msg->wParam, Text, "賦值情況", MB_OK);
}
}
return CallNextHookEx(NULL, code, wParam, lParam);
}
__declspec(dllexport) void Test(HWND hWnd)
{
char Text[50];
HWND hTest = FindWindow("#32770", "");
DWORD dwThreadId = GetWindowThreadProcessId(hTest, NULL);
HANDLE hLib = GetModuleHandle("dll.dll");
HHOOK hook = SetWindowsHookEx(WH_CALLWNDPROC, TestProc, hLib, dwThreadId);
if (hook) {
SendMessage(hTest, WM_NULL, (WPARAM)hWnd, 0); // 觸發 Hook
UnhookWindowsHookEx(hook);
sprintf(Text, "haveinit=%d noinit=%d", haveinit, noinit);
MessageBox(hWnd, Text, "輸出結果", MB_OK);
}
}
haveinit 正確顯示 100。noinit 顯示 隨機亂碼(未初始化導致各程序各自分配記憶體,未真正共享)。haveinit 仍為 100,noinit 依然錯誤。Windows 載入 DLL 時,已初始化的資料會被映射到共享記憶體區段(PAGE_READWRITE | SEC_COMMIT),所有程序看到同一份實體記憶體。未初始化資料 (BSS) 則由載入器在各程序私有位址空間個別配置零頁,不會映射到共享區段,導致各程序各自擁有獨立副本,失去共享效果。
// 正確寫法
#pragma data_seg(".Shared")
static int g_nSharedCounter = 0; // 明確初始化
static BOOL g_bFlag = FALSE; // 明確初始化
static MY_STRUCT g_SharedData = {0}; // 結構體零初始化
#pragma data_seg()
#pragma comment(linker, "/section:.Shared,rws")
CString, std::string)作為共享變數,建構/解構行為在跨程序環境未定義。Mutex、Critical Section 或 Interlocked API,Sleep(10) 輪詢不可靠。#pragma data_seg 是實現進程間共享記憶體的經典技巧,但初始化不是選項,是必要條件。希望這篇血淚經驗能幫你避開同樣的坑。