我们有一些Win32应用程序(在Delphi 2006中编码),有时用户会收到一条错误消息,指出“系统错误。代码:8。没有足够的存储空间来处理此命令。”
从堆栈跟踪看起来它总是在CreateWnd调用
期间Main ($1edc):
004146cc +070 app.exe SysUtils RaiseLastOSError
00414655 +005 app.exe SysUtils RaiseLastOSError
004ce44c +130 app.exe Controls TWinControl.CreateWnd
00535a72 +022 app.exe cxControls TcxControl.CreateWnd
004ce82a +016 app.exe Controls TWinControl.CreateHandle
00553d21 +005 app.exe cxContainer TcxContainer.CreateHandle
00586ef1 +005 app.exe cxEdit TcxCustomEdit.CreateHandle
005c331d +005 app.exe cxDropDownEdit TcxCustomDropDownEdit.CreateHandle
004ceaf0 +074 app.exe Controls TWinControl.UpdateShowing
004ceb1e +0a2 app.exe Controls TWinControl.UpdateShowing
004cebdc +03c app.exe Controls TWinControl.UpdateControlState
004d118a +026 app.exe Controls TWinControl.CMVisibleChanged
004cb713 +2bb app.exe Controls TControl.WndProc
004cf569 +499 app.exe Controls TWinControl.WndProc
004b727d +4c1 app.exe Forms TCustomForm.WndProc
004cb3a0 +024 app.exe Controls TControl.Perform
004c9f6a +026 app.exe Controls TControl.SetVisible
004b6c46 +03a app.exe Forms TCustomForm.SetVisible
004baf1b +007 app.exe Forms TCustomForm.Show
004bb151 +14d app.exe Forms TCustomForm.ShowModal
007869c7 +0d3 app.exe UfrmPrice 770 +19 TfrmPrice.EditPrice
0078655d +009 app.exe UfrmPrice 628 +0 TfrmPrice.actNewBidExecute
00431ce7 +00f app.exe Classes TBasicAction.Execute
004c2cb5 +031 app.exe ActnList TContainedAction.Execute
004c397c +050 app.exe ActnList TCustomAction.Execute
00431bb3 +013 app.exe Classes TBasicActionLink.Execute
004af384 +090 app.exe Menus TMenuItem.Click
004b059f +013 app.exe Menus TMenu.DispatchCommand
004b16fe +082 app.exe Menus TPopupList.WndProc
004b164d +01d app.exe Menus TPopupList.MainWndProc
004329a8 +014 app.exe Classes StdWndProc
7e4196b2 +00a USER32.dll DispatchMessageA
004bea60 +0fc app.exe Forms TApplication.ProcessMessage
004bea9a +00a app.exe Forms TApplication.HandleMessage
004becba +096 app.exe Forms TApplication.Run
008482c5 +215 app.exe AppName 129 +42 initialization
我从来没有能够找到导致这种情况的根源,因为它很少发生我没有关心的问题,但我想找出导致它的原因并希望纠正它... < / p>
编辑:完整Stacktrace
编辑2:更多信息...今天遇到此问题的客户已将我的应用安装了大约4个月,并且每天8小时都在他的电脑上运行。问题只出现在今天并且一直重复出现,即使他杀了我的应用程序并重新启动它。他的系统中没有其他应用程序表现得很奇怪。重启后问题完全消失了。这是否意味着史蒂夫提到的堆短缺?
答案 0 :(得分:31)
实际上这是ATOM表的问题。 I reported this issue to Embarcadero因为它给我带来了许多悲伤。
如果您监视全局原子表,您将看到Delphi应用程序正在泄漏原子,留下应用程序的ID而不会将其从内存中删除:
您将看到大量以下项目:
**Delphi000003B4*
*Controlofs0040000000009C0**
基本上,由于您在请求另一个时不能注册超过0xFFFF的不同Windows消息ID,系统将返回“系统错误。代码:8。没有足够的存储空间来处理此消息命令强>”。然后,您将无法启动任何创建窗口的应用程序。
在Embarcadero QC Central报道了此问题出现在Windows 7 / Windows Server 2008下。事实上,在Windows Server 2003上以及它之前运行是因为错误的实现,一旦索引包含最多16384个单元,它就会回收ATOM。 / p>
随意使用我的Global Atom Monitor检查您的Delphi应用程序是否正在泄漏原子。
要解决此问题,您需要Embarcadero的补丁。
答案 1 :(得分:27)
如果您的程序使用大量Windows资源,则可能是资源堆短缺。
可以增加一个注册表项来提高XP的堆大小。对于Vista,Microsoft已将默认值设置得更高。我建议将默认值3072更改为至少8192。
此信息记录在MS Knowledge Base中(或搜索“Out of Memory”)。有关参数值的其他详细信息,请参阅文章KB184802。
我建议您阅读知识库文章,但有关更改的基本信息是:
运行注册表编辑器(REGEDT32.EXE)。
从HKEY_ LOCAL_MACHINE子树,转到以下键:
\System\CurrentControlSet\Control\Session Manager\SubSystem
在屏幕右侧双击按键:
windows
在弹出窗口中,您将看到一个非常长的字段。将光标移动到字符串开头附近寻找此值(值可能会有所不同):
SharedSection=1024,3072,512
SharedSection使用以下格式指定系统和桌面堆:SharedSection=xxxx,yyyy,zzz
其中xxxx
定义系统范围堆的最大大小(以千字节为单位),yyyy
定义每个桌面堆的大小,zzz
定义“非交互式”窗口站的桌面堆大小。
仅将yyyy
值更改为8192(或更大),然后按OK。
退出注册表编辑器并重新启动PC以使更改生效。
答案 2 :(得分:21)
我一直在寻找2年,感谢Jordi Corbilla answer我终于得到了它!
简而言之: Delphi源码存在导致此问题的错误!
让我们了解发生了什么:
Windows有一个名为“Atom table”的内存区域,用于相互通信的应用程序(see more)。
此外,Windows还有另一个“内存区域”,称为“窗口消息系统”,它的作用相同(see more)。
这两个存储区域各有“16k插槽”。在第一个中,可以使用以下Windows API REMOVE 一个原子:
GlobalDeleteAtom // for removing an atom added by "GlobalAddAtom"
在第二个“区域”中,我们只是无法删除任何内容!
RegisterWindowMessage函数通常用于注册 两个合作应用程序之间通信的消息。如果 两个不同的应用程序注册相同的消息字符串, 应用程序返回相同的消息值。 消息仍然存在 注册到会议结束。
德尔福编译的应用程序(至少由D7编写)将在“消息区域”中记录一些记录,并在“原子表”中记录其他一些记录每次启动。应用程序尝试在应用程序关闭时删除它们,但我发现很多(和很多)“原子泄漏”,即使应用程序关闭后也是如此。
此时您可以看到,如果您的服务器每天启动数千个应用程序,您可能很快就会达到16k限制,问题就开始了!此时的解决方案?只有一次重启。
那么,我们能做什么?好吧,我的朋友,很抱歉告诉你,但我们需要修复Delphi源代码并重新编译所有应用程序。
首先,打开单位Controls.pas并替换以下行:
RM_GetObjectInstance := RegisterWindowMessage(PChar(ControlAtomString));
有:
RM_GetObjectInstance := RegisterWindowMessage('RM_GetObjectInstance');
然后重新编译Delphi包和您的应用程序。
因为我发现即使应用程序关闭后原子泄漏,我创建了一个垃圾收集任何原子留下的应用程序。它每小时运行以下代码:
procedure GarbageCollectAtoms;
var i, len : integer;
cstrAtomName: array [0 .. 1024] of char;
AtomName, Value, procName: string;
ProcID,lastError : cardinal;
countDelphiProcs, countActiveProcs, countRemovedProcs, countCantRemoveProcs, countUnknownProcs : integer;
// gets program's name from process' handle
function getProcessFileName(Handle: THandle): string;
begin
Result := '';
{ not used anymore
try
SetLength(Result, MAX_PATH);
if GetModuleFileNameEx(Handle, 0, PChar(Result), MAX_PATH) > 0 then
SetLength(Result, StrLen(PChar(Result)))
else
Result := '';
except
end;
}
end;
// gets the last 8 digits from the given atomname and try to convert them to and integer
function getProcessIdFromAtomName(name:string):cardinal;
var l : integer;
begin
result := 0;
l := Length(name);
if (l > 8) then
begin
try
result := StrToInt64('$' + copy(name,l-7,8));
except
// Ops! That should be an integer, but it's not!
// So this was no created by a 'delphi' application and we must return 0, indicating that we could not obtain the process id from atom name.
result := 0;
end;
end;
end;
// checks if the given procID is running
// results: -1: we could not get information about the process, so we can't determine if is active or not
// 0: the process is not active
// 1: the process is active
function isProcessIdActive(id: cardinal; var processName: string):integer;
var Handle_ID: THandle;
begin
result := -1;
try
Handle_ID := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, false, id);
if (Handle_ID = 0) then
begin
result := 0;
end
else
begin
result := 1;
// get program's name
processName := getProcessFileName(Handle_ID);
CloseHandle(Handle_ID);
end;
except
result := -1;
end;
end;
procedure Log(msg:string);
begin
// Memo1.Lines.Add(msg);
end;
begin
// initialize the counters
countDelphiProcs := 0;
countActiveProcs := 0;
countRemovedProcs := 0;
countUnknownProcs := 0;
// register some log
Log('');
Log('');
Log('Searching Global Atom Table...');
for i := $C000 to $FFFF do
begin
len := GlobalGetAtomName(i, cstrAtomName, 1024);
if len > 0 then
begin
AtomName := StrPas(cstrAtomName);
SetLength(AtomName, len);
Value := AtomName;
// if the atom was created by a 'delphi application', it should start with some of strings below
if (pos('Delphi',Value) = 1) or
(pos('ControlOfs',Value) = 1) or
(pos('WndProcPtr',Value) = 1) or
(pos('DlgInstancePtr',Value) = 1) then
begin
// extract the process id that created the atom (the ProcID are the last 8 digits from atomname)
ProcID := getProcessIdFromAtomName(value);
if (ProcId > 0) then
begin
// that's a delphi process
inc(countDelphiProcs);
// register some log
Log('');
Log('AtomName: ' + value + ' - ProcID: ' + inttostr(ProcId) + ' - Atom Nº: ' + inttostr(i));
case (isProcessIdActive(ProcID, procName)) of
0: // process is not active
begin
// remove atom from atom table
SetLastError(ERROR_SUCCESS);
GlobalDeleteAtom(i);
lastError := GetLastError();
if lastError = ERROR_SUCCESS then
begin
// ok, the atom was removed with sucess
inc(countRemovedProcs);
// register some log
Log('- LEAK! Atom was removed from Global Atom Table because ProcID is not active anymore!');
end
else
begin
// ops, the atom could not be removed
inc(countCantRemoveProcs);
// register some log
Log('- Atom was not removed from Global Atom Table because function "GlobalDeleteAtom" has failed! Reason: ' + SysErrorMessage(lastError));
end;
end;
1: // process is active
begin
inc(countActiveProcs);
// register some log
Log('- Process is active! Program: ' + procName);
end;
-1: // could not get information about process
begin
inc(countUnknownProcs);
// register some log
Log('- Could not get information about the process and the Atom will not be removed!');
end;
end;
end;
end;
end;
end;
Log('');
Log('Scan complete:');
Log('- Delphi Processes: ' + IntTostr(countDelphiProcs) );
Log(' - Active: ' + IntTostr(countActiveProcs) );
Log(' - Removed: ' + IntTostr(countRemovedProcs) );
Log(' - Not Removed: ' + IntTostr(countCantRemoveProcs) );
Log(' - Unknown: ' + IntTostr(countUnknownProcs) );
TotalAtomsRemovidos := TotalAtomsRemovidos + countRemovedProcs;
end;
(上面的代码基于this code)
之后,我再也没有得到这个f **错误!
延迟更新:
此外,这是此错误的来源:Application error: fault address 0x00012afb
答案 3 :(得分:2)
您可以使用Microsoft的Desktop Heap Monitor查看堆统计信息(使用%等),可从以下位置获取:
答案 4 :(得分:2)
我注意到这个错误(系统错误。代码:8。没有足够的存储...)最近使用一些Twain代码时,它发生在我的计算机而不是我的同事身上,我们的机器之间唯一真正的区别是我使用笔记本电脑屏幕作为第二台显示器,因此我的桌面总尺寸更大。
我找到了Steve Black上面提到的问题的文档,但我找到了一种方法(至少修复了我的机器上的错误),不需要编辑注册表:
旧代码正在使用
DC := GetDC(Owner.VirtualWindow);
// ...
ReleaseDC(Owner.VirtualWindow, DC);
我发现用它替换它可以摆脱我的错误
DC := CreateCompatibleDC(Owner.VirtualWindow);
// ...
DeleteDC(DC);
我不知道这是否与您的问题有关,但将来可能对其他人有所帮助。
答案 5 :(得分:0)
编译器中可能存在错误,这是一个公平的赌注,这是你的应用程序中导致问题的东西。可能是你的应用程序泄漏窗口句柄或其他一些GUI对象,如钢笔/画笔?这可能是一个原因。
答案 6 :(得分:0)
对我来说,只是一堆TJPEGImages以幻灯片放映方式解压缩,最终耗尽内存。