2009-01-26 53 views
0

執行我在Delphi編寫的2007年如何正常退出有代碼MDI形式德爾福

一個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母體形式的一個函數d看起來像這樣

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

所有形式存儲在列表中,因爲種種原因,以及所有子窗體擴展TBaseForm類。

理想情況下,我想一個辦法判斷的形式執行代碼,並防止用戶關閉窗體或隱藏,直到代碼完成,因爲在某些情況下,可能生成報表和更新作爲狀態面板,當發生異常時,報告將不完整。

因爲所有的表單都是TbaseFrom的子類,所以這樣做的一些全局方式將是理想的,所以我可以將代碼添加到基本表單並使其適用於所有下降的表單。

回答

4

您提供的信息不足,但想到的最簡單的解決方案是在OnCloseQuery處理程序中測試代碼是否正在執行,如果是,則將CanClose設置爲False。

或者,您可以通過創建表單和後臺代碼都知道的中間對象,從MDI表單分離代碼。你讓它有一個表單的引用,它在表單關閉時被重置。通過將所有對錶單的訪問路由到該中間對象,您可以防止出現異常。

編輯:您需要提供有關如何執行試圖在釋放MDI表單後訪問MDI表單的代碼的信息。有一些方法來執行工人代碼,例如:

  • 中或另一對象在應用程序對象的的OnIdle處理程序OnTimer事件處理程序
  • 的形式的方法
  • 在一個後臺線程

請注意,在第一種情況下,如果您自己在代碼中執行它,或者如果調用Application.ProcessMessages,則只能釋放窗體。沒有關於你的代碼的更多信息,沒有人可以爲你的問題給出具體的答案。

編輯2:隨着您添加的信息,似乎有問題的代碼總是在窗體的方法中執行。這很容易通過創建布爾成員在開始執行時設置爲True,並且在執行完成時設置爲False來實現。現在,您只需要在基類中爲OnCloseQuery添加處理程序,並且如果成員(例如fExecuting)爲True,則將CanClose設置爲False。您可以默默禁止關閉,或顯示信息框。我只是簡單地顯示進度表或在狀態欄中顯示某些內容,以免用模態信息框過多地中斷用戶。

我絕對會做的是讓用戶取消長時間運行的過程。因此,您還可以顯示一個消息框,詢問用戶是否要取消操作並關閉。您仍然需要跳過表單的關閉,但可以將請求存儲爲關閉,並在執行結束後進行處理。

+0

謝謝,我還需要提供什麼? – Re0sless 2009-01-26 09:44:33

0

每個窗體都有一個OnCloseQuery事件。

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

您可以通過將CanClose設置爲False來延遲關閉。

您需要決定是否要處理關閉,直到處理完成。或者您可能需要用戶再次關閉。

0

以MDI形式引入私人領域例如。 FProcessing

在DB調用代碼做:

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; 

你應該ASLO檢查整個應用程序terminaation。

0

我創建了一個對象,可以在不使用線程的情況下爲您執行過程或方法。它使用計時器,但只顯示簡單的一個線路呼叫。它還支持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 
//----------------------------------------------------------------------------- 
0

如果用戶想放棄,因爲操作這麼長時間,他們爲什麼不讓他們呢?稍微修改你的代碼來檢查(在application.process消息是一個好地方之前)一個「想要退出」的變量,如果它是真的,那麼從循環中保釋,釋放你的對象並取消。然後把它放在前面提到的dmajkic中。