Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 584
Jens Mühlenhoff Avatar asked Jul 19 '11 10:07

Jens Mühlenhoff


1 Answers

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.

like image 75
David Heffernan Avatar answered Sep 22 '22 14:09

David Heffernan