Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't WINWORD.EXE quit after Closing the document from Delphi?

I managed to distill one of the underlying issues rooted in my question How to trace _AddRef / _Release calls for OLE Automation objects in the unit below.

I'll answer this answer too, just in case anyone else bumps into this.

The question: with the below code, why doesn't WINWORD.EXE always quit (sometimes it does quit).

The unit can probably be trimmed down even more.

unit Unit2;

interface

uses
  Winapi.Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls,
  WordXP;

type
  TForm2 = class(TForm)
    WordXPFailsToQuitButton: TButton;
    procedure WordXPFailsToQuitButtonClick(Sender: TObject);
  private
    FWordApplication: TWordApplication;
  strict protected
    function GetWordApplication: TWordApplication; virtual;
    function GetWordApplication_Documents: Documents; virtual;
    procedure WordApplication_DocumentBeforeClose(ASender: TObject; const Doc: _Document; var Cancel: WordBool); virtual;
    procedure WordApplication_Quit(Sender: TObject); virtual;
    property WordApplication: TWordApplication read GetWordApplication;
    property WordApplication_Documents: Documents read GetWordApplication_Documents;
  end;

var
  Form2: TForm2;

implementation

uses
  Vcl.OleServer;

{$R *.dfm}

function TForm2.GetWordApplication: TWordApplication;
begin
  if not Assigned(FWordApplication) then
  begin
    FWordApplication := TWordApplication.Create(nil);

    FWordApplication.AutoConnect := False;
    FWordApplication.AutoQuit := False;
    FWordApplication.ConnectKind := ckNewInstance;
    FWordApplication.OnDocumentBeforeClose := WordApplication_DocumentBeforeClose;
    FWordApplication.OnQuit := WordApplication_Quit;
    FWordApplication.Connect;
  end;
  Result := FWordApplication;
end;

function TForm2.GetWordApplication_Documents: Documents;
begin
  Result := WordApplication.Documents;
  if not Assigned(Result) then
    raise EAccessViolation.Create('WordApplication.Documents');
end;

procedure TForm2.WordXPFailsToQuitButtonClick(Sender: TObject);
begin
  try
    WordApplication_Documents.Add(EmptyParam, EmptyParam, EmptyParam, EmptyParam);
    WordApplication.Visible := True;
    WordApplication.ActiveDocument.Close(False, EmptyParam, EmptyParam);
  finally
    WordApplication.OnQuit := nil;
    WordApplication.OnDocumentBeforeClose := nil;
    WordApplication.AutoQuit := True;
    WordApplication.Disconnect;
    WordApplication.Free;
    FWordApplication := nil;
  end;
end;

procedure TForm2.WordApplication_DocumentBeforeClose(ASender: TObject; const Doc: _Document; var Cancel: WordBool);
begin
  FWordApplication.Disconnect;
end;

procedure TForm2.WordApplication_Quit(Sender: TObject);
begin
  FWordApplication.Disconnect;
end;

end.
like image 413
Jeroen Wiert Pluimers Avatar asked May 25 '13 19:05

Jeroen Wiert Pluimers


1 Answers

Answer part 1:

Comment out the disconnect in the below event:

procedure TForm2.WordApplication_DocumentBeforeClose(ASender: TObject; const Doc: _Document; var Cancel: WordBool);
begin
//  FWordApplication.Disconnect;
end;

The event will be called during the DocumentClose(...) method, then disconnect and delete the OLE interface from the FWordApplication instance.

I have not yet figured out which reference is dangling, but this effectively keeps WINWORD.EXE alive most of the times.

Answer part 2:

Sometimes WINWORD.EXE does quit because toe WordApplication_DocumentBeforeClose event is not called. The reason is that the code runs so fast that Word is not fully initialized yet to perform the event.

like image 163
Jeroen Wiert Pluimers Avatar answered Oct 23 '22 01:10

Jeroen Wiert Pluimers