0

Delphi2007で記述されたMDIアプリケーションがあります。

コードの実行中にユーザーがフォームを終了すると、例外が発生します。これは、コードがコンポーネントを更新しようとしている、またはフォームで解放されたオブジェクトを使用しようとしているためです。

とにかく、終了イベントでコードが実行されているかどうかを知ることができますか、それともこの状況に対処するための標準的な方法がありますか?

より多くの情報で更新

例外は通常、次の状況で発生します。

子mdiフォームのボタンを押すと、フォーム内の関数がアクティブになります。関数はデータベースに移動してデータを取得し、再フォーマットしてフォームのビジュアルコンポーネント(TListViewを使用可能)に表示します。 )。

コードの実行に時間がかかる場合(処理するデータが多い場合など)、ユーザーは興味を失い、閉じるボタンをクリックします(これを回避するためにコードの速度が調整されています)。

関数内のコードは、それが属するフォームが解放されていても(コードはフォームのプライベートセクションにあります)、ビジュアルコンポーネントを更新しようとすると、存在しなくなります(解放されたため)。フォーム)そしてそれは例外をスローします。

子フォームのコードは、これが発生したときに通常はループ内にあり、レコードを循環させ、それに応じてリストビューを更新します。ループには、次のようなコードが含まれています。

inc(i);
if (i mod 25) = 0 then
begin
    StatusPnl.Caption := 'Loading ' + intToStr(i) + ', Please wait';
    application.ProcessMessages;
end;

その他のコードサンプル

fromCloseイベントは次のようになります

//Snip
if (Not (Owner = nil)) then
with (Owner as  IMainForm)do
begin
    //Snip
    DoFormFree(Self,Self.Name);
end
else
//Snip

DoFormFreeは、メインのmdi親フォームの関数であり、次のようになります。

//Snip
(G_FormList.Objects[x] as TBaseForm).Release;
G_FormList.Objects[i] := nil;
G_FormList.Delete(i);
//Snip

さまざまな理由から、すべてのフォームはリストに格納され、すべての子フォームはTBaseFormクラスを拡張します。

理想的には、フォーム内のコードが実行されているかどうかを確認し、ユーザーがフォームを閉じないようにするか、コードが終了するまで非表示にする方法が必要です。場合によっては、レポートが生成され、ステータスパネルとして更新されることがあります。例外が発生します。その場合、レポートは不完全になります。

すべてのフォームはTbaseのサブクラスであるため、これを行うためのグローバルな方法が理想的であるため、コードをベースフォームに追加して、すべての子孫フォームで機能させることができます。

4

5 に答える 5

4

十分な情報を提供していませんが、頭に浮かぶ最も簡単な解決策は、コードが実行されているかどうかをOnCloseQueryハンドラーでテストし、実行されている場合はCanCloseをFalseに設定することです。

または、フォームとバックグラウンドコードの両方が認識している中間オブジェクトを作成することにより、コードをMDIフォームから切り離すことができます。これにフォームへの参照を持たせ、フォームを閉じるとリセットされます。この中間オブジェクトを介してフォームへのすべてのアクセスをルーティングすることにより、例外を防ぐことができます。

編集:解放された後にMDIフォームにアクセスしようとするコードを実行する方法に関する情報を提供する必要があります。ワーカーコードを実行するには、次のような方法があります。

  • フォームまたは別のオブジェクトのメソッドで
  • OnTimerイベントハンドラー内
  • ApplicationオブジェクトのOnIdleハンドラー内
  • バックグラウンドスレッドで

最初のケースでは、コードで自分で行うか、Application.ProcessMessagesを呼び出す場合にのみ、フォームを解放できることに注意してください。あなたのコードがどのように見えるかについてのより多くの情報がなければ、誰もあなたにあなたの質問に対する特定の答えを与えることができません。

編集2:追加情報を使用すると、問題のコードは常にフォームのメソッドで実行されるようです。これは、実行の開始時にTrueに設定され、実行の終了時にFalseに設定されるブールメンバーを作成することで簡単にキャッチできます。これで、基本クラスにOnCloseQueryのハンドラーを追加するだけで、メンバー(fExecutingなど)がTrueの場合はCanCloseをFalseに設定できます。黙って閉じることを禁止したり、情報ボックスを表示したりできます。モーダル情報ボックスでユーザーの邪魔をしすぎないように、進行状況フォームを表示するか、ステータスバーに何かを表示します。

私が間違いなく行うことは、ユーザーが長時間実行されているプロセスをキャンセルできるようにすることです。したがって、操作をキャンセルして閉じるかどうかをユーザーに尋ねるメッセージボックスを表示することもできます。それでもフォームの終了をスキップする必要がありますが、終了するリクエストを保存し、実行が終了したら処理することができます。

于 2009-01-26T09:30:45.587 に答える
0

各フォームにはOnCloseQueryイベントがあります。

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);

CanCloseをFalseに設定することにより、これを使用してクローズを延期できます。

処理が完了するまでクローズを処理するかどうかを決定する必要があります。または、ユーザーに再度閉じるように要求する場合があります。

于 2009-01-26T09:30:06.443 に答える
0

MDI 形式でプライベート フィールドを導入します。F処理中

データベース呼び出しコードで次のことを行います。

FProcessing := true;
try
  i := 0;  
  if (i mod 25) = 0 then
  begin
    // do your code 
    Application.ProcessMessages; 
  end;
finally
  FProcessing := false; 
end;

MDIForm.FormCloseQuery()で行う

procedure TMDIForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  if FProcesing then 
    CanClose := False;  
   // or you can ask user to stop fetching db data
end;

アプリ全体の終了も確認する必要があります。

于 2009-01-26T11:24:39.553 に答える
0

スレッドを使用せずにプロシージャまたはメソッドを実行できるオブジェクトを作成しました。タイマーを使用しますが、単純な 1 行の呼び出しのみを公開します。また、RTTI もサポートしているため、次のいずれかのボタンをクリックするだけで済みます。

ExecuteMethodProc( MyCode ) または ExecuteMethodName( 'MyCode' );

よろしく、ブライアン

// Method execution
//-----------------------------------------------------------------------------

type
  TArtMethodExecuter = class( TObject )
    constructor Create;
    destructor  Destroy; override;
  PRIVATE

    FMethod           : TProcedureOfObject;
    FTimer            : TTimer;
    FBusy             : boolean;
    FFreeAfterExecute : boolean;
    FHandleExceptions : boolean;

    procedure DoOnTimer( Sender : TObject );
    procedure SetBusy( AState : boolean );

  PUBLIC
    procedure ExecuteMethodProc(
                AMethod       : TProcedureOfObject;
                AWait         : boolean = False );

    procedure ExecuteMethodName(
                AMethodObject : TObject;
          const AMethodName   : string;
                AWait         : boolean = False );

    property  FreeAfterExecute : boolean
                read FFreeAFterExecute
                write FFreeAfterExecute;

    property  HandleExceptions : boolean
                read FHandleExceptions
                write FHandleExceptions;

    property  Busy : boolean
                read FBusy;

  end;





procedure ExecuteMethodName(
            AMethodObject : TObject;
     const  AMethodName    : string;
            AHandleExceptions : boolean = True );
// Executes this method of this object in the context of the application.
// Returns immediately, with the method executing shortly.

procedure ExecuteMethodProc(
            AMethodProc : TProcedureOfObject;
            AHandleExceptions : boolean = True );
// Executes this method of this object in the context of the application.
// Returns immediately, with the method executing shortly.

function  IsExecutingMethod : boolean;
// Returns TRUE if we are already executing a method.


// End method execution
//-----------------------------------------------------------------------------




// Method execution
//-----------------------------------------------------------------------------


{ TArtMethodExecuter }

var
  iMethodsExecutingCount : integer = 0;

const
  wm_ExecuteMethod = wm_User;

constructor TArtMethodExecuter.Create;
begin
  Inherited;
end;

destructor TArtMethodExecuter.Destroy;
begin
  FreeAndNil( FTimer );
  Inherited;
end;

procedure TArtMethodExecuter.DoOnTimer( Sender : TObject );

  procedure RunMethod;
  begin
    try
      FMethod
    except
      on E:Exception do
        ArtShowMessage( E.Message );
    end
  end;

begin
  FreeAndNil(FTimer);
  try
    If Assigned( FMethod ) then
      RunMethod
     else
      Raise EArtLibrary.Create(
        'Cannot execute method - no method defined.' );
  finally
    SetBusy( False );
    If FFreeAfterExecute then
      Free;
  end;
end;



procedure TArtMethodExecuter.SetBusy(AState: boolean);
begin
  FBusy := AState;

  If AState then
    Inc( iMethodsExecutingCount )
   else
    If iMethodsExecutingCount > 0 then
      Dec( iMethodsExecutingCount )
end;



procedure TArtMethodExecuter.ExecuteMethodProc(
          AMethod       : TProcedureOfObject;
          AWait         : boolean = False );
begin
  SetBusy( True );
  FMethod         := AMethod;
  FTimer          := TTimer.Create( nil );
  FTimer.OnTimer  := DoOnTimer;
  FTimer.Interval := 1;
  If AWait then
    While FBusy do
      begin
      Sleep( 100 );
      Application.ProcessMessages;
      end;
end;



procedure TArtMethodExecuter.ExecuteMethodName(AMethodObject: TObject;
  const AMethodName: string; AWait: boolean);
var
  RunMethod : TMethod;
begin
  RunMethod.code := AMethodObject.MethodAddress( AMethodName );
  If not Assigned( RunMethod.Code ) then
    Raise EArtLibrary.CreateFmt(
      'Cannot find method name "%s". Check that it is defined and published.', [AMethodName] );

  RunMethod.Data := AMethodObject;
  If not Assigned( RunMethod.Data ) then
    Raise EArtLibrary.CreateFmt(
      'Method object associated with method name "%s" is not defined.', [AMethodName] );

  ExecuteMethodProc(
    TProcedureOfObject( RunMethod ),
    AWait );
end;


procedure ExecuteMethodName(
            AMethodObject : TObject;
      const AMethodName   : string;
            AHandleExceptions : boolean = True );
// Executes this method of this object in the context of the application.
// Returns immediately, with the method executing shortly.
var
  ME : TArtMethodExecuter;
begin
  If IsExecutingMethod then
    If AHandleExceptions then
      begin
      ArtShowMessage( 'A method is already executing.' );
      Exit;
      end
     else
      Raise EArtLibrary.Create( 'A method is already executing.' );

  ME := TArtMethodExecuter.Create;
  ME.FreeAfterExecute := True;
  ME.HandleExceptions := AHandleExceptions;
  ME.ExecuteMethodName( AMethodObject, AMethodName );
end;


procedure ExecuteMethodProc(
            AMethodProc : TProcedureOfObject;
            AHandleExceptions : boolean = True );
// Executes this method of this object in the context of the application.
// Returns immediately, with the method executing shortly.
var
  ME : TArtMethodExecuter;
begin
  If IsExecutingMethod then
    If AHandleExceptions then
      begin
      ArtShowMessage( 'A method is already executing.' );
      Exit;
      end
     else
      Raise EArtLibrary.Create( 'A method is already executing.' );

  ME := TArtMethodExecuter.Create;
  ME.FreeAfterExecute := True;
  ME.HandleExceptions := AHandleExceptions;
  ME.ExecuteMethodProc( AMethodProc );
end;

function  IsExecutingMethod : boolean;
// Returns TRUE if we are already executing a method.
begin
  Result := iMethodsExecutingCount > 0;
end;

// End Method execution
//-----------------------------------------------------------------------------
于 2009-01-26T12:00:21.320 に答える
0

操作に時間がかかりすぎるためにユーザーがあきらめたい場合、ユーザーも許可しないのはなぜですか? コードを少し変更して (application.process メッセージが適切な場所である直前に) 「wants to quit」変数をチェックし、それが true の場合はループから抜け出し、オブジェクトを解放してキャンセルします。次に、これをdmajkicが以前に提案したものでラップします。

于 2009-01-26T19:42:44.587 に答える