【发布时间】:2020-02-25 00:22:12
【问题描述】:
我正在使用一个内部 MFC 应用程序,它试图通过在使用 Win32 LoadResource 函数获得的 DLGTEMPLATEEX 结构中找到对话框并直接写入该结构的字体大小成员来以编程方式更改对话框的字体大小。在 VS 2019 的 Debugger 下运行时,会触发内存写访问冲突。
BOOL CMk20Dlg::Create(UINT nIDTemplate, CWnd* pParentWnd)
{
LPCTSTR lpszTemplateName = MAKEINTRESOURCE(nIDTemplate);
ASSERT(IS_INTRESOURCE(lpszTemplateName) || AfxIsValidString(lpszTemplateName));
m_lpszTemplateName = lpszTemplateName;
if (IS_INTRESOURCE(m_lpszTemplateName) && m_nIDHelp == 0)
m_nIDHelp = LOWORD(reinterpret_cast<DWORD_PTR>(m_lpszTemplateName));
HINSTANCE hInst = AfxFindResourceHandle(lpszTemplateName, RT_DIALOG);
HRSRC hResource = ::FindResource(hInst, lpszTemplateName, RT_DIALOG);
if (hResource == NULL)
TRACE(_T("FindResource failed with error %d\n"), ::GetLastError());
HGLOBAL hTemplate = ::LoadResource(hInst, hResource);
DLGTEMPLATE* lpTemplate = static_cast<DLGTEMPLATE*>(::LockResource(hTemplate));
WORD* pwFontSize = GetResFontSizeOffset(lpTemplate);
*pwFontSize = static_cast<WORD>((8.0 * theApp.GetFontScaleFactor()) + 0.5);
// ^-- crash on write to *pwFontSize
我相信代码在返回的结构中找到了正确的目标内存位置,因为该结构在预期的偏移量处具有正确的签名(0xFFFF 将其标识为 DLGTEMPLATEEX 而不仅仅是 DLGTEMPLATE - 尽管代码没有任何错误检查是否不是),并且我单步执行了计算指向字体大小成员的指针的代码。它指向一个值“8”的 DWORD,这对于系统字体大小来说似乎是合理的。
WORD* CMk20Dlg::GetResFontSizeOffset(DLGTEMPLATE* lpTemplate)
{
WORD* pwPtr = reinterpret_cast<WORD*>(lpTemplate);
// lpTemplate is in the format of a DLGTEMPLATEEX
// Refer Win32 SDK documentation of "struct" DLGTEMPLATEEX
++pwPtr; // dlgVer
++pwPtr; // signature
/* ^-- At this point *pwPtr has value 0xFFFF in the debugger, indicating DLGTEMPLATEEX */
/* The ++'ing of pwPtr continues through the rest of the struct members,
then it skips over the menu array, window class array, title, etc. Finally: */
// We are now pointing at the font size word
return pwPtr;
}
当代码试图通过指针写入将“8”实际更改为另一个数字时,返回值指向 DWORD“8”,它在 Visual Studio 2019 调试器中崩溃,并出现内存“写访问冲突”。
我对我在这里看到的情况有很多担忧。
首先,像这样破坏这个内存位置有什么用吗?操作系统如何看到值发生了变化?我希望正确的方法是一些 Win32 函数调用,如“SetDlgFontSize”之类的,而不是在数据结构中探索。
其次,这段代码到底是如何工作的?它是否适用于旧版本的 Windows,但不适用于 Windows 10?现在失败是因为我将项目升级到 VS 2019(以前是到 2012 年)。还是 VS 2019 调试器(或 Debug 版本)抢先在具有我不应该编写的操作系统结构的内存页面上设置写保护位?
第三,我需要决定是通过注释掉崩溃代码还是“正确”设置字体大小来解决这个问题。这取决于违规代码是否真的产生了影响。如果没有,我可以将其注释掉,但如果我需要正确复制效果,我迄今为止发现的唯一 MFC 代码可以将字体大小和字体系列设置在一起。那么我还需要添加代码来找出正确的字体...
【问题讨论】:
-
我希望从
LoadResource/LockResource返回的值本质上是指向可执行文件/DLL 中包含的资源数据的指针,并且它是不可写的(但我没有通过快速搜索找到任何与此相关的文档)。 -
我已经修改了返回的指针中的数据,并没有产生任何不良影响,但是我总是在调用 LoadResource() 之后使用 LockResource()。您可以查看 MFC 中 CDialogTemplate 的实现。它在
中声明。实现在 MFC 源的 dlgtempl.cpp 文件中。 -
如果您只想更改对话框字体,请参阅此link
标签: c++ windows winapi fonts mfc