You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

64位编译时winuser.h中CopyAcceleratorTable函数异常问题

64位下CopyAcceleratorTable复制失败的原因及解决方法

你遇到的问题核心是64位系统下CopyAcceleratorTable的内存对齐要求,具体分析和解决方案如下:

原因分析

  • ACCEL结构体实际大小为6字节:由于结构体成员对齐规则,BYTE fVirt字段后会被填充1字节以满足WORD类型的2字节对齐要求,最终结构体总大小为2+2+2=6字节。
  • 64位Windows系统中,CopyAcceleratorTable要求目标缓冲区的起始地址必须是8字节对齐的;而32位系统下函数仅要求4字节对齐,对非对齐地址的兼容性更强。
  • 当第一个加速器表的元素个数为奇数(如13)时,第一个表占用的内存为13*6=78字节。new分配的数组起始地址是8字节对齐的,但&lpAccel[13]的地址为起始地址+78,78对8取余为6,导致该地址未满足8字节对齐要求,因此CopyAcceleratorTable无法正确复制第二个表的数据。

解决方法

方法1:为每个表单独分配对齐内存,再合并

利用_aligned_malloc为每个加速器表分配8字节对齐的临时缓冲区,复制完成后用memcpy合并到最终的连续数组中(memcpy不严格要求内存对齐):

// Load accelerator tables
HACCEL hAccelTable1 = LoadAcceleratorsFromDll1();
HACCEL hAccelTable2 = LoadAcceleratorsFromDll2();

// Determine lengths of accelerator tables
int nAccelSize1 = ::CopyAcceleratorTable(hAccelTable1, NULL, 0);
int nAccelSize2 = ::CopyAcceleratorTable(hAccelTable2, NULL, 0);

// 为第一个表分配8字节对齐的内存
LPACCEL lpTemp1 = static_cast<LPACCEL>(_aligned_malloc(nAccelSize1 * sizeof(ACCEL), 8));
::CopyAcceleratorTable(hAccelTable1, lpTemp1, nAccelSize1);

// 为第二个表分配8字节对齐的内存
LPACCEL lpTemp2 = static_cast<LPACCEL>(_aligned_malloc(nAccelSize2 * sizeof(ACCEL), 8));
::CopyAcceleratorTable(hAccelTable2, lpTemp2, nAccelSize2);

// 分配最终的连续数组(64位下new默认8字节对齐)
LPACCEL lpAccelFinal = new ACCEL[nAccelSize1 + nAccelSize2];
// 合并两个表的数据
memcpy(lpAccelFinal, lpTemp1, nAccelSize1 * sizeof(ACCEL));
memcpy(lpAccelFinal + nAccelSize1, lpTemp2, nAccelSize2 * sizeof(ACCEL));

// Create new accelerator table
HACCEL hAccelTableNew = ::CreateAcceleratorTable(lpAccelFinal, nAccelSize1 + nAccelSize2);
if (hAccelTableNew != NULL)
{
     pclApp->GetKeyboardManager()->UpdateAccelTable(NULL, hAccelTableNew);
}

// 释放临时内存
_aligned_free(lpTemp1);
_aligned_free(lpTemp2);
delete[] lpAccelFinal;

方法2:直接使用对齐内存分配最终缓冲区

如果希望减少内存分配次数,可以直接分配一块足够大的8字节对齐内存,先复制第一个表,再计算第二个表的对齐起始地址后复制:

// Load accelerator tables
HACCEL hAccelTable1 = LoadAcceleratorsFromDll1();
HACCEL hAccelTable2 = LoadAcceleratorsFromDll2();

// Determine lengths of accelerator tables
int nAccelSize1 = ::CopyAcceleratorTable(hAccelTable1, NULL, 0);
int nAccelSize2 = ::CopyAcceleratorTable(hAccelTable2, NULL, 0);

// 计算所需总内存:预留最多7字节的对齐填充空间
size_t totalBytes = (nAccelSize1 + nAccelSize2) * sizeof(ACCEL) + 7;
// 分配8字节对齐的内存
LPACCEL lpAccel = static_cast<LPACCEL>(_aligned_malloc(totalBytes, 8));

// 复制第一个表
::CopyAcceleratorTable(hAccelTable1, lpAccel, nAccelSize1);

// 计算第二个表的对齐起始地址
uintptr_t firstEndAddr = reinterpret_cast<uintptr_t>(lpAccel + nAccelSize1);
uintptr_t secondStartAddr = firstEndAddr + (8 - (firstEndAddr % 8)) % 8;
LPACCEL lpAccel2 = reinterpret_cast<LPACCEL>(secondStartAddr);

// 复制第二个表
::CopyAcceleratorTable(hAccelTable2, lpAccel2, nAccelSize2);

// 创建新加速器表时,需要将两个表的数据复制到连续的有效数组中(避免中间填充被误判为元素)
LPACCEL lpFinal = new ACCEL[nAccelSize1 + nAccelSize2];
memcpy(lpFinal, lpAccel, nAccelSize1 * sizeof(ACCEL));
memcpy(lpFinal + nAccelSize1, lpAccel2, nAccelSize2 * sizeof(ACCEL));

HACCEL hAccelTableNew = ::CreateAcceleratorTable(lpFinal, nAccelSize1 + nAccelSize2);
if (hAccelTableNew != NULL)
{
     pclApp->GetKeyboardManager()->UpdateAccelTable(NULL, hAccelTableNew);
}

// 释放内存
_aligned_free(lpAccel);
delete[] lpFinal;

注意事项

  • 64位系统下new默认分配8字节对齐内存,但数组元素的连续地址可能因结构体大小导致后续起始地址非对齐。
  • 使用_aligned_malloc分配的内存必须用_aligned_free释放,不能混用delete或free。

内容的提问来源于stack exchange,提问作者pisoir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 06:52:32