Как правильно освободить интерфейс за OleVariant? - PullRequest
8 голосов
/ 19 июля 2011

Я пытаюсь найти безопасный / детерминированный способ освобождения интерфейса, который инкапсулирован в OleVariant.

AFAICS Delphi освобождает ссылки на интерфейсы в конце процедуры, но в моем случае я должен сделать это раньше, потому что я должен выключить 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

Я решил поместить OleVariant в дополнительный экземпляр класса, который я могу освободить перед вызовом 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;

Является ли это решение безопасным или есть лучшее решение, которое я упустил из виду?

1 Ответ

9 голосов
/ 19 июля 2011

Компилятор явно использует неявную переменную локального интерфейса для возвращаемого значения из CreateOleObject. Затем он освобождается в конце процедуры, слишком поздно для вас.

Есть несколько способов победить это. Прежде всего, вы можете явно указать ссылку на интерфейс IDispatch, возвращаемую CreateOleObject. Это позволяет контролировать его время жизни.

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;

Альтернативой может быть перемещение кода, вызывающего CreateOleObject, в отдельную подпрограмму со своей областью действия.

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;

Поскольку неявная локальная ссылка находится в области действия DoWork, она освобождается в конце DoWork и, следовательно, перед запуском CoUninitialize.

Моя рекомендация - использовать второй вариант, который чище и заставляет компилятор выполнять работу от вашего имени.

...