What is the correct way to free an interface behind an OleVariant?
Asked Answered
A

1

8

I am trying to find a safe/deterministic way to release an interface which is encapsulated in an OleVariant.

AFAICS Delphi releases interface references at the end of a procedure, but in my case I have to do it earlier, because I have to shut down COM.

procedure Test;
var
  LLibrary: OleVariant;
begin
  CoInitialize(nil);
  try
    LLibrary := Null;
    try
      LLibrary := CreateOleObject(LibraryName);
    finally
      LLibrary := Unassigned; // <-- I would like to release the interface here
    end;
  finally
    CoUninitialize; // <-- Shutdown of COM
  end;
end; // <-- The compiler releases the interface here

I though of putting the OleVariant in an extra class instance that I can free before I call CoUninitialize.

procedure Test;
var
  Container: TLibraryContainer; // Holds the OleVariant
begin
  CoInitialize(nil);
  try
    Container := TLibraryContainer.Create;
    try
      {...}
    finally
      Container.Free;
    end;
  finally
    CoUninitialize;
  end;
end;

Is this solution safe or is there a better solution I have overlooked?

Amery answered 19/7, 2011 at 10:33 Comment(0)
C
9

The compiler is clearly using an implicit local interface variable for the return value from CreateOleObject. This is then released at the end of the routine, too late for you.

There are a couple of ways to defeat this. First of all you could be explicit about the IDispatch interface reference returned by CreateOleObject. This allows you to control its lifetime.

procedure Test;
var
  intf: IDispatch;
  LLibrary: OleVariant;
begin
  CoInitialize(nil);
  try
    intf := CreateOleObject(LibraryName);
    try
      LLibrary := intf;
    finally
      VarClear(LLibrary);
      intf := nil;
    end;
  finally
    CoUninitialize;
  end;
end;

An alternative would be to move the code that called CreateOleObject into a separate routine with its own scope.

procedure DoWork;
var
  LLibrary: OleVariant;
begin
  LLibrary := CreateOleObject(LibraryName);
  //do stuff with LLibrary
end;

procedure Test;
begin
  CoInitialize(nil);
  try
    DoWork;
  finally
    CoUninitialize;
  end;
end;

Since the implicit local reference is within the scope of DoWork it is released at the end of DoWork and therefore before you run CoUninitialize.

My recommendation is to use the second option which is cleaner and forces the compiler to do the work on your behalf.

Chloral answered 19/7, 2011 at 10:41 Comment(3)
The only real option is using the secondary routine, unless you enjoy counting function calls and inspecting generated assembler code to make sure there are no hidden local variables. +1 for calling your DoWork routine... DoWork, see my deleted answer for details.Coherent
@Cosmin I agree that the second option is better.Chloral
Actually my real code is a bit more sophisticated and I think that I will have to use your first variant, but I will try to refactor the code so that I can use the second one. :)Birdella

© 2022 - 2024 — McMap. All rights reserved.