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

全域共享變數在 DLL 中的初始化陷阱與解決方案

     編輯:VC 教學

問題背景

在 Win32 DLL 專案中使用全域共享變數時,若 **未在定義處顯式初始化**,變數在進程附加時可能為 0 或隨機值,導致跨模組資料共享失效。本文以 Visual Studio .NET 2003 + Windows Server 2003 實測驗證此行為。

核心結論

全域共享變數必須在定義時初始化,否則無效。

驗證程式碼

DLL 端 (main.c)

// main.c
#include <windows.h>
#include "resource.h"

__declspec(dllexport) void Test(HWND hWnd);

// 共享資料段:命名為 .SHARED,屬性 READ | WRITE | SHARED
#pragma data_seg(".SHARED")
HWND g_hSharedWnd = NULL;   // ✅ 定義時初始化為 NULL
// HWND g_hSharedWnd;       // ❌ 未初始化:在新進程附加時不會自動歸零
#pragma data_seg()
#pragma comment(linker, "/SECTION:.SHARED,RWS")

// DLL 進入點
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    return TRUE;
}

__declspec(dllexport) void Test(HWND hWnd)
{
    g_hSharedWnd = hWnd; // 存入共享視窗句柄
}

應用程式端 (UseDlg.cpp)

// 主對話框按鈕事件
void CUseDlg::OnButton1()
{
    Test(GetSafeHwnd()); // 呼叫 DLL 匯出函數,傳入自身視窗句柄
}

實測現象對比

定義方式進程 A 呼叫 Test()進程 B 讀取 g_hSharedWnd結果
HWND g_hSharedWnd = NULL; (初始化)設定為 0x00120345讀到 0x00120345✅ 共享成功
HWND g_hSharedWnd; (未初始化)設定為 0x00120345讀到 0x00000000❌ 共享失敗

根本原因分析

  • Windows 載入 DLL 時,對 **已初始化** 的共享資料段,只在 **第一個進程** 分配實體記憶體並套用初值;後續進程映射同一塊實體頁面。
  • 對 **未初始化** 的資料段 (BSS 段),各進程映射各自的 **零頁** (Copy-on-Write 機制),互不干擾,自然無法共享。
  • 連結器指令 /SECTION:.SHARED,RWS 僅設定區段屬性,**不改變初始化語意**。

最佳實踐建議

  1. 共享變數 **務必在定義處顯式初始化** (哪怕是 = 0 或 = NULL)
  2. 使用 #pragma data_seg(".SHARED") 明確指定共享段名稱
  3. 搭配 #pragma comment(linker, "/SECTION:.SHARED,RWS") 設定讀寫共享屬性
  4. 避免在共享段放置非 POD 型別 (如 C++ 物件、STL 容器),建構/解構順序不可控
  5. 考慮使用 CreateFileMapping + MapViewOfFile 替代,語意更清晰、跨語言相容性更好

延伸:共享資料段的大小限制

共享段總大小受限於 **分頁檔案大小** 及 **系統分頁大小 (通常 4KB)**。僅建議存放少量純量資料 (句柄、計數器、旗標),勿存大型緩衝區。

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