很快,我的问题是,当有大量可用内存时,为什么 WinAPIRegisterClass
会失败,我能做些什么来防止它?ERROR_NOT_ENOUGH_MEMORY
背景: 我正在开发许多人用于自动化文件传输的应用程序(WinSCP FTP/SFTP 客户端)。有些人每分钟、每天都从 Windows 调度程序运行它。
我收到很多报告,在运行一定次数后,应用程序停止工作。触发问题的运行次数似乎并不准确,但在数万到数十万之间。此外,似乎问题仅在 Windows 调度程序下运行时出现,而不是在常规 Windows 会话中运行时出现。虽然我不能 100% 确认这一点。
此外,所有报告似乎都适用于 Windows 2008 R2 + 一些适用于 Windows 7。同样,这可能只是巧合。
我自己能够在 Windows 7 上重现该问题。一旦系统进入此状态,我的应用程序将不再在调度程序的会话中启动。但它在正常的常规会话中开始就好了。还有一些其他应用程序(不一定是全部)甚至在调度程序的会话中启动。同样在这种状态下,我无法调试应用程序,因为它甚至在调试器(或进程监视器等工具)运行时都不会加载。
该应用程序使用 Embarcadero(前 Borland)C++ Builder VCL 库。它在 VCL 初始化代码中的某处崩溃(我WinMain
什至没有启动)并以代码 3 退出。检查初始化代码正在做什么,我可能能够识别触发崩溃的代码(尽管它可能只是许多可能的代码之一原因)。
罪魁祸首似乎是返回( )的RegisterClass
WinAPI 函数。发生这种情况时,VCL 代码会抛出异常;由于还没有异常处理程序,它会使应用程序崩溃。8
ERROR_NOT_ENOUGH_MEMORY
我已经使用在 VS 2012 中开发的一个非常简单的 C++ 控制台应用程序验证了这一点(将问题与 C++ Builder 和 VCL 隔离开来)。核心代码是:
SetLastError(ERROR_SUCCESS);
fout << L"Registering class" << std::endl;
WNDCLASS WndClass;
memset(&WndClass, 0, sizeof(WndClass));
WndClass.lpfnWndProc = &DefWindowProc;
WndClass.lpszClassName = L"TestClass";
WndClass.hInstance = GetModuleHandle(NULL);
ATOM Atom = RegisterClass(&WndClass);
DWORD Error = GetLastError();
// The Atom is NULL and Error is ERROR_NOT_ENOUGH_MEMORY here
(测试应用的完整代码在最后)
尽管有错误,但它似乎不是内存问题。我在RegisterClass
调用之前和之后通过分配 10 MB 内存验证了什么(可以在最后的完整测试代码中看到)。
绝望,我什至偷看了 Wine 的RegisterClass
. 它确实可以失败ERROR_NOT_ENOUGH_MEMORY
,但只有当它无法为类注册分配内存时。什么是几个字节。它也使用分配内存HeapAlloc
。Wine 不会RegisterClass
因任何其他原因而失败,并带有任何其他错误代码。
对我来说,它首先看起来像是 Windows 中的一个错误。我相信 Windows 应该在进程退出时释放进程分配的所有资源。所以无论应用程序实现得多么糟糕,上一次运行在资源(如内存)方面都不应该对后续运行产生任何影响。无论如何,我很乐意找到解决方法。
更多事实:测试系统除了标准系统进程(总共大约 50 个)之外没有运行任何特殊的东西。就我而言,它是 VMware 虚拟机,尽管我的用户显然在真实的物理机上看到了问题。该进程的先前实例已经消失,因此它们没有被正确终止,这会阻止系统释放资源。大约有 500 MB 的可用内存(占总数的一半)。只分配了大约 16000 个句柄。
测试 VS 应用程序的完整代码:
#include "stdafx.h"
#include "windows.h"
#include <fstream>
int _tmain(int argc, _TCHAR* argv[])
{
std::wofstream fout;
fout.open(L"log.txt",std::ios::app);
SetLastError(ERROR_SUCCESS);
fout << L"Allocating heap" << std::endl;
LPVOID Mem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 10 * 1024 * 1024);
DWORD Error = GetLastError();
fout << L"HeapAlloc [" << std::hex << intptr_t(Mem) << std::dec
<< L"] Error [" << Error << "]" << std::endl;
// ===== Main testing code begins =====
SetLastError(ERROR_SUCCESS);
fout << L"Registering class" << std::endl;
WNDCLASS WndClass;
memset(&WndClass, 0, sizeof(WndClass));
WndClass.lpfnWndProc = &DefWindowProc;
WndClass.lpszClassName = L"TestClass";
WndClass.hInstance = GetModuleHandle(NULL);
ATOM Atom = RegisterClass(&WndClass);
Error = GetLastError();
fout << L"RegisterClass [" << std::hex << intptr_t(Atom) << std::dec
<< L"] Error [" << Error << "]" << std::endl;
// ===== Main testing code ends =====
SetLastError(ERROR_SUCCESS);
fout << L"Allocating heap" << std::endl;
Mem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 10 * 1024 * 1024);
Error = GetLastError();
fout << L"HeapAlloc [" << std::hex << intptr_t(Mem) << std::dec
<< L"] Error [" << Error << "]" << std::endl;
fout << L"Done" << std::endl;
return 0;
}
输出是(当从 Windows 7 系统上的 Windows 调度程序运行时,通过我的应用程序的数万次运行进入上述状态):
Allocating heap
HeapAlloc [ec0020] Error [0]
Registering class
RegisterClass [0] Error [8]
Allocating heap
HeapAlloc [18d0020] Error [0]
Done