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

VC++ 全域共享變數陷阱:DLL 資料段共享必須初始化

     編輯:VC++

前言

使用 #pragma data_seg 實作 DLL 跨行程共享變數時,發現未初始化的變數無法正確共享。本文以實測程式碼驗證此行為,避免開發者踩坑。

核心結論

共享資料段中的變數必須在定義時初始化,否則各行程看到的將是各自私有的複本,而非真正共享的單一實體。

驗證程式碼

主程式 (main.cpp)

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

DLL (dll.cpp) — 關鍵差異在此

#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 / res.rc)

// 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

執行結果預期

  1. 點擊「測試共享」按鈕
  2. Hook 內彈出:init=200, uninit=200 (兩者皆被修改)
  3. 主程式讀取彈出: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
  • 此行為在 Visual Studio .NET 2003 + Windows Server 2003 驗證,其他版本同理
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved