首页  编辑  

OLE和Office的事件关联

Tags: /超级猛料/Office.OA自动化/   Date Created:

用TWordApplication的方式,有事件支持,但是,我用OLE的时候,如何也能支持事件呢?

请看下面的代码:

uses Word_TLB, activex, comobj, ConnectionObject

// ConnectionObject is the unit containing TWordConnection

// Follow link at the bottom of the page for TWordConnection sample code

procedure StartWordConnection(WordApp: _Application;

                             WordDoc: _Document;

                             var WordSink: TWordConnection);

var

 PointContainer: IConnectionPointContainer;

 Point: IConnectionPoint;

begin

 try

   // TWordConnection is the COM object which receives the

   // notifications from Word. Make sure to free WordSink when

   // you are done with it.

   WordSink := TWordConnection.Create;

   WordSink.WordApp := WordApp;

   WordSink.WordDoc := WordDoc;

   // Sink with a Word application

   OleCheck(WordApp.QueryInterface(IConnectionPointContainer, PointContainer));

   if Assigned(PointContainer) then begin

     OleCheck(PointContainer.FindConnectionPoint(ApplicationEvents, Point));

     if Assigned(Point) then

       Point.Advise((WordSink as IUnknown), WordSink.AppCookie);

   end;

   // Sink with a Word document

   OleCheck(WordDoc.QueryInterface(IConnectionPointContainer, PointContainer));

   if Assigned(PointContainer) then begin

     OleCheck(PointContainer.FindConnectionPoint(DocumentEvents, Point));

     if Assigned(Point) then

       Point.Advise((WordSink as IUnknown), WordSink.DocCookie);

   end;

 except

   on E: Exception do ShowMessage(E.Message);

 end;

end;

//////////////////////////////

unit ConnectionObject;

interface

uses Word_TLB;

type

 TWordConnection = class(TObject, IUnknown, IDispatch)

 protected

   {IUnknown}

   function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;

   function _AddRef: Integer; stdcall;

   function _Release: Integer; stdcall;

   { IDispatch }

   function GetIDsOfNames(const IID: TGUID; Names: Pointer;

     NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;

   function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;

   function GetTypeInfoCount(out Count: Integer): HResult; stdcall;

   function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;

     Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;

  public

    WordApp: _Application;

    WordDoc: _Document;

    AppCookie, DocCookie: Integer;

 end;

implementation

{ IUnknown Methods }

uses windows, activex, main;

procedure LogComment(comment: string);

begin

 Form1.Memo1.Lines.Add(comment);

end;

function TWordConnection._AddRef: Integer;

begin

 Result := 2;

end;

function TWordConnection._Release: Integer;

begin

 Result := 1;

end;

function TWordConnection.QueryInterface(const IID: TGUID;

 out Obj): HResult;

begin

 Result := E_NOINTERFACE;

 Pointer(Obj) := nil;

 if (GetInterface (IID, Obj)) then Result := S_OK;

 if not Succeeded (Result) then

   if (IsEqualIID(IID, DocumentEvents) or IsEqualIID(IID, ApplicationEvents)) then

     if (GetInterface(IDispatch, Obj)) then Result := S_OK;

end;

{ IDispatch Methods }

function TWordConnection.GetIDsOfNames(const IID: TGUID; Names: Pointer;

 NameCount, LocaleID: Integer; DispIDs: Pointer): HResult;

begin

 Result := E_NOTIMPL;

end;

function TWordConnection.GetTypeInfo(Index, LocaleID: Integer;

 out TypeInfo): HResult;

begin

 Pointer(TypeInfo) := nil;

 Result := E_NOTIMPL;

end;

function TWordConnection.GetTypeInfoCount(out Count: Integer): HResult;

begin

 Count := 0;

 Result := E_NOTIMPL;

end;

function TWordConnection.Invoke(DispID: Integer; const IID: TGUID;

 LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo,

 ArgErr: Pointer): HResult;

begin

 //This is the entry point for Word event sinking

 Result := S_OK;

 case DispID of

   1: ; // Startup

   2: ; // Quit

   3: ; // Document change

   4: ; // New document

   5: ; // Open document

   6: ; // Close document

   else Result := E_INVALIDARG;

 end;

end;

end.

使用方法,ConnectionObject是一个单独的单元,在其中的Invoke过程中的case DispID后面,你可以添加你的事件支持代码!如果需要全部的事件常量,请自己查看相关资料即可。

如果要OLE创建的东西支持事件,可以利用StartWordConnection函数来进行关联即可!

************************************

下面是Ex-Ex的解答,比较好的:

http://www.experts-exchange.com/Programming/Programming_Languages/Delphi/Q_10237322.html#1

Title: Word-close event with OLE and dp3.0

From: dr_gonzo

Date: 11/23/1999 04:16AM PST

Answer Grade: A Points: 60

Hi!

I have an app written in Delphi 3.0 that controles an instance of word via OLE. Word does not exactly behave the way one could expect in some cases.

I posted this to the Office group also, but since nowone of you reads those group I add it here too. I'm aware that this is a question that does not really commit to any area. This is the closest though.

I want my word document to tell me when the user has clicked the x-button(or control menu Close[Ctrl+F4].

If I make a template and writes an event handler for the Close-event it works so neat with a normal document, but my OLE-document never sends me any close-event it just kills the window...

How am I supposed to make word let my app know when it is closing rather than when it has closed. I want to be able to pop up some own dialogs before the actual close of the document...

by the way. It's the samething with the dirty-flag. It's always considered to be "False" if it's an OLE-document...

why the hell is that?

What can I do about it? How do I write my own Dirty-flag for a document?  

Accepted Answer from vladika  11/24/1999 03:27AM PST  

BTW when you close document you can process this event in the Delphi.

See example

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

StdCtrls, ComObj, ActiveX;

const

WordDocEventIID: TGUID = '{000209F6-0000-0000-C000-000000000046}';

type

TForm1 = class;

// this class dispatch events from word

TWordDocumentEventDispatcher = class(TObject, IUnknown, IDispatch)

private

  FOwner: TForm1;

  FRefCount : Integer;

protected

  { IUnknown }

  function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;

  function _AddRef: Integer; stdcall;

  function _Release: Integer; stdcall;

  { IDispatch }

  function GetTypeInfoCount(out Count: Integer): HResult; stdcall;

  function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;

  function GetIDsOfNames(const IID: TGUID; Names: Pointer;

    NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;

  function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;

    Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;

public

  constructor Create(AOwner: TForm1);

end;

TForm1 = class(TForm)

  Button1: TButton;

  procedure Button1Click(Sender: TObject);

  procedure FormCreate(Sender: TObject);

  procedure FormDestroy(Sender: TObject);

private

  { Private declarations }

  FDispatcher: TWordDocumentEventDispatcher;

  FConnection: Integer;

  FDoc: Variant;

  procedure OnClose;

  procedure Connect;

  procedure Disconnect;

public

  { Public declarations }

end;

var

Form1: TForm1;

implementation

{$R *.DFM}

{ TWordDocumentEventDispatcher }

constructor TWordDocumentEventDispatcher.Create(AOwner: TForm1);

begin

FOwner := AOwner;

FRefCount := 1;

end;

function TWordDocumentEventDispatcher.QueryInterface(const IID: TGUID; out Obj): HResult;

begin

if GetInterface(IID, Obj) then

begin

  Result := S_OK;

  Exit;

end;

if IsEqualIID(IID, WordDocEventIID) then

begin

  GetInterface(IDispatch, Obj);

  Result := S_OK;

  Exit;

end;

Result := E_NOINTERFACE;

end;

function TWordDocumentEventDispatcher._AddRef: Integer;

begin

FRefCount := FRefCount + 1;

Result := FRefCount;

end;

function TWordDocumentEventDispatcher._Release: Integer;

begin

FRefCount := FRefCount -1;

Result := FRefCount;

end;

function TWordDocumentEventDispatcher.GetTypeInfoCount(out Count: Integer): HResult;

begin

Count := 0;

Result:= S_OK;

end;

function TWordDocumentEventDispatcher.GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult;

begin

Pointer(TypeInfo) := nil;

Result := E_NOTIMPL;

end;

function TWordDocumentEventDispatcher.GetIDsOfNames(const IID: TGUID; Names: Pointer;

NameCount, LocaleID: Integer; DispIDs: Pointer): HResult;

begin

Result := E_NOTIMPL;

end;

function TWordDocumentEventDispatcher.Invoke(DispID: Integer; const IID: TGUID;

LocaleID: Integer; Flags: Word; var Params;

VarResult, ExcepInfo, ArgErr: Pointer): HResult;

begin

try

  case DispID of

   -1: Exit;  // DISPID_UNKNOWN

    6: FOwner.OnClose;

  end;

  Result := S_OK;

except

  Result := S_FALSE;

end;

end;

procedure TForm1.Button1Click(Sender: TObject);

var

WordApp : Variant;

Doc: Variant;

begin

Disconnect;

try

  WordApp := GetActiveOleObject('Word.Application.8');

except on exception do

  WordApp := CreateOleObject('Word.Application.8');

end;

WordApp.Visible := True;

WordApp.Activate;

FDoc := WordApp.Documents.Open('C:\My documents\Test.doc');

Connect;

end;

procedure TForm1.Connect;

begin

InterfaceConnect(FDoc, WordDocEventIID, FDispatcher, FConnection);

end;

procedure TForm1.Disconnect;

begin

InterfaceDisconnect(FDoc, WordDocEventIID, FConnection);

FDoc := Unassigned;

end;

procedure TForm1.FormCreate(Sender: TObject);

begin

FDispatcher := TWordDocumentEventDispatcher.Create(Self);

end;

procedure TForm1.FormDestroy(Sender: TObject);

begin

Disconnect;

FDispatcher.Free;

end;

procedure TForm1.OnClose;

begin

MessageBox(0, 'OnClose', '', 0); // this procedure will be executed when you close document in the Word

end;

Comment from dr_gonzo  11/24/1999 04:14AM PST  

Vladika.

This seems really intresting. I have problems making it work though. It doesn't compile cause Delphi 3.0 sais

"undeclared identifier: InterfaceConnect"

though I include the OleCtrl.pas

that beacuse the function is private.

Could you mail me the example source .zip-ed?

a.ohman@jit.se

This looks intresting. If the event ever go from Word at all that is.

Cause till now it's really look like Word doesn't send any events.

But I'd like to try this one out anyway.

\Dr. Gonzo  

Comment from vladika  11/24/1999 04:47AM PST  

Ok. I sent it.

Just in case I wrote source for this functions.

procedure InterfaceConnect(const Source: IUnknown; const IID: TIID;

const Sink: IUnknown; var Connection: Longint);

var

CPC: IConnectionPointContainer;

CP: IConnectionPoint;

begin

Connection := 0;

if Succeeded(Source.QueryInterface(IConnectionPointContainer, CPC)) then

  if Succeeded(CPC.FindConnectionPoint(IID, CP)) then

    CP.Advise(Sink, Connection);

end;

procedure InterfaceDisconnect(const Source: IUnknown; const IID: TIID;

var Connection: Longint);

var

CPC: IConnectionPointContainer;

CP: IConnectionPoint;

begin

if Connection <> 0 then

  if Succeeded(Source.QueryInterface(IConnectionPointContainer, CPC)) then

    if Succeeded(CPC.FindConnectionPoint(IID, CP)) then

      if Succeeded(CP.Unadvise(Connection)) then Connection := 0;

end;

Comment from RBertora  11/24/1999 04:55AM PST  

Just watching now as you two seem to have things cooking nicely :-)

But I'd be very surprised if you can get D5 to work with D3.

Rob ;-)  

Comment from vladika  11/24/1999 04:57AM PST  

Ok. I sent it.

Just in case I wrote source for this functions.

procedure InterfaceConnect(const Source: IUnknown; const IID: TIID;

const Sink: IUnknown; var Connection: Longint);

var

CPC: IConnectionPointContainer;

CP: IConnectionPoint;

begin

Connection := 0;

if Succeeded(Source.QueryInterface(IConnectionPointContainer, CPC)) then

  if Succeeded(CPC.FindConnectionPoint(IID, CP)) then

    CP.Advise(Sink, Connection);

end;

procedure InterfaceDisconnect(const Source: IUnknown; const IID: TIID;

var Connection: Longint);

var

CPC: IConnectionPointContainer;

CP: IConnectionPoint;

begin

if Connection <> 0 then

  if Succeeded(Source.QueryInterface(IConnectionPointContainer, CPC)) then

    if Succeeded(CPC.FindConnectionPoint(IID, CP)) then

      if Succeeded(CP.Unadvise(Connection)) then Connection := 0;

end;

Comment from vladika  11/24/1999 05:05AM PST  

Sorry for reposting.

To: RBertora

Why not?

I do not use special D5 features, classes or functions. I have not D3 at hand just now. But when I will back to home (4 hours later) I will convert it to D3 certainly. :)

Comment from vladika  11/24/1999 11:08AM PST  

Also look at http://www.intac.com/~bly/com/resources/downloads.htm

"EventSinkImp is a utility that imports sink interfaces from COM server type libraries and automatically produces code for a Delphi non-visual component that publishes the sink methods as Delphi events."