使用 #pragma data_seg 實作 DLL 跨行程共享變數時,發現未初始化的變數無法正確共享。本文以實測程式碼驗證此行為,避免開發者踩坑。
共享資料段中的變數必須在定義時初始化,否則各行程看到的將是各自私有的複本,而非真正共享的單一實體。
#include <windows.h>
#include "resource.h"
__declspec(dllimport) void Test(HWND hWnd);
LRESULT CALLBACK MainProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_COMMAND:
if (LOWORD(wParam) == IDOK) { Test(hWnd); return 1; }
else if (LOWORD(wParam) == IDCANCEL) { EndDialog(hWnd, 0); return 1; }
}
return 0;
}
int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE, LPSTR, int nCmdShow)
{
DialogBox(hInst, MAKEINTRESOURCE(ID_DLG), NULL, MainProc);
return 0;
}
#include <windows.h>
#include <stdio.h>
#pragma data_seg(".Shared")
static int g_nInitialized = 100; // 有初始化 — 真正共享
static int g_nUninitialized; // 無初始化 — 各行程私有複本!
#pragma data_seg()
#pragma comment(linker, "/section:.Shared,rws") // 讀/寫/共享
__declspec(dllexport) LRESULT CALLBACK HookProc(int code, WPARAM wParam, LPARAM lParam)
{
if (code >= 0)
{
CWPSTRUCT* pMsg = (CWPSTRUCT*)lParam;
if (pMsg->message == WM_NULL)
{
g_nInitialized = 200;
g_nUninitialized = 200;
char buf[64];
sprintf(buf, "Hook 內: init=%d, uninit=%d", g_nInitialized, g_nUninitialized);
MessageBox((HWND)pMsg->wParam, buf, "Hook 內賦值", MB_OK);
}
}
return CallNextHookEx(NULL, code, wParam, lParam);
}
__declspec(dllexport) void Test(HWND hWnd)
{
HWND hTarget = FindWindow("#32770", "");
DWORD dwThreadId = GetWindowThreadProcessId(hTarget, NULL);
HMODULE hDll = GetModuleHandle("TestDll.dll");
HHOOK hHook = SetWindowsHookEx(WH_CALLWNDPROC, HookProc, hDll, dwThreadId);
if (hHook)
{
SendMessage(hTarget, WM_NULL, (WPARAM)hWnd, 0); // 觸發 Hook
UnhookWindowsHookEx(hHook);
char buf[64];
sprintf(buf, "主程式讀取: init=%d, uninit=%d", g_nInitialized, g_nUninitialized);
MessageBox(hWnd, buf, "主程式讀取結果", MB_OK);
}
}
// resource.h
#define ID_DLG 101
// res.rc
ID_DLG DIALOGEX 0, 0, 186, 90
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Shared Var Test"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "測試共享", IDOK, 26, 25, 50, 14
PUSHBUTTON "關閉", IDCANCEL, 99, 26, 50, 14
END
init=200, uninit=200 (兩者皆被修改)init=200, uninit=100 (或 0/亂碼)解釋:g_nInitialized 定義時賦值 100,編譯器將其置於共享資料段,所有行程映射同一實體記憶體。g_nUninitialized 無初值,編譯器置於 BSS 段,#pragma data_seg 不影響,各行程載入 DLL 時各自獲得私有複本。
#pragma data_seg(".Shared")
static int g_nSharedVar = 0; // 必須显式初始化,哪怕是 0
#pragma data_seg()
/section:.Shared,rws 必須與 #pragma comment(linker,...) 或專案設定一致CMutex 或 Interlocked* API