首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

VC++ 全域共享變數陷阱:#pragma data_seg 必須初始化才有效

     編輯:VC 教學

前言:被共享變數坑慘的經驗

近期開發中使用 #pragma data_seg 實作跨程序共享變數,發現未初始化的共享變數在 DLL 注入其他程序時失效。經驗證總結鐵律:全域共享變數一定要在定義時初始化,否則無效。本文提供完整可編譯驗證程式碼。

驗證程式碼架構

  • main.c:EXE 入口,建立對話框,載入 DLL 並安裝 Hook。
  • dll.c:DLL 實作,定義共享資料段,安裝 WH_CALLWNDPROC Hook 攔截目標視窗訊息。
  • res.rc / resource.h:對話框資源。

核心 DLL 程式碼 (dll.c)

#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);
    }
}

執行結果說明

  1. Hook 觸發時,haveinit 正確顯示 100。
  2. noinit 顯示 隨機亂碼(未初始化導致各程序各自分配記憶體,未真正共享)。
  3. Hook 卸載後 EXE 讀取共享變數,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")
  • 所有共享變數必須顯式初始化(哪怕是 0 / FALSE / NULL)。
  • 避免使用非 POD 類型(如 CString, std::string)作為共享變數,建構/解構行為在跨程序環境未定義。
  • 同步存取請使用 Mutex、Critical Section 或 Interlocked API,Sleep(10) 輪詢不可靠。

結語

#pragma data_seg 是實現進程間共享記憶體的經典技巧,但初始化不是選項,是必要條件。希望這篇血淚經驗能幫你避開同樣的坑。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved