Nous avons quelques applications Win32 (codées dans Delphi 2006) où l'utilisateur reçoit parfois un message d'erreur indiquant "Erreur système. Code: 8. Le stockage disponible est insuffisant pour traiter cette commande.".
Depuis la pile, il semble que ce soit toujours le cas lors de l'appel 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
Je n'ai jamais été en mesure de cerner les causes de ceci et, comme cela se produit assez rarement, je ne suis pas inquiet, mais j'aimerais savoir ce qui les cause et, espérons-le, le corriger ...
EDIT: Full Stacktrace
EDIT 2: Plus d'infos ... Le client qui a vécu cela aujourd'hui a mon application installée depuis environ 4 mois et s'exécute sur son PC 8 heures par jour. Le problème est seulement apparu aujourd'hui et a continué à réapparaître même s'il a tué mon application et l'a redémarré. Aucune des autres applications de son système ne s'est comportée étrangement. Après un redémarrage, le problème disparaît complètement. Est-ce que cela indique la pénurie de tas mentionnée par Steve?
EDIT 3: Intéressant billet de blog msdn ici et ici sur le sujet du segment de bureau. Bien que je ne sois pas sûr que ce soit la cause du problème, cela semble certainement probable.
Si votre programme utilise beaucoup de ressources Windows, cela pourrait être une pénurie de ressources.
Il existe une entrée de registre qui peut être augmentée pour augmenter la taille de segment de mémoire pour XP. Pour Vista, Microsoft définit déjà la valeur par défaut plus élevée. Je recommande de changer le défaut 3072 à au moins 8192.
Ces informations sont documentées dans la base de connaissances MS (ou recherchez «Mémoire insuffisante»). Vous trouverez plus de détails concernant les valeurs de paramètre dans l'article KB184802 .
Je vous suggère de lire l'article de la base de connaissances, mais les informations de base sur le changement sont les suivantes:
Exécutez l'Éditeur du Registre (REGEDT32.EXE).
Dans la sous-arborescence HKEY_ LOCAL_MACHINE, accédez à la clé suivante:
\System\CurrentControlSet\Control\Session Manager\SubSystem
Sur le côté droit de l'écran, double-cliquez sur la clé:
windows
Dans la fenêtre contextuelle, vous verrez un très long champ sélectionné. Déplacez le curseur vers le début de la chaîne en recherchant ceci (les valeurs peuvent varier):
SharedSection=1024,3072,512
SharedSection spécifie les segments de mémoire système et de bureau au format suivant: SharedSection=xxxx,yyyy,zzz
où xxxx
définit la taille maximale du segment de mémoire système (en kilo-octets), yyyy
définit la taille du segment de mémoire par bureau et zzz
définit la taille du segment de une station de fenêtre "non interactive".
Modifiez SEULEMENT la valeur yyyy
en 8192 (ou plus) et appuyez sur OK.
Quittez l'Éditeur du Registre et redémarrez le PC pour que la modification soit prise en compte.
Bonne chance.
En réalité, c'est un problème avec la table ATOM. J'ai signalé ce problème à Embarcadero car il me cause beaucoup de chagrin.
Si vous surveillez la table des atomes globaux, vous constaterez que les applications Delphi perdent des atomes, laissant l'identifiant de votre application sans le supprimer de la mémoire:
Vous verrez des tonnes d'articles suivants:
**Delphi000003B4*
*Controlofs0040000000009C0**
En principe, comme vous ne pouvez pas enregistrer plus de 0xFFFF différents identifiants de messages Windows dès que vous en demandez un autre, le système renvoie "Erreur système. Code: 8. Le stockage disponible est insuffisant pour traiter cette commande" . Ensuite, vous ne serez pas en mesure de démarrer une application qui crée une fenêtre.
Un autre problème a été signalé à Embarcadero QC Central.
Ce problème se présente sous Windows 7/Windows Server 2008. Le fait que sur Windows Server 2003 et avant son exécution est dû à une implémentation incorrecte, qui recycle les ATOM une fois que leur index est entouré d'un maximum de 16384 unités.
N'hésitez pas à utiliser mon Global Atom Monitor pour vérifier si vos applications Delphi ont des fuites d'atomes ou non.
Pour résoudre ce problème, vous aurez besoin d'un correctif d'Embarcadero.
Je cherche depuis 2 ans et grâce à Jordi Corbilla répond je l’ai enfin compris!
En quelques mots: La source Delphi a des bugs qui vous causent ce problème!
Comprenons ce qui se passe:
Windows a une zone de mémoire appelée "Table Atom", qui sert aux applications se communiquer ( voir plus ).
En outre, Windows possède une autre "zone de mémoire", appelée "Système de messagerie Windows", qui a la même fonction ( voir plus ).
Ces deux zones de mémoire ont chacune "16k slots". Dans le premier, il est possible de SUPPRIMER un atome, en utilisant l'API Windows suivante:
GlobalDeleteAtom // for removing an atom added by "GlobalAddAtom"
Dans la seconde "zone", nous ne faisons que IMPOSSIBLE DE RETIRER!
La fonction RegisterWindowMessage est généralement utilisée pour enregistrer messages pour la communication entre deux applications coopérantes. Si deux applications différentes enregistrent la même chaîne de message, le les applications renvoient la même valeur de message. Le message reste enregistré jusqu'à la fin de la session.
Les applications compilées Delphi (au moins par D7) placent un enregistrement dans "Zone de messagerie" et quelques autres enregistrements dans "Table Atom" À chaque fois qu'ils sont démarrés. L'application tente de les supprimer lors de la fermeture de l'application, mais j'ai trouvé beaucoup (et beaucoup) de "fuites atomiques", même après la fermeture de l'application.
À ce stade, vous pouvez voir que si vous avez un serveur qui lance des milliers d'applications par jour, vous devriez probablement atteindre bientôt la limite de 16k, et le problème commence! La solution à ce stade? Rien qu'un redémarrage simple.
Alors, que pouvons-nous faire? Eh bien, mon ami, je suis désolé de vous le dire, mais nous devons corriger le code source de Delphi et recompiler toutes les applications.
Tout d’abord, ouvrez le fichier Controls.pas et remplacez la ligne suivante:
RM_GetObjectInstance := RegisterWindowMessage(PChar(ControlAtomString));
pour:
RM_GetObjectInstance := RegisterWindowMessage('RM_GetObjectInstance');
puis recompilez les packages Delphi et vos applications.
Comme j'ai trouvé des fuites d'atomes même après la fermeture de l'application, j'ai créé une application qui permet à Garbage de collecter tous les atomes laissés. Il ne fait que lancer le code suivant toutes les heures:
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;
(Ce code ci-dessus était basé sur ce code )
Après cela, je n'ai plus jamais cette erreur f **!
Mise à jour tardive:
En outre, c’est la source de cette erreur: Erreur d’application: adresse de défaillance 0x00012afb
Vous pouvez utiliser Desktop Heap Monitor de Microsoft pour afficher des statistiques sur le tas (utilisez%, etc.). Il est disponible à l'adresse suivante:
J'ai remarqué cette erreur (Erreur système. Code: 8. Stockage insuffisant ...) lors de l'utilisation d'un code Twain, cela se passait sur mon ordinateur et non sur celui de mon collègue, et la seule différence réelle entre nos machines est que j'utilise le écran d'ordinateur portable en tant que deuxième moniteur, mes dimensions totales de bureau sont donc plus grandes.
J'ai trouvé la documentation du problème évoqué ci-dessus par Steve Black, mais j'ai trouvé un moyen (qui corrige au moins l'erreur sur ma machine) qui ne nécessite pas de modification du registre:
L'ancien code utilisait
DC := GetDC(Owner.VirtualWindow);
// ...
ReleaseDC(Owner.VirtualWindow, DC);
et j'ai trouvé que le remplacer par cela m'a débarrassé de mes erreurs
DC := CreateCompatibleDC(Owner.VirtualWindow);
// ...
DeleteDC(DC);
Je ne sais pas si cela a un rapport avec votre problème, mais cela pourrait être utile pour d'autres à l'avenir.
Il pourrait y avoir des bugs dans le compilateur, il y a fort à parier que c'est quelque chose dans votre application qui pose problème. Se pourrait-il que votre application présente des fuites de poignées de fenêtre ou d'un autre objet d'interface graphique tel que des stylos/pinceaux? Cela pourrait être une cause.
Pour moi, c’était juste un tas de TJPEGImages qui décompressaient en diaporama et qui manquaient finalement de mémoire.