详细介绍GlobalLock()和GlobalUnlock()

书籍:《Visual C++ 2017从入门到精通》的4.2 按钮控件

环境:visual studio 2022

内容:[例 4.2]制作图片按钮

说明:以上内容大部分来自腾讯元宝。

函数原型与功能定位​

​GlobalLock()​
  • ​核心作用​​:锁定全局内存块,获取可访问的指针
  • ​参数​​:hMem 由 GlobalAlloc 或 GlobalReAlloc 返回
  • ​返回值​​:成功返回内存指针,失败返回 NULL
  • ​所属头文件​​:winbase.h
​GlobalUnlock()​
BOOL GlobalUnlock(
  HGLOBAL hMem  // 全局内存句柄
);
  • ​核心作用​​:解除内存锁定,使指针失效
  • ​参数​​:hMem 需与 GlobalLock 使用的句柄一致
  • ​返回值​​:非零表示成功,零表示失败(通过 GetLastError 获取错误码)
  • ​所属头文件​​:winbase.h

​锁定机制解析​

1. ​​锁定计数器​
  • ​内部机制​​:每个内存对象维护一个锁定计数器(初始为 0)
    • GlobalLock 每次调用计数器 +1
    • GlobalUnlock 每次调用计数器 -1
  • ​锁定条件​​:只有计数器 > 0 时内存保持锁定状态
2. ​​内存移动控制​
  • ​可移动内存(GMEM_MOVEABLE)​​:
    • 未锁定时,系统可合并/移动内存块优化空间
    • 锁定后内存地址固定,直到计数器归零
  • ​固定内存(GMEM_FIXED)​​:
    • 锁定计数器始终为 0
    • 返回的句柄即内存地址(无需锁定/解锁)
3. ​​多线程安全性​
  • ​线程间竞争​​:不同线程对同一内存块的锁定操作需通过计数器同步
  • ​死锁风险​​:需确保 GlobalLock 和 GlobalUnlock 成对出现

​典型应用场景​

1. ​​跨进程数据交换​
// 剪贴板操作示例
HGLOBAL hClipData = GlobalAlloc(GMEM_MOVEABLE, dataSize);
LPVOID pData = GlobalLock(hClipData);  // 锁定获取指针
CopyMemory(pData, sourceData, dataSize);
SetClipboardData(CF_BITMAP, hClipData);
GlobalUnlock(hClipData);  // 解锁后剪贴板可被其他进程访问
2. ​​动态数据流处理​
// 实现 IStream 接口的读写
STDMETHODIMP CMyStream::Read(void* pv, ULONG cb, ULONG* pcbRead) {
    HGLOBAL hMem = GlobalLock(hGlobal);
    memcpy(pv, (BYTE*)hMem + currentPosition, cb);
    *pcbRead = cb;
    GlobalUnlock(hMem);
    currentPosition += cb;
    return S_OK;
}
3. ​​COM 对象内存管理​
// 通过全局内存共享数据
HGLOBAL hSharedData = GlobalAlloc(GMEM_MOVEABLE, sizeof(DataStruct));
DataStruct* pData = (DataStruct*)GlobalLock(hSharedData);
pData->version = 2;
GlobalUnlock(hSharedData);

​错误处理与调试​

1. ​​常见错误码​
​错误码​​含义​
NULL(GlobalLock)句柄无效或内存已释放
ERROR_NOT_LOCKED尝试解锁未锁定的内存
ERROR_INVALID_HANDLE无效的内存句柄
2. ​​调试技巧​
  • ​锁定计数验证​​:
    LONG lockCount = 0;
    do {
        lockCount = GlobalHandle(hMem)->dwLockCount;
        Sleep(10);
    } while (lockCount > 0);  // 检测锁定泄漏
  • ​内存转储分析​​:
    // 使用 WinDbg 查看锁定状态
    !heap -p -a <hMem地址>

​性能优化建议​

  1. ​最小化锁定时间​​:
  2. ​批量数据处理​​:
    HGLOBAL hBuffer = GlobalAlloc(GMEM_MOVEABLE, 1024 * 1024);
    LPVOID pBuffer = GlobalLock(hBuffer);
    // 一次性处理大块数据
    ProcessData(pBuffer, 1024 * 1024);
    GlobalUnlock(hBuffer);

​特殊场景注意事项​

1. ​​内存映射文件​
// 将文件映射到全局内存
HANDLE hFile = CreateFile("data.bin", GENERIC_READ, ...);
HGLOBAL hMap = GlobalAlloc(GMEM_MOVEABLE, fileSize);
LPVOID pMap = GlobalLock(hMap);
ReadFile(hFile, pMap, fileSize, &bytesRead, NULL);
GlobalUnlock(hMap);
2. ​​多语言支持​
// 锁定包含多语言字符串的资源
HRSRC hRes = FindResource(NULL, MAKEINTRESOURCE(IDR_STRINGS), RT_STRING);
HGLOBAL hString = LoadResource(NULL, hRes);
LPVOID pString = GlobalLock(hString);
// 根据语言选择字符串偏移量
CString str = *(LPCTSTR)((BYTE*)pString + 0x100);
GlobalUnlock(hString);

​与 GlobalAlloc() 的协作关系​

​分配标志​​锁定要求​​典型场景​
GMEM_FIXED无需锁定,直接使用句柄作为指针长期驻留内存的数据结构
GMEM_MOVEABLE必须锁定后才能访问临时缓冲区、跨进程数据交换
GMEM_DISCARDABLE锁定时禁止系统丢弃内存可恢复的缓存数据

​源码实现原理​

(基于 Windows 内核实现)

// ntddk.h
typedef struct _HEAP_LOCK_ENTRY {
    ULONG LockCount;  // 锁定计数器
    PVOID pAddress;   // 内存起始地址
} HEAP_LOCK_ENTRY;

// win32k.sys 内部实现
LPVOID APIENTRY GlobalLock(HGLOBAL hMem) {
    HEAP_LOCK_ENTRY* pEntry = GetLockEntry(hMem);
    if (InterlockedIncrement(&pEntry->LockCount) == 1) {
        return pEntry->pAddress;
    }
    return NULL;  // 锁定失败
}

BOOL APIENTRY GlobalUnlock(HGLOBAL hMem) {
    HEAP_LOCK_ENTRY* pEntry = GetLockEntry(hMem);
    if (InterlockedDecrement(&pEntry->LockCount) >= 0) {
        return TRUE;
    }
    return FALSE;  // 计数器溢出
}

​总结​

GlobalLock() 和 GlobalUnlock() 是 Windows 内存管理的核心接口,其设计体现了以下特性:

  1. ​安全性​​:通过锁定计数器防止内存竞争
  2. ​灵活性​​:支持可移动内存的动态管理
  3. ​兼容性​​:为旧系统 API 提供基础支持

​最佳实践​​:

  • 优先使用 GMEM_FIXED 减少锁定开销
  • 锁定后确保在异常处理中解锁
  • 大内存操作时结合 VirtualLock 提升性能

通过合理运用这两个函数,可在跨进程通信、动态数据流处理等场景中实现高效安全的内存管理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值