3

I have a class that implements an interface, which is made available for plugins. The declaration of class is quite simple. There is only one instance of this class for an entire application. When the function that returns the interface is called, it calls _AddRef on the retrieved interface before passing it back as result. Unfortunately it works until I try to free the object (see "finalization" section) - it reports Invalid Pointer Operation. If I comment it out, it works fine (however FastMM reports memory leaks, so the object is not being freed).

Here is the part of the code in the function that returns the interface (in fact it is an overridden QueryInterface of my "ServicesManager" class).

if ConfigManager.GetInterface(IID, obj) then
begin
  ISDK_ConfigManager(obj)._AddRef;
  result:= 0;
end

and the code of ConfigManager class ...

type
  TConfigManager = class(TInterfacedObject, ISDK_ConfigManager)
  private
  ... 
  end;

var
  ConfigManager: TConfigManager;
implementation

...

initialization
  ConfigManager:= TConfigManager.Create();
finalization
  if ConfigManager <> nil then
    FreeAndNil(ConfigManager); //if I comment it out, it leaks the memory but no Invalid Ptr. Op. raises

What am I doing wrong? I need to pass a reference to exactly this instance of ConfigManager.

Rob Kennedy
  • 156,531
  • 20
  • 258
  • 446
migajek
  • 8,204
  • 14
  • 71
  • 113
  • What does TConfigManager.Destroy do? – Ozan Sep 17 '09 at 00:58
  • nothing that would cause an error. to be sure I commented it out as well before posting here, didn't helped me ... – migajek Sep 17 '09 at 01:14
  • ok I found a *workaround*, not a *fix*. Instead of calling FreeAndNil, I'm calling ._release ... But still looking for an answer!! – migajek Sep 17 '09 at 01:15

4 Answers4

10

The number one piece of advice you'll hear when dealing with interfaces is to never mix interface references with object references. What this means is that once you start referring to an object via an interface reference, you cease to refer to it via an object reference. Ever.

The reason is that the first time you assign an interface variable, the reference count of the object will become 1. When that variable goes out of scope or gets assigned a new value, the reference count becomes zero, and the object frees itself. This is all without any modification of the original object-reference variable, so when you later try to use that variable, it's not a null pointer, but the object it referred to is gone — it's a dangling reference. When you try to free something that doesn't exist, you get an invalid-pointer-operation exception.

Declare your ConfigManager variable as an interface. Don't free it yourself. Once you do that, you can move the entire declaration of TConfigManager into the implementation section because no code outside that unit will ever refer to it.

Also, there's rarely any reason to provide your own implementation of QueryInterface. (You said you overrode it, but that's impossible since it's not virtual.) The one provided by TInterfacedObject should be sufficient. The one you're providing is actually causing a memory leak because you're incrementing the reference count when you shouldn't be. GetInterface already calls _AddRef (by performing an interface assignment), so you're returning objects with inflated reference counts.

Rob Kennedy
  • 156,531
  • 20
  • 258
  • 446
  • ad 1. ok, and what to do if I'd like my main form to implement one of the interfaces, and than be passed as the plugin requests it? BTW I have it implemented the same way (Form.GetInterface... + _AddRef) and it works there! ad 2.right, not really overriding it, just using "own" version ;) Why? because what I'm passing to plugin is interface to "services" which is the only thing plugin can access and is very limited, does not expose any function. However later it can be used to access various interfaces, with code likie ` (FApplication as ISDK_ConfigManager).DoSomething ... ` – migajek Sep 17 '09 at 01:03
  • In the form, `_AddRef` doesn't really do anything. And I don't understand you reason for having your own implementation of `QueryInterface`. *All* interfaces provide `QueryInterface`, even your "service" interface that you say doesn't expose any functions. (And if it doesn't have any functions, then why does it exist at all?) – Rob Kennedy Sep 17 '09 at 01:25
  • it exists to provide interfaces for all the services available, as in given example ;) – migajek Sep 17 '09 at 08:38
  • oh, I found it. In some discussion forum they said that TComponent (and descendandts I guess) are not references-counted. Should I use some class which doesn't count references as a base one?... – migajek Sep 17 '09 at 08:48
  • I think I've been clear about what I think you should do. See Jeroen's answer for the code example. I've also updated my answer to explain why your custom `QueryInterface` implementation is wrong. – Rob Kennedy Sep 17 '09 at 12:49
2

You said this is a plugin system? Are you loading your plugins as BPLs? I ran into that problem last week, actually. You can't rely on finalization to clear your interface references. You need to make sure to clear them before you unload the plugin, or its memory space becomes invalid.

Edit: By "clearing interface references" I mean calling _Release on them, either by manually setting it to nil or by letting the references go out of scope. If your interface manager holds interface references to the plugins, they'll get cleared when the interface manager gets destroyed.

Mason Wheeler
  • 77,748
  • 42
  • 247
  • 453
  • nope, the plugins are dlls however they both (app & plugins) use shared BPLs. Huh could you please tell me what do you mean by clearing ifaces references? BTW how did you edited my qestion to highlight the syntax? :P I don't get this editor on StackOverflow, it doesn't handle < code > tags for me ... – migajek Sep 17 '09 at 00:50
  • StackOverflow doesn't do code-view with < code > tags. It does it by special indentation, or backticks if you embed code in a paragraph. I fixed it by removing the tags, highlighting the code, and pressing the "010 101" button above the editor, which sets up the formatting automatically. – Mason Wheeler Sep 17 '09 at 01:21
1

I totally agree with Rob.

What most likely helps is rewriting your initialization code like below.

Now ConfigManager is of type ISDK_ConfigManager, and by assigning nil to it, the reference count will decrement. When the reference count becomes zero, it will automatically become freed.

type
  TConfigManager = class(TInterfacedObject, ISDK_ConfigManager)
  private
  ...
  end;

var
  ConfigManager: ISDK_ConfigManager;
implementation

...

initialization
  ConfigManager:= TConfigManager.Create();
finalization
  ConfigManager := nil;
end;

--jeroen

Jeroen Wiert Pluimers
  • 23,000
  • 6
  • 63
  • 142
0

does TConfigManager class has any method declared as "published"?