3

I'm still trying to come to grips with using Interfaces. I'm implementing them for the sole purpose of interacting with objects which are instantiated within a DLL. When I use it, everything works fine, all the methods work as expected, etc. The issue is when it comes to cleaning up the object behind that interface.

I have a simple interface like so

IMyInterface = interface
  ['{d52b14f3-156b-4df8-aa16-cb353193d27c}']
  procedure Foo;
end;

And an object for it

TMyObject = class(TInterfacedObject, IMyInterface)
private
  procedure Foo;
end;

Inside the DLL I have a global variable of this object as well as two exported functions to create and destroy this instance

var
  _MyObject: TMyObject;

function CreateMyObject: IMyInterface; stdcall;
begin
  _MyObject:= TMyObject.Create;
  Result:= IMyInterface(_MyObject);
end;

function DestroyMyObject: Integer; stdcall;
begin
  _MyObject.Free; //   <--   Invalid Pointer Operation
end;

The destructor of the object does virtually nothing, just inherited and I still have this issue. But I get Invalid Pointer Operation on _MyObject.Free.

I use LoadLibrary and GetProcAddress to access these exported methods.

Why am I getting this and how do I fix it?

Jerry Dodge
  • 26,858
  • 31
  • 155
  • 327
  • What happens when the caller calls `CreateMyObject` twice? Why do you need a global variable at all? All you need is a function that returns a interface implemented by a newly created object. – David Heffernan Sep 12 '13 at 07:42
  • @David Actually my above code is a re-worked version of a much more complex infrastructure. I'm actually keeping a list of these instances, but to post how I manage this list would be a lot of unnecessary code, so I pretty much watered it down to only show the core basics of how things work. – Jerry Dodge Sep 12 '13 at 17:34

2 Answers2

6

An invalid pointer operation means you freed something that wasn't allocated.

In this case, the object you're freeing has already been destroyed. Put a breakpoint in the destructor and see for yourself.

Interfaces have reference-counting code associated with them, which is why all the advice you've read about interfaces says not to mix them with object references, which have no such reference counting.

When you instantiate the object and assign it to your global variable, the object's reference count is zero, and interfaces aren't involved yet. When you assign it to the function result, the reference count becomes one. You can watch how that happens if you enable debug DCUs and step through that statement with the debugger. (The type cast isn't necessary, by the way; the compiler already knows that the object implements the target interface and will allow the simple assignment by itself.)

Elsewhere, on the consuming side of this DLL, the variable holding the last interface reference of the object gets cleared. The reference count becomes zero, and the object destroys itself.

Once the object is destroyed, your global variable is a dangling reference. It holds the address of an object that doesn't exist anymore. When you call Free on it, the destructor passes the address to the memory manager, but the memory manager knows that it doesn't have anything at that address (anymore), so it raises an exception.

To fix this, change the type of that global variable to be the interface type, and then remove the Free call; replace it with a statement assigning nil to the variable. With those changes, creating the object and storing an interface reference in the variable will set the object's reference count to one, and returning it to the caller will set it to two. When the consumer clears its reference, the count will drop to one, and the new nil assignment will set it to zero, making the object free itself at the proper time.

Once you start accessing an object through interface references, it's best not to use it through ordinary object references anymore. The risk is just too great that you'll accidentally use the object after its already been destroyed.

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
  • The other option is to remove reference counting by implementing the AddRef and Release methods to always have a value of (say) 1; you then free the object manually as you would for non-refcounted object references. – David Sep 12 '13 at 04:50
  • @David, the return value has nothing to do with removing reference counting. To remove reference counting, you simply implement them to not count references. That doesn't solve all problems, though. If you have a dangling interface reference, you're likely to get access violations or other undefined behavior. – Rob Kennedy Sep 12 '13 at 07:42
  • Geez, now I don't like Interfaces [actually I don't like reference counting]. Never would have guessed that I'm not supposed to free it. – Jerry Dodge Sep 12 '13 at 17:32
  • @RobKennedy, I don't understand your comment, sorry. I read your answer as it already being freed due to interfaces freeing themselves when the refcount hits 0, and mixing object and interface references. Thus one common solution is to disable reference counting and free interfaced objects manually. What have I missed in your comment or the question about the "return value"? – David Sep 12 '13 at 23:27
  • @David, you said the way to remove reference counting is to implement `AddRef` and `Release` to return 1. The return value is irrelevant; *no* production code ever uses that value. To disable reference counting, you do whatever you want in those methods, but don't *count the references*. It doesn't solve very much, though, because even if you're not counting the interface references, that doesn't mean there aren't still any in existence. If there are, and you free the object manually, your program may still crash. If anything, keep counting references, but disable the self-freeing behavior. – Rob Kennedy Sep 12 '13 at 23:51
  • @RobKennedy: '1' is arbitrary. It's preventing the references from being counted just as you suggest. Dangling references are another problem (a design problem) but at least this stops the object being freed from under you if mixing object/interface references. It's just a suggestion! It's a common Delphi technique. See http://stackoverflow.com/questions/769329/bypassing-disabling-delphis-reference-counting-for-interfaces and http://stackoverflow.com/questions/7080178/is-there-a-non-reference-counted-base-class-like-tinterfacedobject and http://blogs.teamb.com/craigstuntz/2008/01/23/37786/ – David Sep 13 '13 at 00:03
2

You should not call .Free on TInterfacedObject-based class. It is freed automatically when last reference with interface is nil'ed.

Your code example should look like:

var
  _MyObject: IUnknown;

function CreateMyObject: IMyInterface; stdcall;
begin
  // unified interface
  _MyObject:= TMyObject.Create as IUnknown;
  // cast to IMyInterface
  Result:= _MyObject as IMyInterface;
end;

function DestroyMyObject: Integer; stdcall;
begin
  _MyObject := nil; 
end;
Alexey Petushkov
  • 2,010
  • 19
  • 19