C++ 使用ATL子类化在Windows 10 64位上随机崩溃
从一开始:自2017年3月1日起,这是微软确认的一个错误。阅读结尾处的评论。 简短描述: 我在使用MFC、ATL的大型应用程序中遇到随机崩溃。在所有这些情况下,当ATL子类化用于一个窗口时,通过一个窗口的简单操作(移动、调整大小、设置焦点、绘制等),我会在一个随机执行地址上崩溃 首先,它看起来像一个疯狂的指针或堆损坏,但我将整个场景缩小到一个使用纯ATL和仅使用Windows API的非常简单的应用程序 需求/我使用的场景:C++ 使用ATL子类化在Windows 10 64位上随机崩溃,c++,visual-studio-2015,windows-10,atl,visual-studio-2017,C++,Visual Studio 2015,Windows 10,Atl,Visual Studio 2017,从一开始:自2017年3月1日起,这是微软确认的一个错误。阅读结尾处的评论。 简短描述: 我在使用MFC、ATL的大型应用程序中遇到随机崩溃。在所有这些情况下,当ATL子类化用于一个窗口时,通过一个窗口的简单操作(移动、调整大小、设置焦点、绘制等),我会在一个随机执行地址上崩溃 首先,它看起来像一个疯狂的指针或堆损坏,但我将整个场景缩小到一个使用纯ATL和仅使用Windows API的非常简单的应用程序 需求/我使用的场景: 该应用程序是使用VS 2015 Enterprise Update
- 该应用程序是使用VS 2015 Enterprise Update 3创建的
- 该程序应编译为32位
- 测试应用程序使用CRT作为共享DLL
- 该应用程序在Windows 10 Build 14393.693 64位下运行(但我们在Windows 8.1和Windows Server 2012 R2下都有repos,均为64位)
- atlthunk.dll的版本为10.0.14393.0
atlthunk.dll!AtlThunk_Call(unsigned int,unsigned int,unsigned int,long) Unknown
atlthunk.dll!AtlThunk_0x00(struct HWND__ *,unsigned int,unsigned int,long) Unknown
user32.dll!__InternalCallWinProc@20() Unknown
user32.dll!UserCallWinProcCheckWow() Unknown
user32.dll!SendMessageWorker() Unknown
user32.dll!SendMessageW() Unknown
CrashAtlThunk.exe!WindowCheck() Line 52 C++
调试器中引发的异常如下所示:
Exception thrown at 0x0BF67000 in CrashAtlThunk.exe:
0xC0000005: Access violation executing location 0x0BF67000.
还是另一个样本
Exception thrown at 0x2D75E06D in CrashAtlThunk.exe:
0xC0000005: Access violation executing location 0x2D75E06D.
我对atlthunk.dll的了解:
Atlthunk.dll似乎只是64位操作系统的一部分。我在Win 8.1和Win 10系统上找到了它
如果atlthunk.dll可用(所有Windows 10计算机),则此dll关心Thunk。如果DLL不存在,则按标准方式执行thunk:在堆上分配一个块,将其标记为可执行,添加一些load和一个jump语句
如果DLL存在。它包含256个用于子类化的预定义插槽。如果完成了256个子类,DLL会再次将自身重新加载到内存中,并使用DLL中接下来的256个可用插槽
据我所知,atlthunk.dll属于Windows 10,不可交换或再发行
检查内容:
- 防病毒系统已打开或关闭,没有更改
- 数据执行保护并不重要。(/NXCOMPAT:NO,EXE在系统设置中定义为排除,也会崩溃)
- 子类之后对FlushinInstructionCache的其他调用或睡眠调用没有任何效果
- 堆完整性在这里不是问题,我用多个工具重新检查了它
- 还有上千个(我可能已经忘记我测试的内容了)…;)李>
- 还有其他人能重演吗
- 有人看到过类似的效果吗
- 有人知道或能想象出这样做的原因吗
- 有人知道如何避开这个问题吗李>
// CrashAtlThunk.cpp : Defines the entry point for the application.
//
// Windows Header Files:
#include <windows.h>
// C RunTime Header Files
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit
#include <atlbase.h>
#include <atlstr.h>
#include <atlwin.h>
// Global Variables:
HINSTANCE hInst; // current instance
const int NUM_WINDOWS = 1000;
//------------------------------------------------------
// The problematic code
// After the 256th subclass the application randomly crashes.
class CMyWindow : public CWindowImpl<CMyWindow>
{
public:
virtual BOOL ProcessWindowMessage(_In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam, _Inout_ LRESULT& lResult, _In_ DWORD dwMsgMapID) override
{
return FALSE;
}
};
void WindowCheck()
{
HWND ahwnd[NUM_WINDOWS];
CMyWindow subclass[_countof(ahwnd)];
HWND hwndFrame;
ATLVERIFY(hwndFrame = ::CreateWindow(_T("Static"), _T("Frame"), SS_SIMPLE, 0, 0, 10, 10, NULL, NULL, hInst, NULL));
for (int i = 0; i<_countof(ahwnd); ++i)
{
ATLVERIFY(ahwnd[i] = ::CreateWindow(_T("Static"), _T("DummyWindow"), SS_SIMPLE|WS_CHILD, 0, 0, 10, 10, hwndFrame, NULL, hInst, NULL));
if (ahwnd[i])
{
subclass[i].SubclassWindow(ahwnd[i]);
ATLVERIFY(SendMessage(ahwnd[i], WM_GETTEXTLENGTH, 0, 0)!=0);
}
}
for (int i = 0; i<_countof(ahwnd); ++i)
{
if (ahwnd[i])
::DestroyWindow(ahwnd[i]);
}
::DestroyWindow(hwndFrame);
}
//------------------------------------------------------
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
hInst = hInstance;
int iCount = _tcstol(lpCmdLine, nullptr, 10);
__try
{
WindowCheck();
if (iCount==0)
{
::MessageBox(NULL, _T("Succeeded"), _T("CrashAtlThunk"), MB_OK|MB_ICONINFORMATION);
}
else
{
TCHAR szFileName[_MAX_PATH];
TCHAR szCount[16];
_itot_s(--iCount, szCount, 10);
::GetModuleFileName(NULL, szFileName, _countof(szFileName));
::ShellExecute(NULL, _T("open"), szFileName, szCount, nullptr, SW_SHOW);
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
::MessageBox(NULL, _T("Crash"), _T("CrashAtlThunk"), MB_OK|MB_ICONWARNING);
return FALSE;
}
return 0;
}
//crashatlhunk.cpp:定义应用程序的入口点。
//
//Windows头文件:
#包括
//C运行时头文件
#包括
#包括
#包括
#包括
#定义_ATL_CSTRING_EXPLICIT_构造函数//某些CSTRING构造函数将是显式的
#包括
#包括
#包括
//全局变量:
HINSTANCE hInst;//当前实例
const int NUM_WINDOWS=1000;
//------------------------------------------------------
//有问题的代码
//在第256个子类之后,应用程序随机崩溃。
CMyWindow类:公共CWindowImpl
{
公众:
虚拟BOOL ProcessWindowMessage(_In_hwndhwnd,_In_uintumsg,_In_wparamwparam,u In_lparamlparam,_Inout_LRESULT&LRESULT,u In_worddwmsgmapid)重写
{
返回FALSE;
}
};
void WindowCheck()
{
HWND ahwnd[NUM_WINDOWS];
CMyWindow亚类[_countof(ahwnd)];
HWND-HWND帧;
ATLVERIFY(hwndFrame=::CreateWindow(_T(“Static”),_T(“Frame”),SS_SIMPLE,0,0,10,10,NULL,NULL,hInst,NULL));
对于(int i=0;i这是atlthunk.dll中的错误。当它第二次加载自身时,然后通过MapViewOfFile调用手动执行此操作。在这种情况下,并非每个相对于模块基的地址都是
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
// Get the load address of ATLTHUNK.DLL
// HMODULE hMod = LoadLibrary(_T("atlThunk.dll"));
// Now allocate a page at the prefered start address
void* pMem = VirtualAlloc(reinterpret_cast<void*>(0x0f370000), 0x10000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
DWORD dwLastError = ::GetLastError();
hInst = hInstance;
WindowCheck();
return 0;
}
// A minimum ATL program with more than 256 windows. In practise they would not be toplevel, but e.g. buttons.
// Thanks to https://www.codeguru.com/cpp/com-tech/atl/article.php/c3605/Using-the-ATL-Windowing-Classes.htm
// for helping with ATL.
// You need to be up to date, like have KB3030947 or KB3061512. Otherwise asserts will fail instead.
#undef _DEBUG
#include <atlbase.h>
ATL::CComModule _Module;
#include <atlwin.h>
#include <assert.h>
#include <string>
BEGIN_OBJECT_MAP(ObjectMap) END_OBJECT_MAP()
struct CMyWindow : CWindowImpl<CMyWindow>
{
BEGIN_MSG_MAP(CMyWindow) END_MSG_MAP()
};
int __cdecl wmain()
{
// Exacerbate the problem, which can happen more like if by chance.
PROCESS_INFORMATION process = { 0 };
{
// Be sure another process has atlthunk loaded.
WCHAR cmd[] = L"rundll32 atlthunk,x";
STARTUPINFOW startup = { sizeof(startup) };
BOOL success = CreateProcessW(0, cmd, 0, 0, 0, 0, 0, 0, &startup, &process);
assert(success && process.hProcess);
CloseHandle(process.hThread);
// Get atlthunk's usual address.
HANDLE file = CreateFileW((std::wstring(_wgetenv(L"SystemRoot")) + L"\\system32\\atlthunk.dll").c_str(), GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
assert(file != INVALID_HANDLE_VALUE);
HANDLE mapping = CreateFileMappingW(file, 0, PAGE_READONLY | SEC_IMAGE, 0, 0, 0);
assert(mapping);
void* view = MapViewOfFile(mapping, 0, 0, 0, 0);
assert(view);
UnmapViewOfFile(view);
VirtualAlloc(view, 1, MEM_COMMIT | MEM_RESERVE, PAGE_NOACCESS);
}
_Module.Init(0, 0);
const int N = 300;
CMyWindow wnd[N];
for (int i = 0; i < N; ++i)
{
wnd[i].Create(0, CWindow::rcDefault, L"Hello", (i < N - 1) ? 0 : (WS_OVERLAPPEDWINDOW | WS_VISIBLE));
wnd[i].DestroyWindow();
}
TerminateProcess(process.hProcess, 0);
CloseHandle(process.hProcess);
MSG msg;
while (GetMessageW(&msg, 0, 0, 0))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
_Module.Term();
}