Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Smart pointers and COM-server unloading. Part 3

0.00/5 (No votes)
21 Nov 2015 1  
In the article I describe an approach to handle COM-server unloading issues using smart pointers.

Introduction

I will remind that in this series of articles I research cases, when COM-server application doesn't unload. In the previous part of the article we researched the reasons, which can cause application to unload and not to unload. In this part of the article I will introduce the concept of smart pointer which can become pretty useful.

Index

The article consists of several parts. Here is the full list of the article parts:

About smart pointers

The concept of smart pointers originates from the languages like C++, where programmer has deep level of control over memory allocation and deallocation procedures involved, when operations with objects are performed. As wikipedia states smart pointer is an abstract data type which simulates a pointers while providing some additional features.

Delphi language is not as flexible as C++, but with current language state it is possible to implement smart pointers with slight limitations. In my case I would like to have all references to entry-point interface registered in some global registry. Pay attention onto the fact that I talk about references to entry-point interface and not about instances of entry-point objects. We easily can extend entry-point interface object implementation but extending entry-point interface references requires some thoughts.

Good news is that there is smart pointers implementation available in Delphi.Spring4D framework. The framework source code is available on BitBucket. But currently smart pointers are not part of the main branch, so you need to find a branch, which has this implementation. There is not much code in the smart pointers implemetation so I will reproduce the code here:

type
  TSmartPointer<T> = class(TInterfacedObject, ISmartPointer<T>)
  private
    fValue: T;
    function Invoke: T; inline;
  public
    constructor Create; overload;
    constructor Create(const value: T); overload;
    destructor Destroy; override;
  end;

implementation
...

constructor TSmartPointer<T>.Create;
begin
  case {$IFDEF DELPHIXE7_UP}System.GetTypeKind(T){$ELSE}GetTypeKind(TypeInfo(T)){$ENDIF} of
    tkClass: PObject(@fValue)^ := TActivator.CreateInstance(TypeInfo(T));
    tkPointer: PPointer(@fValue)^ := AllocMem(GetTypeSize(GetTypeData(TypeInfo(T)).RefType^));
  end;
end;

constructor TSmartPointer<T>.Create(const value: T);
begin
  fValue := value;
end;

destructor TSmartPointer<T>.Destroy;
begin
  FinalizeValue(fValue, 
    {$IFDEF DELPHIXE7_UP}
    System.GetTypeKind(T)
    {$ELSE}
    GetTypeKind(TypeInfo(T))
    {$ENDIF});
  inherited;
end;

function TSmartPointer<T>.Invoke: T;
begin
  Result := fValue;
end;

Having such a smart pointer we can use it in many cases, when pointed type expected. We can call pointed object methods indirectly through the smart pointer. We can pass smart pointer as an argument to the function which expects pointed type. And we can even cast smart pointer to another interface, which pointed object implements. But there are some cases when transparent typecasting is not supported. In particular, we should manually create smart pointer from the pointed object and we should manually implement getter and setter methods for pointed object property, if the field has type of smart pointer. 

type
  TDummyClass = class(TObject)
  private
    FApplication: ISmartPointer<ITestUnloadApplication>;
    // Application getter.
    function GetApplication: ITestUnloadApplication;
    // Application setter.
    procedure SetApplication(
      // Application.
      const Value: ITestUnloadApplication);
  public
    // Doesn't work.
    // property Application: ITestUnloadApplication read FApplication write FApplication;
    // Works like this.
    property Application: ITestUnloadApplication read GetApplication write SetApplication;
  end;  

...
procedure DoSomething(const ApplicationIntf: ITestUnloadApplication); 
begin
  ...
end;

procedure TestProcedure;
var
  ApplicationRef: ISmartPointer<ITestUnloadApplication>;
begin
  ApplicationRef := TSmartPointer<ITestUnloadApplication>.Create(
    TTestUnloadApplication.Create as ITestUnloadApplication);
  // ITestUnloadApplication interface method called indirectly through smart pointer.
  ApplicationRef.DoSomething;
  // ITestUnloadApplication function argument is passed indirectly from smart pointer.
  DoSomething(ApplicationRef);
end;

I think it is quite evident now that we can replace pointed object fields with smart pointer fields with pretty slight amount of modifications. And having references replaced we can add some additional logic into our smart pointers to keep track of interface references usage. I will produce the complete example in the next part of the article.

What's next

In this part of the article I introduced the concept of smart pointers, which we can use to extend the behavior of standard pointer variables.

In the next part of the article I will explain how to use smart pointers to keep track of application unloading cases. This part of the article will be the final one.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here