Search code examples
delphiinterfacedelphi-7

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.


Solution

  • 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.