zig
zig

Reputation: 4624

How to extend existing interface IMessageFilter with TInterfacedObject?

I want to implement IOleMessageFilter as described here:

How to: Fix 'Application is Busy' and 'Call was Rejected By Callee' Errors

I have found a Delphi implementation which works fine:

`EOleException: Call was rejected by callee` while iterating through `Office.Interop.Word.Documents`

(See UPDATE #1 in the answer)

The implementation looks like this:

type
  TOleMessageFilter = class(TInterfacedObject, IMessageFilter)
  public
    // IMessageFilter
    function HandleInComingCall(dwCallType: Longint; htaskCaller: HTask;
      dwTickCount: Longint; lpInterfaceInfo: PInterfaceInfo): Longint;stdcall;
    function RetryRejectedCall(htaskCallee: HTask; dwTickCount: Longint;
      dwRejectType: Longint): Longint;stdcall;
    function MessagePending(htaskCallee: HTask; dwTickCount: Longint;
      dwPendingType: Longint): Longint;stdcall;

    // TOleMessageFilter
    procedure RegisterFilter;
    procedure RevokeFilter;
  end;

implementation

function TOleMessageFilter.HandleInComingCall(dwCallType: Integer; htaskCaller: HTask; dwTickCount: Integer; lpInterfaceInfo: PInterfaceInfo): Longint;
begin
  Result := 0;
end;

function TOleMessageFilter.MessagePending(htaskCallee: HTask; dwTickCount, dwPendingType: Integer): Longint;
begin
  Result := 2 //PENDINGMSG_WAITDEFPROCESS
end;        

function TOleMessageFilter.RetryRejectedCall(htaskCallee: HTask; dwTickCount, dwRejectType: Integer): Longint;
begin
  Result := -1;
  if dwRejectType = 2 then
    Result := 99;
end;

procedure TOleMessageFilter.RegisterFilter;
var
  OldFilter: IMessageFilter;
  NewFilter: IMessageFilter;
begin
  OldFilter := nil;
  NewFilter := TOleMessageFilter.Create as IMessageFilter;
  CoRegisterMessageFilter(NewFilter,OldFilter);
end;

procedure TOleMessageFilter.RevokeFilter;
var
  OldFilter: IMessageFilter;
  NewFilter: IMessageFilter;
begin
  OldFilter := nil;
  NewFilter := nil;
  CoRegisterMessageFilter(NewFilter,OldFilter);
end;

This exact Delphi code is found on many other sites on the web. So far so good. I have only changed the class name to TOleMessageFilter instead of IOleMessageFilter.

The usage is however a bit annoying.

var
  Filter: TOleMessageFilter;

Filter := TOleMessageFilter.Create;
Filter.RegisterFilter;    
...    
Filter.RevokeFilter;
Filter.Free;

What I want is, Filter to be declared as interface e.g. IOleMessageFilter.

var
  Filter: IOleMessageFilter;

Filter := TOleMessageFilter.Create as IOleMessageFilter;
Filter.RegisterFilter;
...
Filter.RevokeFilter;
Filter := nil;

and have the benefit of auto freeing the TInterfacedObject.

How do I create a new IOleMessageFilter which "derives" from IMessageFilter but still has new methods RegisterFilter() and RevokeFilter(), is implemented as TOleMessageFilter = class(TInterfacedObject, IOleMessageFilter), and still be able to use it also with CoRegisterMessageFilter() which expects IMessageFilter (used in the RegisterFilter() method)?

I have tried to declare:

IOleMessageFilter = interface(IMessageFilter)
  procedure RegisterFilter;
  procedure RevokeFilter;
end;

TOleMessageFilter = class(TInterfacedObject, IOleMessageFilter)
...
end; 

But then calling CoRegisterMessageFilter throws an error:

Interface not supported.

EDIT:

I have also tried to declare TOleMessageFilter as:

TOleMessageFilter = class(TInterfacedObject, IMessageFilter, IOleMessageFilter)

Which "seems" to work, but I'm not sure it's correct approach.

Upvotes: 3

Views: 581

Answers (1)

whosrdaddy
whosrdaddy

Reputation: 11860

Split up both interfaces and let TOleMessageFilter keep a reference to the actual message filter, as a bonus you don't have to call RegisterFilter and RevokeFilter anymore as this will done from constructor/destructor:

program SO46913922;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  ActiveX,
  Windows,
  SysUtils;


type
  IOleMessageFilter = interface
  ['{0ECA5DA7-F6C7-4D21-8FD3-872558F88CBE}']
    procedure RegisterFilter;
    procedure RevokeFilter;
  end;

  TMessageFilter = class(TInterfacedObject, IMessageFilter)
  public
    // IMessageFilter
    function HandleInComingCall(dwCallType: Longint; htaskCaller: HTask;
      dwTickCount: Longint; lpInterfaceInfo: PInterfaceInfo): Longint;stdcall;
    function RetryRejectedCall(htaskCallee: HTask; dwTickCount: Longint;
      dwRejectType: Longint): Longint;stdcall;
    function MessagePending(htaskCallee: HTask; dwTickCount: Longint;
      dwPendingType: Longint): Longint;stdcall;
  end;

  TOleMessageFilter = class(TInterfacedObject, IOleMessageFilter)
  private
    Filter : IMessageFilter;
    procedure RegisterFilter;
    procedure RevokeFilter;
  public
    constructor Create;
    destructor Destroy; override;
  end;


function TMessageFilter.HandleInComingCall(dwCallType: Integer; htaskCaller: HTask; dwTickCount: Integer; lpInterfaceInfo: PInterfaceInfo): Longint;
begin
  Result := 0;
end;

function TMessageFilter.MessagePending(htaskCallee: HTask; dwTickCount, dwPendingType: Integer): Longint;
begin
  Result := 2 //PENDINGMSG_WAITDEFPROCESS
end;

function TMessageFilter.RetryRejectedCall(htaskCallee: HTask; dwTickCount, dwRejectType: Integer): Longint;
begin
  Result := -1;
  if dwRejectType = 2 then
    Result := 99;
end;

procedure TOleMessageFilter.RegisterFilter;
var
  OldFilter: IMessageFilter;

begin
  OldFilter := nil;
  Filter := TMessageFilter.Create;
  CoRegisterMessageFilter(Filter,OldFilter);
end;

procedure TOleMessageFilter.RevokeFilter;
var
  OldFilter: IMessageFilter;
  NewFilter: IMessageFilter;
begin
  OldFilter := nil;
  NewFilter := nil;
  CoRegisterMessageFilter(NewFilter,OldFilter);
  Filter := nil;
end;

constructor TOleMessageFilter.Create;
begin
 RegisterFilter;
end;

destructor TOleMessageFilter.Destroy;
begin
 RevokeFilter;
 inherited;
end;

var
  Filter :  IOleMessageFilter;

begin
  try
   CoInitialize(nil);
   Filter := TOleMessageFilter.Create;
   Readln; // do something
   Filter := nil;
  finally
   CoUninitialize();
  end;
  Readln;
end.

Upvotes: 2

Related Questions