已获取对话框尺寸,如何获取资源文件中其按钮控件的尺寸与位置?
如何从资源文件中获取对话框控件的原始尺寸与位置
当然可以通过编程获取资源文件里定义的按钮(或其他控件)的原始位置和尺寸!这里有两种可靠的方法,根据你的场景选择:
方法一:直接解析对话框模板资源(推荐,获取原始定义值)
如果想拿到和.rc文件里完全一致的数值(比如你示例里的30,46,42,18),最准确的方式是直接加载并解析对话框的资源模板。Windows的对话框资源存储为标准或扩展模板结构,我们可以遍历其中的控件条目提取信息。
具体步骤与代码示例
- 加载对话框资源并锁定内存
- 判断模板类型(标准/扩展)
- 遍历控件模板,提取坐标、尺寸和控件ID
#include <windows.h> #include <afxwin.h> // 若用MFC,非MFC环境替换为GetModuleHandle(NULL) // 辅助函数:跳过对话框模板中的Unicode字符串并对齐到DWORD边界 LPBYTE SkipDialogStrings(LPBYTE pData) { // 跳过以0结尾的Unicode字符串 while (*(WORD*)pData != 0) { pData += sizeof(WCHAR); } pData += sizeof(WCHAR); // 跳过结尾的空字符 // 对齐到DWORD(4字节)边界 DWORD offset = ((DWORD)pData) % sizeof(DWORD); if (offset != 0) { pData += (sizeof(DWORD) - offset); } return pData; } void GetControlResourceRect(UINT nDlgID) { HINSTANCE hInst = AfxGetInstanceHandle(); // 非MFC环境用GetModuleHandle(NULL) // 查找对话框资源 HRSRC hRes = FindResource(hInst, MAKEINTRESOURCE(nDlgID), RT_DIALOG); if (!hRes) return; // 加载并锁定资源 HGLOBAL hResData = LoadResource(hInst, hRes); if (!hResData) return; LPVOID pTemplate = LockResource(hResData); LPBYTE pItem = nullptr; if (*(WORD*)pTemplate == 0xFFFF) { // 扩展对话框模板(DLGTEMPLATEEX) DLGTEMPLATEEX* pExTpl = (DLGTEMPLATEEX*)pTemplate; pItem = (LPBYTE)pExTpl + sizeof(DLGTEMPLATEEX); pItem = SkipDialogStrings(pItem); // 跳过菜单、类名、标题等字符串 } else { // 标准对话框模板(DLGTEMPLATE) DLGTEMPLATE* pStdTpl = (DLGTEMPLATE*)pTemplate; pItem = (LPBYTE)pStdTpl + sizeof(DLGTEMPLATE); pItem = SkipDialogStrings(pItem); } // 遍历所有控件模板 while (*(WORD*)pItem != 0x0000) { // 控件列表以0x0000结尾 if (*(WORD*)pItem == 0xFFFF) { // 扩展控件模板 DLGITEMTEMPLATEEX* pItemEx = (DLGITEMTEMPLATEEX*)pItem; int x = pItemEx->x; int y = pItemEx->y; int width = pItemEx->cx; int height = pItemEx->cy; WORD ctrlID = pItemEx->id; // 根据ID判断目标控件 if (ctrlID == IDOK) { // 这里拿到OK按钮的原始资源值:x=30, y=46, width=42, height=18 // 可以存储或使用这些数值 } else if (ctrlID == IDCANCEL) { // Cancel按钮的原始值:x=145, y=46, width=42, height=18 } // 移动到下一个控件模板 pItem = (LPBYTE)pItemEx + sizeof(DLGITEMTEMPLATEEX); pItem = SkipDialogStrings(pItem); } else { // 标准控件模板 DLGITEMTEMPLATE* pItemStd = (DLGITEMTEMPLATE*)pItem; int x = pItemStd->x; int y = pItemStd->y; int width = pItemStd->cx; int height = pItemStd->cy; WORD ctrlID = *(WORD*)((LPBYTE)pItemStd + sizeof(DLGITEMTEMPLATE)); // 同样处理IDOK/IDCANCEL if (ctrlID == IDOK) { /* ... */ } // 移动到下一个控件模板 pItem = (LPBYTE)pItemStd + sizeof(DLGITEMTEMPLATE) + sizeof(WORD); pItem = SkipDialogStrings(pItem); } } // 解锁资源(可选,系统会自动释放) UnlockResource(hResData); FreeResource(hResData); }
方法二:对话框初始化时获取运行时原始尺寸(适合已创建对话框的场景)
如果你的对话框已经创建完成,且还未进行自适应缩放,在OnInitDialog(MFC)或WM_INITDIALOG消息处理函数中,可以通过控件句柄获取初始位置和尺寸(此时的数值和资源文件定义一致,未被DPI缩放或调整的情况下):
BOOL OnInitDialog() { CDialogEx::OnInitDialog(); // 获取OK按钮的句柄 HWND hBtnOK = GetDlgItem(IDOK)->m_hWnd; // MFC方式 // 非MFC:HWND hBtnOK = GetDlgItem(hDlg, IDOK); RECT rcBtn; GetWindowRect(hBtnOK, &rcBtn); // 转换为对话框客户区坐标(如果需要) ScreenToClient(m_hWnd, (LPPOINT)&rcBtn); ScreenToClient(m_hWnd, (LPPOINT)&rcBtn + 1); // rcBtn.left就是资源里的x,top是y,right-left是width,bottom-top是height // 此时数值和资源定义一致(未缩放时) return TRUE; }
注意事项
- 方法二的前提是对话框还未被缩放或调整,如果系统开启了高DPI缩放,可能需要关闭对话框的DPI感知才能拿到原始值;而方法一不受DPI影响,直接读取资源文件中的原始数值。
- 解析模板时要注意处理Unicode字符串和DWORD对齐,这是对话框资源格式的要求。
内容的提问来源于stack exchange,提问作者Pangu
相关产品推荐
相关产品推荐

