An object that implements interfaces and does not derive from TInterfacedObject

davelane
davelane used Ask the Experts™
on
I am trying to create a thread that implements an interface. Therefore the thread object will have the definition:
  TMyThread = class(TThread, IInterogate)
  ...
  end;

The common practice is to derive the class from TInterfacedObject or TComObject to handle the ref counting and querying. In this case I must however derive from TThread.
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Commented:
You need to implement the following metods in youtr decended tthread object

  TInterfacedThread = class(TThread, IInterface)
  private
    FOwnerInterface: IInterface;
  protected
    { IInterface }
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  public
    function QueryInterface(const IID: TGUID; out Obj): HResult; virtual; stdcall;
    procedure AfterConstruction; override;
  end;


procedure TInterfacedThread.AfterConstruction;
begin
  inherited;
  if GetOwner <> nil then
    GetOwner.GetInterface(IInterface, FOwnerInterface);
end;

function TInterfacedThread._AddRef: Integer;
begin
  if FOwnerInterface <> nil then
    Result := FOwnerInterface._AddRef else
    Result := -1;
end;

function TInterfacedThread._Release: Integer;
begin
  if FOwnerInterface <> nil then
    Result := FOwnerInterface._Release else
    Result := -1;
end;

function TInterfacedThread.QueryInterface(const IID: TGUID;
  out Obj): HResult;
const
  E_NOINTERFACE = HResult($80004002);
begin
  if GetInterface(IID, Obj) then Result := 0 else Result := E_NOINTERFACE;
end;

Commented:
I think you should derive your class from TInterfacedObject or TComObject. If some (or all) of the interface methods should be runned in separate thread, just create a new thread in method implementation.

Commented:
..but you know that every one of your interface methods will be executed in the calling thread, not in your one unless u use some synchronisation?

if the thread should be controlled by the interface, you could, for example, give it two events.

 TInterfacedThread = class(TThread, IInterface)
 private
 ..
 threaddone: integer;
 threadstart: integer;

... a lot of variables which represent the parameters
to the interfaced functions
..for example
 a: integer;
whattodo: integer;
 public
 constructor create;
procedure interfacex(a: integer);
procedure threadx(a: integer);
procedure execute; override;
destructor destroy;
end;

constructor tinterfacethread.create;
begin
inherited create;
..
threaddone = CreateEvent(false, true, nil);
threadstart = CreateEvent(false, false, nil);
end;

procedure interfacex(a: integer);
begin
waitforsingleobject(threaddone, INFINITE);
self.a := a;
setevent(threadstart);
end;

procedure tinterfacethread.threadx(a: integer);
begin
...
WriteLn(a);
...
end;

procedure tinterfacethread.execute;
begin
while not terminated do
begin
waitforsingleobject(threadstart, INFINITE);
 case whattodo of
  1: threadx(a);
  ..
  ..
 end;
setevent(threaddone);
end;

destructor tinterfacethread.destroy;
begin
Closehandle(threaddone);
closehandle(threadstart);
end;

okay, this is complicated at the first glance, but with this code you ensure that all methods are run in the thread.
in the interface expose the interfacex routines.
OWASP: Threats Fundamentals

Learn the top ten threats that are present in modern web-application development and how to protect your business from them.

Author

Commented:
OK, I am a pretty experienced programmer but have not actually done much with threads as of yet. Let me tell you what I was trying to do and see if I am going about it the wrong way:

I am trying to create a kind of supercharged progress indicator and queueing system for tasks. I defined one interface called ITask which has all the methods required for a generic task and one called IProgress which has all the methods required to find out the progress of the task, (sub tasks, % complete, etc.)

I have loads of tasks that do different things, but all implement ITask. I pass a ITask to the TaskThread object which looks after starting and stopping it. The TaskThread implements IProgress, which allows me via a progress window to interrogate it as to what is going on.

Commented:
Ok, as I wrote your TaskThread object should be derived from TInterfacedObject (or TComObject) and StartTask method of the object should open thread for actually provided task object (may be several tasks in same time if you want).

Commented:
if your system should queue the tasks, i would give you task-queueing-object a list, where the itask-interfaces for new tasks are stored.
(new ones added at the end)
than, i would use a mechanism somewhat like i describe below which starts if the list is not empty, and than works through the tasks, deleting every done task from the list.


TInterfacedThread = class(TThread, IInterface)
private
mytasks : array of itask;
syncevent: cardinal;
public
procedure execute; override;
constructor create; override;
destructor destroy; override;
procedure addtask(x: itask);
end;

constructor TInterfacedThread.create;
begin
syncevent := CreateEvent(false, true, false);
mytasks := nil;
end;

destructor TInterfacedThread.destroy;
begin
closehandle(syncevent);
end;

procedure TInterfacedThread.tintaddtask(x: itask)
begin
waitforsingleobject(syncevent, infinite);
setlength(mytasks, length(mytasks)+1));
mytasks[high(mytasks)] := x;
setevent(syncvent);
end;

procedure tInterfacedThread.execute;
begin
while not terminated do
 begin
 if(length(mytasks)>0)
   begin
   mytasks[0].execute;
   waitforsingleobject(syncevent, infinite);
   mytasks[0] := nil;
   move(@mytasks[0], @mytasks[1], (length(mytasks)-1)*4);
   setlength(mytasks, length(mytasks)-1);
   setevent(syncevent);
 end;
end;

with a code like this, all tasks will be added and then executed one after the other.
(if that's what you want)

Commented:
I don't understand where your problem is.

Thread classes can implement interfaces just as
any other classes can - provided it isn't COM class!

I have written an app that uses loads of TSMSThread classes
which each implement an ISMSThread interface which
exposes methods like Start, Stop, Pause, Get_Status etc.

I recommend that you do NOT use reference counting with
threads. Handle thread creation and destruction yourself.
Simply declare your IProgress interface without descending
from IInterface/IUnknown. Manually create, manage and destroy
your thread classes.

rondi

Author

Commented:
How do you define an interface that does not descend from IUnknown / IInterface? I thought by default all do?

Commented:
in fact all do.
nobody should ever consider to make an interface not inherited from iunknown(=iinterface).
but knowing that an interface is just pointer to an array of pointers to methods, one could (but however, should not) backdoor this definition.

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial