I'm totally lost here, please help!
Best regards
Jörgen
> I'm totally lost here, please help!
You are going to have to give some more information and ideally some
code samples.
--
Marc Rohloff [TeamB]
marc -at- marc rohloff -dot- com
Unit POIHandler_TLB is the Delphi source of POIHandler.tlb, which
contains the definitions for the interfaces (IPOIHandler and
IPOIHandlerPlugin).
Unit POIHandlerPublic contains the COM category definitions, simply
GUIDs to identify plugins etc.
Following is only one of my attempts to get this to work. I have also
tried creating the COM object in the enumeration phase and then storing
it in the TPlugin class (same result).
I've tried using TInterfaceList, TList, TObjectList. I've run out of
ideas now.
Thanks!
Some code:
unit POIH_Class_Plugins;
interface
Uses
Windows, Classes, SysUtils, POIHandler_TLB;
Type
TPlugin = Class(TCollectionItem, IPOIHandlerPlugin)
private
FIntf : IPOIHandlerPlugin;
FGUID : TGUID;
Procedure SetGuid(Const AGuid : TGUID);
public
Constructor Create(AOwner : TCollection); Reintroduce;
Destructor Destroy; Override;
Property Intf : IPOIHandlerPlugin read FIntf implements
IPOIHandlerPlugin;
Property Guid : TGUID read FGUID;
End;
TPlugins = Class(TCollection)
protected
Owner : IUnknown;
Function GetItems(Index : Integer) : TPlugin;
Procedure CleanUp;
public
Constructor Create(AOwner : IUnknown); Virtual;
Destructor Destroy; Override;
Function Add(Const AGuid : TGUID) : TPlugin; Reintroduce;
Function Load(Clear : Boolean = False) : Integer;
Function Get(Const AGuid : TGUID) : TPlugin;
Property Items[Index : Integer] : TPlugin read GetItems; Default;
End;
implementation
Uses
ActiveX, POIHandlerPublic, POIH_Exceptions;
{ TPlugins }
function TPlugins.Add(Const AGuid: TGUID): TPlugin;
begin
Result := TPlugin(Inherited Add);
Result.SetGuid(AGuid);
end;
procedure TPlugins.CleanUp;
begin
CoFreeUnusedLibraries;
end;
constructor TPlugins.Create(AOwner : IUnknown);
begin
Inherited Create;
Owner := IPOIHandler(AOwner);
end;
destructor TPlugins.Destroy;
begin
CleanUp;
Owner := Nil;
inherited;
end;
function TPlugins.Get(const AGuid: TGUID): TPlugin;
Var I : Integer;
begin
Result := Nil;
For I := 0 to Pred(Count) do
If IsEqualGUID(AGUID, Items[I].FGUID) Then
Begin
Result := Items[I];
Break;
End;
end;
function TPlugins.GetItems(Index: Integer): TPlugin;
begin
Result := TPlugin(Inherited Items[Index]);
end;
function TPlugins.Load(Clear: Boolean): Integer;
Var Cat : ICatInformation;
Status : HResult;
Enum : IEnumGUID;
Fetched : Cardinal;
CatIDs : Packed Array of TGUID;
Guid : TGUID;
I : Integer;
Obj : IPOIHandlerPlugin;
begin
If Clear Then
Self.Clear;
SetLength(CatIDs, 1);
CatIDs[0] := CATID_POIHandlerPlugin;
Status := CoCreateInstance(CLSID_StdComponentCategoryMgr, Nil,
CLSCTX_INPROC_SERVER or CLSCTX_INPROC_HANDLER, ICatInformation, Cat);
If Succeeded(Status) Then
Begin
Status := Cat.EnumClassesOfCategories(Length(CatIDs), @CatIDs[0],
0, Nil, Enum);
If Succeeded(Status) Then
Begin
Repeat
Status := Enum.Next(1, Guid, Fetched);
If Succeeded(Status) and (Fetched = 1) Then
Begin
Status := CoCreateInstance(Guid, Nil,
CLSCTX_INPROC_SERVER or CLSCTX_INPROC_HANDLER, IID_IPOIHandlerPlugin, Obj);
If Succeeded(Status) Then
Begin
Obj.Initialize(Owner);
Add(Obj);
End
else
Raise
EPOIHandlerException.Create(SysErrorMessage(Status));
End;
Until Failed(Status) or (Fetched <> 1);
Enum := Nil;
End
else
Raise EPOIHandlerException.Create(SysErrorMessage(Status));
Cat := Nil;
End
else
Raise EPOIHandlerException.Create(SysErrorMessage(Status));
Result := Count;
end;
{ TPlugin }
constructor TPlugin.Create(AOwner: TCollection);
begin
Inherited;
FGUID := GUID_NULL;
FIntf := Nil;
end;
destructor TPlugin.Destroy;
begin
FIntf._Release;
FIntf := Nil;
inherited;
end;
procedure TPlugin.SetGuid(const AGuid: TGUID);
Var Status : HResult;
begin
// Assume this is only called once, after 'add'.
FGUID := AGuid;
Status := CoCreateInstance(AGuid, Nil, CLSCTX_INPROC_SERVER or
CLSCTX_LOCAL_SERVER, IID_IPOIHandlerPlugin, FIntf);
FIntf._AddRef;
If Failed(Status) Then
Raise EPOIHandlerPluginException.Create(SysErrorMessage(Status));
end;
end.
> Here comes some more information, as requested.
Does this work for you? It would not even compile for me for two
reasons:
constructor TPlugins.Create(AOwner : IUnknown);
begin
Inherited Create;
Owner := AOwner;
end;
The inherited constructor expects you to pass the type of the
collection item. i.e. inherited Create(TPlugin)
and
If Succeeded(Status) Then
Begin
Obj.Initialize(Owner);
Add(Obj);
End
The Add method expects a GUID as a parameter.
I assume the plugin is also written in Delphi? I would set breakpoints
in the destroy, _addref and _release methods of the plugin class and
see what is happening and where the methods are being called from.
You might also find your 'load' method more readable if you changed
if xxx then
begin
if yyy then
begin
zzzz;
end
else
raise aaa
end
else
raise bbb
To
if not xxx then
raise bbb;
if not yyy then
raise aaa;
zzzz;
> I am trying to write a plugin system based on COM objects/components.
Have a look at the following article. I use this approach myself for my own
COM-based plugins:
Implementing a Plug-in Framework
http://www.techvanguards.com/com/tutorials/plugin.asp
> The problem arises when my host application is trying to keep
> track of the loaded objects. Somehow they unload or get referenced
> into oblivion...
Then you are not managing the reference counts correctly.
Gambit
> Does this work for you? It would not even compile for me for two
> reasons:
No, sorry... I uncommented/removed some other code (another test) and
wrong constructor got posted. :(
The same problem with the adding of the object.
But, even if these errors are corrected, and it passes compilation, it
will produce runtime errors as Obj.Initialize jumps into uninitialized
memory.
Another thing I forgot to mention is that the plugin code is a simple
COM object. Doesn't matter if it's written in Dephi or C++. Same error.
Regards,
Jörgen
>
> No, sorry... I uncommented/removed some other code (another test) and
> wrong constructor got posted. :(
> The same problem with the adding of the object.
> But, even if these errors are corrected, and it passes compilation, it
> will produce runtime errors as Obj.Initialize jumps into uninitialized
> memory.
So what does the real code for the Load method look like?
I've read this article before, and thought it would help, but there is a
main difference from what I need. I need to keep a list of my plugins,
be it a TList, TCollection or whatever.
There seem to be a difference when You use visual components too, I
can't explain it other than it seems to work better if it's visual
(forms etc.)
>> The problem arises when my host application is trying to keep
>> track of the loaded objects. Somehow they unload or get referenced
>> into oblivion...
>
> Then you are not managing the reference counts correctly.
>
Really? Why do I get the same result if I put ._AddRef's after loading
then? The object still seem to get freed, or the pointer to it gets
screwed up.
>
> Gambit
>
>
Regards,
/Jörgen
Almost the same, but here goes:
function TPlugins.Load(Clear: Boolean): Integer;
Var Cat : ICatInformation;
Status : HResult;
Enum : IEnumGUID;
Fetched : Cardinal;
CatIDs : Packed Array of TGUID;
Guid : TGUID;
I : Integer;
begin
If Clear Then
Self.Clear;
SetLength(CatIDs, 1);
CatIDs[0] := CATID_POIHandlerPlugin;
Status := CoCreateInstance(CLSID_StdComponentCategoryMgr, Nil,
CLSCTX_INPROC_SERVER or CLSCTX_INPROC_HANDLER, ICatInformation, Cat);
If Succeeded(Status) Then
Begin
Status := Cat.EnumClassesOfCategories(Length(CatIDs), @CatIDs[0],
0, Nil, Enum);
If Succeeded(Status) Then
Begin
Repeat
Status := Enum.Next(1, Guid, Fetched);
// Some changes here!
If Succeeded(Status) and (Fetched = 1) Then
Add(Guid)
else
Raise EPOIHandlerException.Create(SysErrorMessage(Status));
// End of changes.
> I need to keep a list of my plugins, be it a TList, TCollection or
> whatever.
None of those classes are well-suited for holding instances of COM object.
Use TInterfaceList for that.
> There seem to be a difference when You use visual components
> too, I can't explain it other than it seems to work better if it's
> visual (forms etc.)
That is not very helpful.
> Really? Why do I get the same result if I put ._AddRef's after
> loading then? The object still seem to get freed, or the pointer
> to it gets screwed up.
There is no way to know without seeing your actual code. But mismanaging
the reference count is the only way a COM object can be unloaded
prematurely.
Gambit
> Almost the same, but here goes:
All that is doing is loading a list of GUIDs, not instantiating the actual
plugin objects. Please show what "Add(Guid)" is doing inside of it.
Gambit
> All that is doing is loading a list of GUIDs, not instantiating the actual
> plugin objects. Please show what "Add(Guid)" is doing inside of it.
He did, further up the thread.
function TPlugins.Add(Const AGuid: TGUID): TPlugin;
begin
Result := TPlugin(Inherited Add);
Result.SetGuid(AGuid);
end;
procedure TPlugin.SetGuid(const AGuid: TGUID);
Var Status : HResult;
begin
// Assume this is only called once, after 'add'.
FGUID := AGuid;
Status := CoCreateInstance(AGuid, Nil, CLSCTX_INPROC_SERVER or
CLSCTX_LOCAL_SERVER, IID_IPOIHandlerPlugin, FIntf);
FIntf._AddRef;
If Failed(Status) Then
Raise EPOIHandlerPluginException.Create(SysErrorMessage(Status));
end;
Jorgen,
In SetGuid you shouldn't call _AddRef if the CoCreateInstanceCall did
not succeed, you will get an AV.
(You shouldn't need to call it at all since the interface is already
reference counted).
> He did, further up the thread.
I have replied to that message now.
Gambit
> TPlugin = Class(TCollectionItem, IPOIHandlerPlugin)
> private
> FIntf : IPOIHandlerPlugin;
Why is TPlugin deriving from the IPOIHandlerPlugin interface directly, as
well as having a separate IPOIHandlerPlugin member? Because of the way
TCollection managed TCollectionItem objects, I doubt you can derive a
TCollectionItem from a COM interface and have it work properly. Get rid of
the derivitive. It is unnecessay in the code you have shown anyway.
Also, get rid of the 'reintroduce' on the TPlugin constructor as well. You
are not changing the signature of the original constructor. Use 'override'
instead. You are changing the signature of the TPlugins constructor,
though. That is where 'reintroduce' belongs.
> Owner := IPOIHandler(AOwner);
That is not the correct way to cast one Interface pointer to another
Interface type. You have to use the 'as' operator, or the Supports()
function, in order to maintain the reference count correctly (which you are
not doing):
Owner := AOwner as IPOIHandler;
Besides, that, Owner is declared as IUnknown, not IPOIHandler, so you are
trying to cast from IUnknown to IPOIHandler back to IUnknown again. That is
redundant. Either declare Owner as IPOIHandler, or get rid of the casting.
> Status := CoCreateInstance(AGuid, Nil, CLSCTX_INPROC_SERVER
> or CLSCTX_LOCAL_SERVER, IID_IPOIHandlerPlugin, FIntf);
> FIntf._AddRef;
Get rid of that _AddRef() call. CoCreateInstance() already calls AddRef()
before exiting. You are not calling _Release()
With all of that said, try this updated code:
unit POIH_Class_Plugins;
interface
uses
Windows, Classes, ComObj, SysUtils, POIHandler_TLB;
type
TPlugin = Class(TCollectionItem)
private
FIntf : IPOIHandlerPlugin;
FGuid : TGUID;
procedure SetGuid(Const AGuid : TGUID);
public
constructor Create(AOwner : TCollection); override;
destructor Destroy; override;
property Intf : IPOIHandlerPlugin read FIntf implements
IPOIHandlerPlugin;
property Guid : TGUID read FGuid;
end;
TPlugins = Class(TCollection)
protected
FOwner : IPOIHandler;
function GetPlugin(Const AGuid : TGUID) : TPlugin;
function GetPluginByGuid(Const AGuid : TGUID) : TPlugin;
public
constructor Create(AOwner : IPOIHandler); reintroduce;
destructor Destroy; override;
function Add(const AGuid : TGUID) : TPlugin; reintroduce;
function Load(AClear : Boolean = False) : Integer;
property Plugins[Index : Integer] : TPlugin read GetPlugin;
default;
property PluginByGuid[const AGuid: TGUID] : TPlugin read
GetPluginByGuid;
end;
EPOIHandlerException = class(EOleSysError);
EPOIHandlerPluginException = class(EOleSysError);
implementation
uses
ActiveX, POIHandlerPublic;
function HandlerCheck(AErrorCode: HResult): HResult;
begin
Result := AErrorCode;
if Failed(AErrorCode) then raise EPOIHandlerException.Create('',
AErrorCode, 0);
end;
function PluginCheck(AErrorCode: HResult): HResult;
begin
Result := AErrorCode;
if Failed(Status) then raise EPOIHandlerPluginException.Create('',
AErrorCode, 0);
end;
{ TPlugins }
constructor TPlugins.Create(AOwner : IPOIHandler);
begin
inherited Create;
FOwner := AOwner;
end;
destructor TPlugins.Destroy;
begin
Clear;
CoFreeUnusedLibraries;
FOwner := nil;
inherited Destroy;
end;
function TPlugins.Add(const AGuid: TGUID): TPlugin;
begin
Result := TPlugin(Inherited Add);
try
Result.SetGuid(AGuid);
except
FreeAndNil(Result);
raise;
end;
end;
function TPlugins.GetPluginByGuid(const AGuid: TGUID): TPlugin;
var
I : Integer;
begin
for I := 0 to Pred(Count) do
begin
Result := Plugins[I];
if IsEqualGUID(AGuid, Result[I].Guid) then Exit;
end;
Result := nil;
end;
function TPlugins.GetPlugin(Index: Integer): TPlugin;
begin
Result := TPlugin(inherited Items[Index]);
end;
function TPlugins.Load(AClear: Boolean): Integer;
var
Cat : ICatInformation;
Enum : IEnumGUID;
Fetched : Cardinal;
CatID : TGUID;
Guid : TGUID;
begin
Result := 0;
if AClear then
begin
Clear;
CoFreeUnusedLibraries;
end;
HandlerCheck(CoCreateInstance(CLSID_StdComponentCategoryMgr, nil,
CLSCTX_INPROC_SERVER or CLSCTX_INPROC_HANDLER, ICatInformation, Cat));
try
CatID := CATID_POIHandlerPlugin;
HandlerCheck(Cat.EnumClassesOfCategories(1, @CatID, 0, nil,
Enum));
try
repeat
if HandlerCheck(Enum.Next(1, Guid, Fetched)) = S_FALSE
then Break; // end of enum
Add(Guid);
until False;
finally
Enum := nil;
end;
finally
Cat := nil;
end;
Result := Count;
end;
{ TPlugin }
constructor TPlugin.Create(AOwner: TCollection);
begin
inherited Create(AOwner);
FGuid := GUID_NULL;
FIntf := nil;
end;
destructor TPlugin.Destroy;
begin
FIntf := nil;
inherited Destroy;
end;
procedure TPlugin.SetGuid(const AGuid: TGUID);
begin
PluginCheck(CoCreateInstance(AGuid, nil, CLSCTX_INPROC_SERVER or
CLSCTX_INPROC_HANDLER, IID_IPOIHandlerPlugin, FIntf));
FIntf.Initialize(TPlugins(Collection).FOwner);
FGuid := AGuid;
end;
end.
Gambit
I haven't yet been able to set the breakpoints mentioned earlier, since
this project is not compiling at the moment (due to all my changes) but
I am writing another application using the same technique and I am
expecting the same kinds of problems in it. So, when that is compiling
and I can set the proper breakpoints, I will come back and report.
But, at the moment, please keep on suggesting on this code, since this
is what I think would work, somehow.
Regards
/Jörgen
I did! If You read the original thread, my posting from July 23, at 18:12:
I've tried using TInterfaceList, TList, TObjectList. I've run out of
ideas now.
>
>> There seem to be a difference when You use visual components
>> too, I can't explain it other than it seems to work better if it's
>> visual (forms etc.)
>
> That is not very helpful.
I know it's not much help, but I haven't explored this yet... One
problem at the time.
>
>> Really? Why do I get the same result if I put ._AddRef's after
>> loading then? The object still seem to get freed, or the pointer
>> to it gets screwed up.
>
> There is no way to know without seeing your actual code. But mismanaging
> the reference count is the only way a COM object can be unloaded
> prematurely.
>
Even if I willingly mismanage the reference counts by smothering the
code with ._AddRef's, my objects still seem to vanish.
As I wrote moments ago in the original branch of this thread, I'm
writing another application, using the same technique (I never learn, do
I? :) ) and when that is compiling I will report from that, then I will
have a better piece of code to show You all, since this current code
isn't compiling due to all my changes.
Regards
/Jörgen
Regards
/Jörgen
Remy Lebeau (TeamB) skrev: