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
相关产品推荐
相关产品推荐

