Board index » delphi » Is Refcounting difference in D5

Is Refcounting difference in D5

Sorry if if this is lame but has the basic reference counting mechanism
changed in D5.

The reason I ask is that I have some components that wrap COM objects.
Typically I create the COM object with cocreateinstance in the wrappers
constructor. Along the way I may create additional interface varaibles using
queryinterface (or as) on this objects initial interface.

Prior to D5 in the destructor for my wrapper I would systimatically set
these various interfaces to nil and then call the final destroy. This worked
fine in D4 but in D5 when I have a form open that contains the control and
then I shut down the IDE I regularly get (but no all way) first a

Quote
>>This program has performed an illegal operation and will be shut down<<
followed by a
>>Runtime error 216 at xxxxxxxxxx.<<

If I eliminate setting the interfaces to nil in the destructor these
shutdown errors go away.

Does this make sense? Should I really not be setting interfaces to nil at
destruction and is this a change or have I always been doing things wrong?

TIA

 

Re:Is Refcounting difference in D5


Quote
> Sorry if if this is lame but has the basic reference
> counting mechanism changed in D5.

No, though we've noticed that D5 fixes some ref-counting bugs present in D4.

Quote
> Does this make sense?

No, sorry, I suspect the problem is deeper than what you've described, and
is likely a bug in your code somewhere.

Quote
> Should I really not be setting interfaces to nil at destruction

You really should set interfaces to nil at destruction, as has always been
the case.
--
Rick Rogers (TeamB)
www.componentfactory.com

Re:Is Refcounting difference in D5


Quote
> No, sorry, I suspect the problem is deeper than what you've described, and
> is likely a bug in your code somewhere.

<sigh> yes well I guess deep down I already knew that.

What I'm doing is building a fancy VCL wrapper around the MSAgent server
object (not the ActiveX client but the server). I've implemented the event
sink as in the folloing code and I'm pretty sure the problem ls with my
implementation of the event sink object/Interface because as long as I don't
create and register the sink then unregister and nil I never get the IDE
shut down errors . What I suspect most is what I've done with the IDispatch
methods.

I'd be really grateful if you or someone else here could comment on my code
and tell me where I went wrong.

  ToaAgentNotifySink = class(TInterfacedObject, IAgentNotifySink)
  private
    fAgent: ToaAgent;
  protected
    {IDispatch methods}
    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;
    {IAgentNotifySink methods}
    procedure Command(dwCommandID: Integer; const punkUserInput: IUnknown);
SafeCall;
    procedure ActivateInputState(dwCharID: Integer; bActivated: Integer);
SafeCall;
    procedure Restart; SafeCall;
    procedure Shutdown; SafeCall;
    procedure VisibleState(dwCharID: Integer; bVisible: Integer; dwCause:
Integer); SafeCall;
    procedure Click(dwCharID: Integer; fwKeys: Smallint; x: Integer; y:
Integer); SafeCall;
    procedure DblClick(dwCharID: Integer; fwKeys: Smallint; x: Integer; y:
Integer); SafeCall;
    procedure DragStart(dwCharID: Integer; fwKeys: Smallint; x: Integer; y:
Integer); SafeCall;
    procedure DragComplete(dwCharID: Integer; fwKeys: Smallint; x: Integer;
y: Integer); SafeCall;
    procedure RequestStart(dwRequestID: Integer); SafeCall;
    procedure RequestComplete(dwRequestID: Integer; hrStatus: Integer);
SafeCall;
    procedure BookMark(dwBookMarkID: Integer); SafeCall;
    procedure Idle(dwCharID: Integer; bStart: Integer); SafeCall;
    procedure Move(dwCharID: Integer; x: Integer; y: Integer; dwCause:
Integer); SafeCall;
    procedure Size(dwCharID: Integer; lWidth: Integer; lHeight: Integer);
SafeCall;
    procedure BalloonVisibleState(dwCharID: Integer; bVisible: Integer);
SafeCall;
  public
    constructor create(Agent: ToaAgent);
  end;

Implementation

constructor ToaAgentNotifySinkEx.create(Agent: ToaAgent);
begin
  inherited create;
  fAgent := Agent;
end;

function ToaAgentNotifySinkEx.GetTypeInfoCount(out Count: Integer): HResult;
begin
  Count := 1;
  Result := S_OK;
end;

function ToaAgentNotifySinkEx.GetTypeInfo(Index, LocaleID: Integer; out
TypeInfo): HResult;
var
  Lib: ITypeLib;
begin
  Pointer(TypeInfo) := nil;
  if Index <> 0 then
  begin
    Result := TYPE_E_ELEMENTNOTFOUND;
    Exit;
  end;
  result := LoadRegTypeLib(LIBID_AgentServerObjects, 1, 0, 0, Lib);
  If result <> S_OK then exit;
  result := Lib.GetTypeInfoOfGuid(IID_IAgentNotifySink,
ITypeInfo(TypeInfo));
end;

function ToaAgentNotifySinkEx.GetIDsOfNames(const IID: TGUID; Names:
Pointer;
  NameCount, LocaleID: Integer; DispIDs: Pointer): HResult;
var
  Info : ITypeInfo;
begin
  // IID must be NULL
  if not IsEqualIID(IID, GUID_NULL) then
  begin
    result := HResult(DISP_E_UNKNOWNINTERFACE);
    exit;
  end;
  // Get the TypeInfo for the specified lcid
  result := GetTypeInfo(0, LocaleID, Info);
  if result <> S_OK then exit;
  // Use the TypeInfo to get the DISPIDs of the specified names.
  // That's the whole point here.  Let TypeInfo do the work so
  // we don't have to.
  result := Info.GetIDsOfNames(Names, NameCount, DispIDs);
end;

function ToaAgentNotifySinkEx.Invoke(DispID: Integer; const IID: TGUID;
LocaleID: Integer;
  Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult;
var
  Info : ITypeInfo;
begin
  // The iid parameter is always supposed to be NULL
  if not IsEqualIID(IID, GUID_NULL) then
  begin
    result := HResult(DISP_E_UNKNOWNINTERFACE);
    exit;
  end;
  // Get the TypeInfo for the specified lcid
  result := GetTypeInfo(0, LocaleID, Info);
  if result <> S_OK then exit;
  // Clear exceptions
  SetErrorInfo(0, nil);
  Result := Info.Invoke(pointer(integer(Self)), dispID, Flags,
TDispParams(Params), VarResult,
    ExcepInfo, ArgErr);
end;

procedure ToaAgentNotifySink.Command(dwCommandID: Integer; const
punkUserInput: IUnknown);
begin
  fAgent.Command(dwCommandID, punkUserInput);
end;

procedure ToaAgentNotifySink.ActivateInputState(dwCharID: Integer;
bActivated: Integer);
begin
  fAgent.ActivateInputState(dwCharID, bActivated);;
end;

procedure ToaAgentNotifySink.Restart;
begin
  // no longer suppoerted by MSAgent
  ShowMessage('Restart event, this should never fire');
end;

procedure ToaAgentNotifySink.Shutdown;
begin
  // no longer suppoerted by MSAgent
  ShowMessage('Shutdown event, this should never fire');
end;

procedure ToaAgentNotifySink.VisibleState(dwCharID: Integer; bVisible:
Integer; dwCause: Integer);
begin
  fAgent.VisibleState(dwCharID, bVisible, dwCause);
end;

procedure ToaAgentNotifySink.Click(dwCharID: Integer; fwKeys: Smallint; x:
Integer; y: Integer);
begin
  fAgent.Click(dwCharID, fwKeys, x, y);
end;

procedure ToaAgentNotifySink.DblClick(dwCharID: Integer; fwKeys: Smallint;
x: Integer; y: Integer);
begin
  fAgent.DblClick(dwCharID, fwKeys, x, y);
end;

procedure ToaAgentNotifySink.DragStart(dwCharID: Integer; fwKeys: Smallint;
x: Integer; y: Integer);
begin
  fAgent.DragStart(dwCharID, fwKeys, x, y);
end;

procedure ToaAgentNotifySink.DragComplete(dwCharID: Integer; fwKeys:
Smallint; x: Integer; y: Integer);
begin
  fAgent.DragComplete(dwCharID, fwKeys, x, y);
end;

procedure ToaAgentNotifySink.RequestStart(dwRequestID: Integer);
begin
  fAgent.RequestStart(dwRequestID);
end;

procedure ToaAgentNotifySink.RequestComplete(dwRequestID: Integer; hrStatus:
Integer);
begin
  fAgent.RequestComplete(dwRequestID, hrStatus);
end;

procedure ToaAgentNotifySink.BookMark(dwBookMarkID: Integer);
begin
  fAgent.BookMark(dwBookMarkID);
end;

procedure ToaAgentNotifySink.Idle(dwCharID: Integer; bStart: Integer);
begin
  fAgent.Idle(dwCharID, bStart);
end;

procedure ToaAgentNotifySink.Move(dwCharID: Integer; x: Integer; y: Integer;
dwCause: Integer);
begin
  fAgent.Move(dwCharID, x, y, dwCause);
end;

procedure ToaAgentNotifySink.Size(dwCharID: Integer; lWidth: Integer;
lHeight: Integer);
begin
  fAgent.SizeChange(dwCharID, lWidth, lHeight);
end;

procedure ToaAgentNotifySink.BalloonVisibleState(dwCharID: Integer;
bVisible: Integer);
begin
   fAgent.BalloonVisibleState(dwCharID, bVisible);
end;

Re:Is Refcounting difference in D5


Try adding IDispatch support.

Quote
>  ToaAgentNotifySink = class(TInterfacedObject, IAgentNotifySink,

IDispatch)

Re:Is Refcounting difference in D5


Quote
> Try adding IDispatch support.

> >  ToaAgentNotifySink = class(TInterfacedObject, IAgentNotifySink,
> IDispatch)

Is this true even though IAgentNotifySink is already defined as

  IAgentNotifySink = interface(IDispatch)
    ['{00D18159-8466-11D0-AC63-00C04FD97575}']
    procedure Command(dwCommandID: Integer; const punkUserInput: IUnknown);
safecall;
    procedure ActivateInputState(dwCharID: Integer; bActivated: Integer);
safecall;
    procedure Restart; safecall;
    procedure Shutdown; safecall;
    procedure VisibleState(dwCharID: Integer; bVisible: Integer; dwCause:
Integer); safecall;
    procedure Click(dwCharID: Integer; fwKeys: Smallint; x: Integer; y:
Integer); safecall;
    procedure DblClick(dwCharID: Integer; fwKeys: Smallint; x: Integer; y:
Integer); safecall;
    procedure DragStart(dwCharID: Integer; fwKeys: Smallint; x: Integer; y:
Integer); safecall;
    procedure DragComplete(dwCharID: Integer; fwKeys: Smallint; x: Integer;
y: Integer); safecall;
    procedure RequestStart(dwRequestID: Integer); safecall;
    procedure RequestComplete(dwRequestID: Integer; hrStatus: Integer);
safecall;
    procedure BookMark(dwBookMarkID: Integer); safecall;
    procedure Idle(dwCharID: Integer; bStart: Integer); safecall;
    procedure Move(dwCharID: Integer; x: Integer; y: Integer; dwCause:
Integer); safecall;
    procedure Size(dwCharID: Integer; lWidth: Integer; lHeight: Integer);
safecall;
    procedure BalloonVisibleState(dwCharID: Integer; bVisible: Integer);
safecall;
  end;

In any case I tried it and it didn't seem to help, or at least I still get
the same error.

In my GetTypeInfo I'm getting an ITypeInfo using GetTypeInfoOfGuid. It
occurs that since this is a non-delphi way of getting an interface that
maybe I'm supposed to do an explicit _AddRef on the returned ITypeInfo
pointer. Does that make sense?

Quote
----- Original Message -----
From: Azret <az...@ibm.remove.net>

Newsgroups: borland.public.delphi.oleautomation
Sent: Sunday, December 19, 1999 6:52 PM
Subject: Re: Is Refcounting difference in D5

> Try adding IDispatch support.

> >  ToaAgentNotifySink = class(TInterfacedObject, IAgentNotifySink,
> IDispatch)

Re:Is Refcounting difference in D5


Quote
> Try adding IDispatch support.

> >  ToaAgentNotifySink = class(TInterfacedObject, IAgentNotifySink,
> IDispatch)

Is this true even though IAgentNotifySink is already defined as

  IAgentNotifySink = interface(IDispatch)
    ['{00D18159-8466-11D0-AC63-00C04FD97575}']
    procedure Command(dwCommandID: Integer; const punkUserInput: IUnknown);
safecall;
    procedure ActivateInputState(dwCharID: Integer; bActivated: Integer);
safecall;
    procedure Restart; safecall;
    procedure Shutdown; safecall;
    procedure VisibleState(dwCharID: Integer; bVisible: Integer; dwCause:
Integer); safecall;
    procedure Click(dwCharID: Integer; fwKeys: Smallint; x: Integer; y:
Integer); safecall;
    procedure DblClick(dwCharID: Integer; fwKeys: Smallint; x: Integer; y:
Integer); safecall;
    procedure DragStart(dwCharID: Integer; fwKeys: Smallint; x: Integer; y:
Integer); safecall;
    procedure DragComplete(dwCharID: Integer; fwKeys: Smallint; x: Integer;
y: Integer); safecall;
    procedure RequestStart(dwRequestID: Integer); safecall;
    procedure RequestComplete(dwRequestID: Integer; hrStatus: Integer);
safecall;
    procedure BookMark(dwBookMarkID: Integer); safecall;
    procedure Idle(dwCharID: Integer; bStart: Integer); safecall;
    procedure Move(dwCharID: Integer; x: Integer; y: Integer; dwCause:
Integer); safecall;
    procedure Size(dwCharID: Integer; lWidth: Integer; lHeight: Integer);
safecall;
    procedure BalloonVisibleState(dwCharID: Integer; bVisible: Integer);
safecall;
  end;

In any case I tried it and it didn't seem to help, or at least I still get
the same error.

In my GetTypeInfo I'm getting an ITypeInfo using GetTypeInfoOfGuid. It
occurs that since this is a non-delphi way of getting an interface that
maybe I'm supposed to do an explicit _AddRef on the returned ITypeInfo
pointer. Does that make sense?

Quote
----- Original Message -----
From: Azret <az...@ibm.remove.net>

Newsgroups: borland.public.delphi.oleautomation
Sent: Sunday, December 19, 1999 6:52 PM
Subject: Re: Is Refcounting difference in D5

> Try adding IDispatch support.

> >  ToaAgentNotifySink = class(TInterfacedObject, IAgentNotifySink,
> IDispatch)

Re:Is Refcounting difference in D5


What happens if you call CoInitialize (NIL) in your ctor and call
CoUninitialize in your dtor *after* you have NILed out all reference
pointers to contained objects?

--
Binh Ly
Need help on COM development?
http://www.techvanguards.com

Quote
Alec Bergamini <al...@o2a.com> wrote in message

news:83ga9p$ir13@forums.borland.com...
Quote
> Sorry if if this is lame but has the basic reference counting mechanism
> changed in D5.

> The reason I ask is that I have some components that wrap COM objects.
> Typically I create the COM object with cocreateinstance in the wrappers
> constructor. Along the way I may create additional interface varaibles
using
> queryinterface (or as) on this objects initial interface.

> Prior to D5 in the destructor for my wrapper I would systimatically set
> these various interfaces to nil and then call the final destroy. This
worked
> fine in D4 but in D5 when I have a form open that contains the control and
> then I shut down the IDE I regularly get (but no all way) first a
> >>This program has performed an illegal operation and will be shut down<<
> followed by a
> >>Runtime error 216 at xxxxxxxxxx.<<
> If I eliminate setting the interfaces to nil in the destructor these
> shutdown errors go away.

> Does this make sense? Should I really not be setting interfaces to nil at
> destruction and is this a change or have I always been doing things wrong?

> TIA

Re:Is Refcounting difference in D5


Hi Binh, Azret, Rick .  and everyone else who might be able to help here.

I tried explicitly doing the CoInitialize and CoUninitialize as suggested
but still not satisfaction. This is pretty frustrating, I can't remember
when I had a bug that took so long to kill. (I had already been at it for at
least a week before I posted the first message here).

I was wondering if there might be some knowledgeble (and brave) COM person
who would be willing to take a look at this component. If so I could email
the source to you. Its not you average component in that its really designed
to be more fun than useful. I had planned it as a christmas present to our
customers but ..... Anyway, its basically a wrapper around the MSAgent
server (which is supposed to be more efficient than the AX version). Its got
a component editor that helps you download the various Agent parts if you
don't already have them.

I would really appreciate any help here. I just know that its something
really simple and stupid, its just that I've been staring at this so long
I'm just not getting it.

As a small incentive and reward for a solution I'm offering a free copy of
our DTalk3 speech enabling components set.

Quote
Binh Ly <b...@castle.net> wrote in message

news:83mq1r$7fk6@forums.borland.com...
Quote
> What happens if you call CoInitialize (NIL) in your ctor and call
> CoUninitialize in your dtor *after* you have NILed out all reference
> pointers to contained objects?

> --
> Binh Ly
> Need help on COM development?
> http://www.techvanguards.com

Re:Is Refcounting difference in D5


Here's a bit more info just in case anyones still following this thread.

Apparently I only get the IDE shutdown errors if a form with my component is
open and that form contains two or more of this component.

If the form is not open then I get no error and if the form only contains
one instance of my component then I get no error.

Does this suggest anything?

Re:Is Refcounting difference in D5


Quote
> Here's a bit more info just in case anyones still following this thread.

> Apparently I only get the IDE shutdown errors if a form with my component
is
> open and that form contains two or more of this component.

> If the form is not open then I get no error and if the form only contains
> one instance of my component then I get no error.

> Does this suggest anything?

I'm trawling this group out of desperation looking  for an answer to a
problem using the DHTMLEdit control (ActiveX import). Weird, but under D5
this control exhibits the same behaviour as yours! If it is the only and
first control created in an app - it works. If it is created after any other
control - it doesn't (or might somtimes??). You can create an app with this
control as the first control created, get it working with toolbars etc, then
cut the control out and paste it back in (now lower down in creation order)
and you will always get an error on exiting the application.

 Don't know the solution and I reckon I would pay for the answer. The only
thought I have at the moment is that it could be the way the app
de-activates the control when ActiveOleControl is moving around. As far as I
can figure, the only thing that is different when you change the creation
order of controls is the way the ActiveOleControl property changes.

Anyone else figured it out?

Regards
Ian Stuart

Re:Is Refcounting difference in D5


Quote
> Here's a bit more info just in case anyones still following this thread.

> Apparently I only get the IDE shutdown errors if a form with my component
is
> open and that form contains two or more of this component.

> If the form is not open then I get no error and if the form only contains
> one instance of my component then I get no error.

> Does this suggest anything?

If I haven't managed to delete my last miserable reply then don't look at
it!

The problem I had was with the TDHTMLEdit control - and it appeared in the
type library import under the CreateControl procedure. For some reason the
FRefCount on FIntf was incorrect - this caused app-exit exceptions it the
control was not the first created on a form, or if more than one control was
on the form. If the controls were never shown - no problem, if the
DefaultInterface was never accessed - no problem.

Just in case this solution overlaps yours.....the TDHTMLEdit is derived from
a TOleControl. In the imported type library DHTMLEDLib_TLB.pas I modified
the CreateControl as shown. This has solved the problem and I can use the
control as I like without exceptions.

procedure TDHTMLEdit.CreateControl;
  procedure DoCreate;
  begin
    FIntf := IUnknown(OleObject) as IDHTMLEdit;
    FIntf._addRef; { ADD THIS HERE }
  end;
begin
  if FIntf = nil then DoCreate;
end;

Regards
Ian Stuart

Re:Is Refcounting difference in D5


Ian,

I appreciate your suggestion and although mine isn't an AX control (and not
based on TOleControl) I did try playing with _AddRef in the constructor
(actually a AfterConstruction override) but it doesn't seem to make any
difference.

The situation is getting really bad. I keep hoping for a suggestion that
will make this problem go away. The real problem is that my understanding of
both COM and Borland's take on COM is insufficient to fix this.

Some suggestion on debugging techniques might help. The error messages
aren't helpful and I never get the error running under the IDE only when I
shut the IDE down.

This is super frustrating and has been going on literally for months now.
So long infact that I'm not to the point of having a finished product (even
a written help file) but I can't release because of this one thing!

In fact I'm so frustrated with this that I'm looking to contract a good COM
person for help and pay the fee.

Please HHHHHEEEEEELLLLLLPPPPPPPP !

Quote
> If I haven't managed to delete my last miserable reply then don't look at
> it!

> The problem I had was with the TDHTMLEdit control - and it appeared in the
> type library import under the CreateControl procedure. For some reason the
> FRefCount on FIntf was incorrect - this caused app-exit exceptions it the
> control was not the first created on a form, or if more than one control
was
> on the form. If the controls were never shown - no problem, if the
> DefaultInterface was never accessed - no problem.

> Just in case this solution overlaps yours.....the TDHTMLEdit is derived
from
> a TOleControl. In the imported type library DHTMLEDLib_TLB.pas I modified
> the CreateControl as shown. This has solved the problem and I can use the
> control as I like without exceptions.

> procedure TDHTMLEdit.CreateControl;
>   procedure DoCreate;
>   begin
>     FIntf := IUnknown(OleObject) as IDHTMLEdit;
>     FIntf._addRef; { ADD THIS HERE }
>   end;
> begin
>   if FIntf = nil then DoCreate;
> end;

> Regards
> Ian Stuart

Re:Is Refcounting difference in D5


Ahhhh....
Never mind!

I finally figured it out after reading a totally unrelated article in an old
Delphi Magazine on the Singleton design pattern. It suddenly dawned on me
that I really wasn't dealing with a one to one relationship between the
object (the MS Agent character) and the event sink. In reality I had N
number of MS Agent Character instances and only one event sink that they all
shared. This explains why the problem only showed up when I had more than
one Agent on a form. When there were 2 or more, the first would get
destroyed (destroying the sink) then the 2nd,  would try to unregister from
the sink but the sink was already gone and error.

Once I realized this and re-coded the problem went away.

Re:Is Refcounting difference in D5


Quote
> When there were 2 or more, the first would get
> destroyed (destroying the sink) then the 2nd...

I'm glad you finally got it solved. Refcount problems can be real bears to
track down.

- Rick

Other Threads