web-dev-qa-db-ja.com

システムエラー。コード:8.このコマンドを処理するのに十分なストレージが利用できない

いくつかのWin32アプリケーション(Delphi 2006でコーディング)があり、ユーザーは "System Error。Code: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

私はこれを引き起こすものの底に達することができませんでした、そしてそれが起こることはかなりめったにないので、私は心配していませんでしたが、私はそれを引き起こし、うまくいけばそれを修正したいと思います...

EDIT:完全なスタックトレース

EDIT 2:詳細...今日これを経験したクライアントは私のアプリを約4か月間インストールし、8時間PCで実行しています一日。問題は今日だけ現れて、彼が私のアプリを殺して再起動したにもかかわらず、再び現れ続けました。彼のシステム上の他のアプリはどれも奇妙な動作をしませんでした。再起動後、問題は完全に解消されます。これは、スティーブが言及したヒープ不足を指し示していますか?

EDIT 3:興味深いmsdnブログ投稿 here および here デスクトップヒープのトピックについてこれが問題の原因であるかどうかはわかりませんが、確かにそうです。

38
Marius

プログラムが多くのWindowsリソースを使用している場合、リソースヒープが不足している可能性があります。

XPのヒープサイズを増やすために増やすことができるレジストリエントリがあります。 Vistaの場合、Microsoftは既にデフォルト値を高く設定しています。デフォルトの3072を少なくとも8192に変更することをお勧めします。

この情報は MSナレッジベース で文書化されています(または「メモリ不足」を検索してください)。パラメーター値に関する追加の詳細については、記事 KB184802 を参照してください。

ナレッジベースの記事を読むことをお勧めしますが、変更に関する基本情報は次のとおりです。

  1. レジストリエディター(REGEDT32.EXE)を実行します。

  2. HKEY_ LOCAL_MACHINEサブツリーから、次のキーに移動します。

    \System\CurrentControlSet\Control\Session Manager\SubSystem
    
  3. 画面の右側で、キーをダブルクリックします。

    windows 
    
  4. ポップアップウィンドウに、非常に長いフィールドが選択されています。これを探して文字列の先頭近くにカーソルを移動します(値は異なる場合があります)。

    SharedSection=1024,3072,512
    
  5. SharedSectionは、次の形式を使用してシステムヒープとデスクトップヒープを指定します。SharedSection=xxxx,yyyy,zzzここで、xxxxはシステム全体のヒープの最大サイズ(キロバイト)を定義し、yyyyはデスクトップヒープごとのサイズを定義し、zzzは「非対話型」ウィンドウステーションのデスクトップヒープ。

  6. yyyy値のみを8192(またはそれ以上)に変更し、[OK]を押します。

  7. レジストリエディターを終了し、PCを再起動して変更を有効にします。

がんばろう。

27
Steve Black

実際、これはATOM table。 この問題をEmbarcaderoに報告しました の問題です。多くの悲しみを引き起こしているためです。

グローバルatomテーブルを監視すると、Delphiアプリがアトムをリークしており、アプリのIDをメモリから削除せずに残していることがわかります。

次のアイテムが大量に表示されます。

**Delphi000003B4*

*Controlofs0040000000009C0**

基本的に、別のウィンドウIDを要求するとすぐに0xFFFFを超える異なるウィンドウメッセージIDを登録できないため、システムは「System Error。Code:8. Not available storage is available」を返します。このコマンドを処理するには」。そうすると、ウィンドウを作成するアプリを起動できなくなります。

別の問題 がEmbarcadero QC Centralで報告されました。

この問題は、Windows 7/Windows Server 2008で発生します。WindowsServer 2003およびそれ以前では、実行が間違っていたため、インデックスが最大16384単位でラップされるとATOMがリサイクルされます。

自由に私の Global Atom Monitor を使用して、Delphiアプリがリークしているかどうかを確認してください原子かどうか。

これを修正するには、Embarcaderoからのパッチが必要です。

31
Jordi Corbilla

私は2年間探していましたが、 Jordi Corbillaの回答のおかげで ようやくわかりました!

簡単に言うと:Delphiソースには、この問題を引き起こしているバグがあります!

何が起こっているのかを理解しましょう:

Windowsには「Atomテーブル」と呼ばれるメモリ領域があり、アプリケーションが相互に通信するのに役立ちます( 詳細 )。

また、Windowsには同じ目的に役立つ「ウィンドウメッセージシステム」と呼ばれる別の「メモリ領域」があります( 詳細 を参照)。

これらのメモリ領域には、それぞれ「16kスロット」があります。最初の方法では、次のWindows APIを使用して、アトムを[〜#〜] remove [〜#〜]することができます。

GlobalDeleteAtom // for removing an atom added by "GlobalAddAtom"

2番目の「領域」では、何も削除できません何でも!

RegisterWindowMessage関数は通常、2つの連携するアプリケーション間で通信するためのメッセージを登録するために使用されます。 2つの異なるアプリケーションが同じメッセージ文字列を登録すると、アプリケーションは同じメッセージ値を返します。 メッセージはセッションが終了するまで登録されたままです

Delphiでコンパイルされたアプリケーション(少なくともD7による)は、「メッセージングエリア」にレコードを、他のいくつかのレコードを「アトムテーブル」に置きます、いつでも開始されます 。アプリケーションは、アプリが閉じているときにそれらを削除しようとしますが、アプリが閉じられた後でも、多くの(および多くの)「アトムリーク」が見つかりました。

この時点で、1日に数千のアプリを起動するサーバーがある場合、すぐに16kの制限に到達する必要があり、問題が発生することがわかります!この時点での解決策は? 1回の再起動だけです。

だから、私たちは何ができますか?友よ、ごめんなさい。しかし、Delphiソースコードを修正し、すべてのアプリケーションを再コンパイルする必要があります。

まず、Controls.pasユニットを開き、次の行を置き換えます。

RM_GetObjectInstance := RegisterWindowMessage(PChar(ControlAtomString));

ために:

RM_GetObjectInstance := RegisterWindowMessage('RM_GetObjectInstance');

delphiパッケージとアプリケーションを再コンパイルします。

アプリを閉じた後でもatomリークが発生していることを発見したため、残されたatomをガベージコレクションするアプリを作成しました。 :

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;

(上記のこのコードは このコードに基づいていました

その後、このf **エラーが再び発生することはありません!

後期アップデート:

また、それがこのエラーの原因です: Application error:fault address 0x00012afb

22
Christian

Microsoftのデスクトップヒープモニターを使用して、ヒープの統計情報(%などを使用)を表示できます。

http://www.Microsoft.com/downloads/details.aspx?familyid=5cfc9b74-97aa-4510-b4b9-b2dc98c8ed8b&displaylang=en

2
HS1

最近、このエラー(システムエラー。コード:8.ストレージが不足しています...)に気づきました。トウェインコードを使用しているときに、同僚ではなくコンピューターで発生していました。 2番目のモニターとしてのラップトップ画面なので、デスクトップの総寸法は大きくなります。

上記のSteve Blackが指摘した問題のドキュメントを見つけましたが、レジストリを編集する必要のない方法(少なくとも私のマシンのエラーを修正する方法)を見つけました。

古いコードが使用していた

  DC := GetDC(Owner.VirtualWindow);
  // ...
  ReleaseDC(Owner.VirtualWindow, DC);

そして、これでこれを置き換えると私のエラーがなくなることがわかりました

  DC := CreateCompatibleDC(Owner.VirtualWindow);
  // ...
  DeleteDC(DC);


これがあなたの問題に関連しているかどうかはわかりませんが、将来的には他の人にとって役立つかもしれません。

2
jasonpenny

コンパイラにバグが存在する可能性があります。問題の原因はアプリにあるのではないでしょうか。アプリがウィンドウハンドルまたはペン/ブラシなどの他のGUIオブジェクトをリークしている可能性がありますか?これが原因の可能性があります。

0
Aikislave

私にとっては、TJPEGImagesがスライドショー方式で圧縮解除されただけで、最終的にはメモリ不足になりました。

0
Anton Duzenko