Idle Musings of a Delphi Junkie

Migrating IIS7 configurations

In the process of setting up my development environment on a new laptop, I was looking for a way to migrate my IIS7 configurations from the old laptop, so I didn’t have to manually configure the many web applications and virtual directories required in my day to day development duties. As there was no obvious ‘Export’ or ‘Import’ options available, I asked my good friend Mr Google, who suggested this article.

The steps outlined seemed pretty straightforward, and just what I was looking for. A few of the commenters remarked that they couldn’t get the process to work, with HTTP 503 errors being reported when attempting to run imported web sites. After the import I had the same problem with my web sites and static content being served, but unlike the commenters, with a little diagnostics and a smattering of common sense, I was able to solve the problem.

The first step was to take a peek in the event viewer to see if that would give any insight into what was going on.

image

As the above screenshot shows, the IIS worker process was unable to find a DLL with the name compdyn.dll. This DLL is used by the dynamic HTTP compression feature of IIS7, which is not installed by default, and a quick squiz into the C:\Windows\System32\inetsrv directory confirms that the DLL is not present on my system. I fired up Programs and Features so I could install this feature, but after waiting a few minutes I was informed that the installation had failed. This time the event log wasn’t too helpful as to why.

image

Working on a hunch that this may have been due to the install process failing because of locked files, I rebooted my machine, tried to apply the configuration change again, and this time it succeeded. I then confirmed that the config change had the desired effect, and that compdyn.dll now existed in the C:\Windows\System32\inetsrv directory. This dance was went another round, due to another required dll that was missing, but once all required IIS features had been installed, I was successfully able to run my imported web applications. As it turns out, I suspect a full reboot was probably not necessary, as the files in use were probably being locked by the IIS worker process attempting to service the failed request. So it would probably have been sufficient to either recycle the applicable application pool, or restart the IIS service.

Posted by David Clegg on April 16th, 2009 under .NET, ASP.NET, IIS | 2 Comments »


Psst! Can you keep a secret?

Between the 23rd and 28th of March, Upscene productions are having a Secret Super Sale on their Database Workbench Pro product, during which time they will be offering a staggering 75% discount!

I am a very satisfied DBW customer, and have been using it with the Interbase module for quite a few years now. If you haven’t already done so, I’d recommend that you take the time to download the trial and check it out, especially if you are an Interbase or Firebird user looking for a good RDBMS tool. Then again, with 75% off they’re almost giving it away, so why not just take the plunge and purchase it. :-)

Oh, and Database Workbench is written in Delphi, so by purchasing it you will be doing your bit to help a Delphi ISV, which is always a good thing in my book.

Posted by David Clegg on March 17th, 2009 under Database, Delphi | Comment now »


The Gravatar Olympics

As you may be aware, CodeCentral now has support for Gravatar avatars. In honour of the pending Summer Olympics, I think it would be fitting to have a little competition. In this event (feel free to draw your own parallels with an actual Olympic event, because I’m too lazy to do so ;-)), the winning team will be the one with the most Gravatars associated with it. Teams will be split according to the first letter of the CodeCentral author’s last name. At the time of posting this, Team M is winning with a total of 4 avatars, closely followed by Team K, who has 3.

As this article explains, associating a Gravatar with your Developer Network account is easy. Simply sign up for a Gravatar account, using the e-mail address associated with your Developer Network profile.

The competition will run from now, until the Olympic Closing Ceremony on 24th August, so on your marks… get set… go!

Posted by David Clegg on August 6th, 2008 under CDN | 4 Comments »


Using String Attributes in ECO IV dbExpress Applications

Today I was converting an ECO III application originally written in Borland Developer Studio 2006, to an ECO IV application using RAD Studio 2007, when I stumbled across an interesting problem. The application talks to an InterBase database, and was using the Borland Data Provider components for its database connectivity. While BDP is still included in RAD Studio 2007, it has been deprecated and dbExpress is now the preferred data access framework to use. So, as well as converting the app to use the newer ECO framework, it also made sense for me to convert it to use dbExpress for database persistence. Thanks to the ECO PersistenceMapper classes, this is a pretty straightforward exercise to do, and normally would simply involve using a new connection and PersistenceMapper class (in this case TAdoDbxConnection and PersistenceMapperDbx respectively).

One of the goals of DBX was to consolidate data access frameworks used in the native and managed development environments. Rather than have different data access framework implementations for both Win32 and .NET, dbExpress drivers were implemented using native Delphi, and a bridge to these drivers was created so that .NET applications can consume them using the ADO.NET classes and interfaces in the System.Data.Common namespace (such as DbConnection and DbCommand). For more details on this, see Steve Shaughnessy’s excellent blog post discussing the new dbExpress features.

One of the challenges raised by this approach is that dbExpress exposes a superset of functionality than what is exposed via the classes and interfaces in System.Data.Common. An example of this is that dbExpress parameters can have a data type and subtype specified, whereas the System.Data.IDataParameter interface only exposes a DbType property. As a result, if you set the parameter type for a dbExpress parameter using the IDataParameter interface or DbParameter class, the dbExpress implementation of this class (TAdoDbxParameter) must try to make a ‘best guess’ choice when setting the value of the TAdoDbxParameter.DbxType and TAdoDbxParameter.DbxSubType properties. Any time there is an ambiguity between the DbType enumeration value and possible TDbxDataTypes values, TDbxDataTypes.UnknownType is used. Likewise, setting TAdoDbxParameter.DbxType results in another ‘best guess’ choice to set the value of the TAdoDbxParameter.DbType property.

One particular instance where this can bite is when attempting to use string parameters to access an InterBase varchar column or parameter. Setting TAdoDbxParameter.DbType to DbType.String results in TAdoDbxParameter.DbxType being set to TDbxDataTypes.BlobType, and TAdoDbxParameter.DbxSubType set to TDBXDataTypes.WideMemoSubType. So when executing a parameterized query or stored procedure using these data types, you will get an exception raised due to invalid data types being specified.

There are two approaches that can be taken to solve this problem. You can either set the TAdoDbxParameter.DbxType parameter explicitly to the correct type (TDbxDataTypes.AnsiStringType or TDbxDataTypes.WideStringType, depending on the character set being used for the varchar column). Or you could set the DbParameter.DbType property to DbType.StringFixedLength (maps to TDbxDataTypes.WideStringType), or DbType.AnsiStringFixedLength (maps to TDbxDataTypes.AnsiStringType). In order to see which TDbxDataTypes value is used for each DbType enumeration member, look in the AdoDbxClient provider source code located in <RAD Studio Install Directory>\source\database\src\pas\dbx\driver\Borland.Data.AdoDbxClientProvider.pas. In particular, check out the TAdoDbxProviderFactory.AdoToDbxType procedure.

For ECO applications, there is no dbExpress specific attribute mapper for string attributes, so the Eco.Persistence.Default.StringAsVarChar attribute mapper is used. Because this attribute mapper performs all operations via common ADO.NET interfaces, it means that setting the data type for a parameter is done via the IDataParameter interface. So setting the type for a string attribute is done by setting IDataParameter.DbType to DbType.String. As discussed above, for TAdoDbxParameter instances this will result in the incorrect data types being used by the dbExpress framework. In order to get around this problem, we have to implement our own attribute mapper which can then be used by our PersistenceMapperDbx instance when performing database operations for string attributes. Here is an example implementation :-

unit CDN.Eco.StringAsDbxVarChar;

interface

uses
  System.Globalization,
  Eco.Persistence.Default, Eco.Persistence, System.Data;

type
  StringAsDbxVarChar = class(AbstractStringSingleColumnAttribute, ISingleColumnAttributeMapping)
  protected
    function GetDbType: DbType; virtual;
  public
    function ValueType: System.Type;
    function ColumnType(ALength: Integer): string;
    procedure ValueToParameter(AValue: System.Object; AParameter: IDataParameter);
    procedure StringToParameter(AValue: string; AParameter: IDataParameter);
    function ColumnToValue(AColumnValue: TObject): System.Object; override;
  end;

  AnsiStringAsDbxVarChar = class(StringAsDbxVarChar)
  protected
    function GetDbType: DbType; override;
  end;

implementation

function StringAsDbxVarChar.ValueType: System.Type;
begin
  Result := typeof(System.String);
end;

function StringAsDbxVarChar.ColumnType(ALength: Integer): string;
begin
  if ALength > 0 then
    Result := System.String.Format(CultureInfo.InvariantCulture, 'VARCHAR({0:d})',
      [TObject(ALength)])
  else
    Result := 'VARCHAR';
end;

function StringAsDbxVarChar.GetDbType: DbType;
begin
  Result := DbType.StringFixedLength;
end;

procedure StringAsDbxVarChar.ValueToParameter(AValue: System.Object; AParameter: IDataParameter);
begin
  if not Assigned(AParameter) then
    raise ArgumentNullException.Create('AParameter');
  EnsureType(AValue, typeof(System.String));
  AParameter.DbType := GetDbType;
  if not Assigned(AValue) then
    AParameter.Value := DbNull.Value
  else
    AParameter.Value := AValue;
end;

procedure StringAsDbxVarChar.StringToParameter(AValue: string; AParameter: IDataParameter);
begin
  ValueToParameter(AValue, AParameter);
end;

function StringAsDbxVarChar.ColumnToValue(AColumnValue: System.Object): System.Object;
begin
  EnsureType(AColumnValue, typeof(System.String));
  if (DBNull.Value.Equals(AColumnValue)) then
    Result := nil
  else
    Result := AColumnValue;
end;

{ AnsiStringAsDbxVarChar }

function AnsiStringAsDbxVarChar.GetDbType: DbType;
begin
  Result := DbType.AnsiStringFixedLength;
end;

end.

Ideally we would be able to inherit from the Eco.Persistence.Default.StringAsVarChar class, and simply re-implement the ValueToParameter procedure. But because this class is marked as sealed, we instead have to inherit from the Eco.Persistence.Default.AbstractStringSingleColumnAttribute class, and implement all the methods of the Eco.Persistence.ISingleColumnAttributemapping interface. The implementation for the these methods have been taken directly from the StringAsVarChar class (the source of which is in <Program Files>\CapableObjects\ECO\4.0\Source\Persistence\DefaultAttributeMappers.cs. The above code has implementations to cater for varchar columns containing Ansi or widestring data.

In order to use one of these attribute mappers, we need to invoke the PersistenceMapperDefinition Collection Editor for our PersistenceMapperDbx component. This is done by expanding the SqlDatabaseConfig property, and clicking on the ellipsis at the end of the PersistenceMapper property

image

We then select the System.String item, and set the value in the property editor to the fully qualified class name of the attribute mapper we wish to use.

image

This attribute mapper will then be used when any string class attributes require interaction with the database.

Edit: Due to popular demand (ok, ok… because John Kaster requested it :-)), I’ve converted this blog post into a CDN article . The content is pretty much the same as this blog post, but with some flowery embellishments to make it more ‘articley’ :-)

Posted by David Clegg on March 18th, 2008 under .NET, Database, Delphi, ECO | 1 Comment »


Dispose considered harmful

I discovered an interesting memory leak in one of my Delphi.NET services the other day, with the leak occurring in a manner I hadn’t encountered previously before. I narrowed the source of the leak down to the database polling threads, which all contained pretty much the same logic. They would create a connection to an InterBase database using the dbExpress AdoDbxClient provider, create a command, populate a DataSet using a data reader, act on the data in the dataset, and finally dispose all created objects and close the database connection. As all object references were local procedural variables, and all objects that implemented IDisposable.Dispose were being disposed explicitly, I was reasonably convinced that the memory leak was not coming from my data access code.

Because all data access in this application is done via a custom data access helper class I’d created, the first thing I needed to do was to see if I could duplicate the bug using a stripped down test application. I also wanted this test app to be database provider agnostic as much as possible, so I could profile memory usage for different providers. Here is the code I came up with. The below code is executed within the context of a thread spawned from a VCL.NET applications main thread :-

procedure TForm1.ThreadExecute(State: TObject);
var
  Con: DbConnection;
  Cmd: DbCommand;
  Reader: DbDataReader;
  DS: DataSet;
  Factory: DbProviderFactory;
  ConnectionString: string;
begin
  Factory := DbProviderFactories.GetFactory(
    ConfigurationManager.AppSettings['ProviderName']);
  ConnectionString :=
    ConfigurationManager.AppSettings['ConnectionString'];
  while FRunning do
  begin
    Con := Factory.CreateConnection;
    try
      Con.ConnectionString := ConnectionString;
      Con.Open;
      try
        Cmd := Con.CreateCommand;
        try
          Cmd.CommandText := 'SELECT * FROM EMPLOYEE';
          Reader := Cmd.ExecuteReader;
          try
            DS := DataSet.Create;
            try
              DS.Load(Reader, LoadOption.OverwriteChanges, ['TableName']);
            finally
              DS.Dispose;
            end;
          finally
            Reader.Close;
            Reader.Dispose;
          end;
        finally
          Cmd.Dispose;
        end;
      finally
        Con.Close;
      end;
    finally
      Con.Dispose;
    end;
    Thread.Sleep(500);
  end;
end;

The above code was a pretty good representation of the logic executed in the application where I initially discovered the memory leak. I compiled the application and copied the resulting executable to create three different versions. This was done so I could execute the same code using the AdoDbxClient provider to connect to the InterBase sample Employee database, and the AdoDbxClient and BlackfishSQL remote ADO.NET providers to connect to the BlackfishSQL sample Employee database. The reason for the three different executables was so I could easily profile memory usage on a per-process basis using Performance Monitor. Here is a screenshot of what it looked like after running all processes for approximately 1 1/2 hours :-

Mem

In the above screenshot DbxMemTestDBXI is the name of the executable talking to InterBase via DBX, DbxMemTestDBXB is the executable talking to BlackfishSQL via DBX, and DbxMemTestBSQL is the executable talking to BlackfishSQL via the remote ADO.NET provider. As the screenshot shows, there was a significant increase in memory usage for the InterBase instance when compared to the instances talking to BlackfishSQL. This tended to confirm my suspicions that the memory leak was happening somewhere in the InterBase DBX provider.

I passed my findings on to the CodeGear Database R&D engineers, and Stephen Blas came back with a workaround. And this is where the tale starts to get really interesting . He noted that if he swapped the call to TAdoDbxCommand.Dispose with TAdoDbxCommand.Free, he saw a significant drop in the rate of memory increase. He also noted that calling TAdoDbxCommand.Dispose prevented the TAdoDbxCommand.Destroy from being called, with the net result being that resources internal to the TAdoDbxCommand instance were not cleaned up, resulting in the memory leak being observed.

This initially didn’t make sense to me, as I was pretty sure that the Delphi .NET compiler created an implementation of the IDisposable interface for any class which declared a destructor with a certain signature, and TAdoDbxCommand did indeed declare a destructor with this signature. After doing a bit of research on the subject, I found that my recollection was correct. Brian Long discusses this in great depth in his excellent article about object destructors and finalizers in .NET using Delphi for .NET and C#., but I’ll endeavour to provide a 50 ft overview here.

Any Delphi for .NET class that implements a destructor with the signature exactly matching ‘destructor Destroy; override’, will have an implementation of the IDisposable interface implemented for it, with the implementation of IDispose.Dispose implemented by the destructor. Looking at TAdoDbxCommand in Ildasm confirms that this has happened here :-

image

The point of this implicit interface implementation is to simplify resource cleanup for Delphi developers coming from the Win32 world. Developers can put resource cleanup code in a class destructor in a similar manner as they are used to in the Win32 world, and any consuming code that obtains and calls the IDisposable interface from this class would result in the code in the destructor being executed.

And on the flip side of the coin, via the magic of class helpers, all .NET classes consumed by Delphi for .NET applications will have the following implementation of Free provided to them :-

procedure TObjectHelper.Free;
var
  FreeNotify: IFreeNotify;
begin
  if (Self <> nil) and (Self is IDisposable) then
  begin
    FreeNotify := IFreeNotify(Self);
    if FreeNotify <> nil then
    begin
      FreeNotify.BeforeFree;
      FreeNotify := nil;
    end;
    if Assigned(VCLFreeNotify) then
      VCLFreeNotify(Self);
    (Self as IDisposable).Dispose;
  end;
end;

The above implementation means that Delphi developers can call .Free on any .NET object, and be assured that any implementation of IDisposable.Dispose will be called, if applicable. So Delphi code which employs the common TObject.Create..try..finally..TObject.Free pattern would operate in a similar manner to the C# using code construct.

So, we now know that TAdoDbxCommand does have an implementation of IDisposable.Dispose, and that it maps to the destructor. So why wasn’t the destructor being called when I called TAdoDbxCommand.Dispose? In short, the answer to the question is that TAdoDbxCommand doesn’t implement a Dispose method.

When we are calling TAdoDbxCommand.Dispose, we are actually invoking an implementation in a base class, and not calling the IDisposable.Dispose implementation (which as we’ve discovered already, is implemented by the destructor). Spelunking through the class hierarchy using Reflector reveals that the implementation is provided by the System.ComponentModel.Component class, Expanding the implementation of the Dispose method shows that it has been implemented as follows :-

procedure Component.Dispose;
begin
  self.Dispose(true);
  GC.SuppressFinalize(self)
end;

The important line here is the call to GC.SuppressFinalize. Finalization can be a costly operation, and also delays the time before an out of scope object reference can be freed. For this reason, it is recommended that implementers of the Dispose pattern should call GC.SuppressFinalize when the disposal logic is deterministically called. This tells the garbage collector that it doesn’t need to call an objects finalizer, as the logic that it implements has already been executed. For more details see the MSDN article on implementing a Dispose method.

And what this means in the context of my original problem when consuming the TAdoDbxCommand class, is that the destructor is never called, either explicitly or via the garbage collector calling the classes finalizer. But as it turns out, even if the finalizer had been called, this would have not resulted in the destructor being called anyway. Here is the implementation, which is once again provided by the System.ComponentModel.Component class:-

procedure Component.Finalize;
begin
  try
    self.Dispose(false)
  finally
    inherited Finalize
  end
end;

As the above code illustrates, the finalizer would simply call Component.Dispose(Disposing: Boolean), which is not overridden in the TAdoDbxCommand class, so it has not opportunity to perform resource cleanup when the garbage collector calls the finalizer.

It is probably also prudent to mention that finalizers are not created for Delphi for .NET classes by default, and have to be explicitly implemented by class developers when they feel it is prudent to do so. Because finalizers come with a performance penalty, they generally should only be used to ensure that any unmanaged resources are freed. It is for this reason that, even though the Delphi for .NET compiler will automatically implement the Dispose pattern for you, it does not automatically add a finalizer to ensure that the dispose implementation will be called by the garbage collector if it isn’t called explicitly. I should probably mention at this point that this isn’t an oversight on the part of the CodeGear R&D team, but is following the advice given to them by the Microsoft CLR team.

If you’re anything like me, the best way to cement and understand concepts such as these is to see them working in practice. Here is a sample Delphi for .NET console app which demonstrates all the concepts covered so far :-

program DisposeTest;

{$APPTYPE CONSOLE}

uses
  System.ComponentModel,
  System.Reflection,
  SysUtils;

type
  TDontSelfDestruct = class
  end;

  TNothingsFinal = class
  public
    destructor Destroy; override;
  end;

  TIndisposed = class(Component)
  public
    destructor Destroy; override;
  end;

  TDisposed = class(TIndisposed)
  strict protected
    procedure Dispose(ADisposing: Boolean); override;
  end;

  TTheFinalWord = class(TNothingsFinal)
  strict protected
    procedure Finalize; override;
  public
    destructor Destroy; override;
  end;

destructor TNothingsFinal.Destroy;
begin
  WriteLn('    TNothingsFinal.Destroy called');
  inherited;
end;

destructor TIndisposed.Destroy;
begin
  WriteLn('    TIndisposed.Destroy called');
  inherited;
end;

procedure TDisposed.Dispose(ADisposing: Boolean);
begin
  inherited;
  WriteLn(System.String.Format('    TDisposed.Dispose called: {0}',
    ADisposing.ToString));
end;

{ TTheFinalWord }

destructor TTheFinalWord.Destroy;
begin
  WriteLn('    TTheFinalWord.Destroy called');
  GC.SuppressFinalize(Self);
  inherited;
end;

procedure TTheFinalWord.Finalize;
begin
  WriteLn('    TTheFinalWord.Finalize called');
  if (Self is IDisposable) then
    (Self as IDisposable).Dispose;
  inherited;
end;

procedure TestClass(AClassType: System.Type);
var
  Instance: TObject;
  DisposeMethod: MethodInfo;
begin
  WriteLn('Testing ' + AClassType.Name);

  WriteLn(System.String.Format('  {0}.Free', AClassType.Name));
  Instance := Activator.CreateInstance(AClassType);
  Instance.Free;

  WriteLn(System.String.Format('  {0}: IDisposable.Dispose', AClassType.Name));
  Instance := Activator.CreateInstance(AClassType);
  if Instance is IDisposable then
    (Instance as IDisposable).Dispose
  else
    WriteLn(System.String.Format('    {0} doesn''t implement IDisposable',
      AClassType.Name));
  Instance := nil;

  WriteLn(System.String.Format('  {0}: non-deterministic finalization',
    AClassType.Name));
  Instance := Activator.CreateInstance(AClassType);
  Instance := nil;
  GC.Collect;

  WriteLn(System.String.Format('  {0}: Dispose implementation', AClassType.Name));
  DisposeMethod := AClassType.GetMethod('Dispose');
  if Assigned(DisposeMethod) then
  begin
    Instance := Activator.CreateInstance(AClassType);
    DisposeMethod.Invoke(Instance, []);
  end
  else
    WriteLn(System.String.Format('    {0} doesn''t expose a Dispose method',
      AClassType.Name));
  WriteLn;
end;

begin
  TestClass(typeof(TDontSelfDestruct));
  TestClass(typeof(TNothingsFinal));
  TestClass(typeof(TIndisposed));
  TestClass(typeof(TDisposed));
  TestClass(typeof(TTheFinalWord));
  ReadLn;
end.

The TDontSelfDestruct class simply overrides TObject (or System.Object in .NET speak), and brings nothing new to the party. It shouldn’t have the implicit IDisposable.Dispose implementation added by the Delphi for .NET compiler, and doesn’t implement a .Dispose method.

Because the TNothingsFinal class declares a destructor, it will have the IDisposable.Dispose implementation added by the compiler, with IDisposable.Dispose mapped to TNotingsFinal.Destroy. As with TDontSelfDestruct, it also doesn’t implement a .Dispose method.

The TIndisposed class inherits from System.ComponentModel.Component, so while it doesn’t explicitly implement a .Dispose method, it inherits the implementation from its base class. As with TNothingsFinal, it implements a destructor, so will have an IDisposable.Dispose implementation added.

The TDisposed class inherits from TIndisposed, but it also overrides the Disposing(Dispose: Boolean) method from System.ComponentModel.Component, which gives it the opportunity to perform any additional cleanup when .Dispose is called.

The TTheFinalWord class descends from TNothingsFinal, so will inherit its IDisposable.Dispose implementation added by the compiler, but also adds a finalizer to ensure that IDisposable.Dispose will be called by the garbage collector if the user doesn’t call it explicitly. The destrructor has a call to GC.SuppressFinalize to ensure the finalizer will not be called if the Dispose implementation is explicitly called.

All the above classes are then subjected to a series of tests to exhibit the behaviour discussed above. First the class is instantiated and .Free is called, which should result in any distructors being called. Next the class is instantiated and any IDisposable.Dispose implementation is called. Then we instantiate the class, nil the reference, and invoke a garbage collection, which should result in any finalizers being called by the garbage collector. Finally, we then instantiate the class and call any Dispose method that may be implemented (as opposed to any IDisposable.Dispose implementation). Here is the output after running the app :-

image

Now getting back to the original problem I discovered with the TAdoDbxCommand class, the class that most closely mimics the behaviour I observed is the TIndisposed class. As the output shows, it does have an IDisposable.Dispose implementation, and calling that results in the destructor being called. It also inherits from the System.ComponentModel.Component class, which implements the dispose pattern, and therefore exposes a .Dispose method. Calling TIndisposed.Dispose results in Component.Dispose being called, and not the IDisposable.Dispose method provided by the destructor. And because no finalizer is specified, there is no opportunity for non-deterministic finalization to be invoked by the garbage collector.

The TDisposed class hints at a possible solution to the problem exhibited by the TAdoDbxCommand class. Because it overrides the System.ComponentModel.Component.Dispose(Disposing: Boolean) method, it has an opportunity to perform any resource cleanup when Component.Dispose is called. And because System.ComponentModel.Component implements a finalizer which calls this Dispose method, this same resource cleanup opportunity is available when the garbage collector performs finalization, if Dispose hasn’t explicitly been called.

And it seems that this is the approach that the CodeGear R&D team have chosen to fix this potential memory leak in the TAdoDbxCommand class (and any others which may suffer a similar problem). They will be moving the resource cleanup logic from the class destructor to an overridden implementation of the Dispose(Disposing: Boolean) method. As demonstrated above, this will provide resource cleanup when the class consumer explicitly calls TAdoDbxCommand.Dispose, as well as ensure this cleanup will be done if Dispose is not explicitly called, and the garbage collector invokes the finalizer.

So the moral of the story is this, boys and girls. Calling TSomeClass.Dispose doesn’t necessarily mean that IDisposable.Dispose will be invoked as you may expect it to be. So Delphi for .NET component developers should ensure that if they descend from a class which implements the dispose pattern, they should perform their resource cleanup by overriding the method in the base class responsible for handling the IDisposable.Dispose implementation. And if there is any resource cleanup that absolutely positively has to be done no matter what, they should ensure that their class implements a finalizer which also calls this resource cleanup logic, if this behaviour hasn’t already been added by an ancestor class.

This also means that if you are currently using the AdoDbxClient provider in your .NET applications and you want deterministic finalization of your objects, for now you should call both the .Dispose and .Free methods of your TAdoDbxCommand instances. A brief examination of the Borland.Data.AdoDbxClient.dll assembly in Reflector shows that that the TAdoDbxCommand and TAdoDbxConnection classes are the only ones currently performing resource cleanup, but you may want to follow this convention for other TAdoDbx* classes being consumed.

And as a sidenote to this, TAdoDbxConnection performs its destructor resource cleanup by calling its public .Close method. It is also good practice for application developers to call .Close explicitly in addition to calling .Dispose, as there are no guarantees that component developers will call .Close in their .Dispose implementations. So, depending on how you consume the TAdoDbxConnection class, it may not be necessary to call its .Free method.

Posted by David Clegg on February 12th, 2008 under .NET, Database, Delphi | 6 Comments »


Installing a Delphi for .NET Windows Service

In the borland.public.delphi.language.delphi.dotnet newsgroup today, I noticed a post from someone enquiring how to create a Windows service using Delphi for .NET. While there are plenty of references about this subject on the internet, such as the "Writing a Useful Windows Service in .NET in Five Minutes" blog entry that Craig Stuntz directed the poster to, I was never happy with the approach they used for installing and uninstalling the service. In a nutshell, you are generally advised to embed a System.Configuration.Install.Installer descendant in your application, and then use the .NET Framework InstallUtil utility to perform the actual installation. I vastly prefer the mechanism used by native Delphi service applications, which can be installed and uninstalled by calling themselves with the relevant command line switches. And partly because of that, I created a service application framework which provides me with this ability, along with providing other functionality commonly required when creating Windows service applications.

At the heart of this framework is the TServiceManager class, which is responsible for installing, uninstalling and running service applications. It also allows for a service application to be run as a standard Windows application sitting in the system tray, which is very handy when developing and debugging. This post will concentrate on the installation portion of this class, with the hope that it will provide a starting point for other developers who, like me, wish to find an alternative way to install their Delphi for .NET service applications.

While the generally accepted way to install .NET services is to create a custom installer class and use InstallUtil to install the service, there is no reason why the native Service Control Manager APIs cannot be used instead, so that is the approach I took with my service manager class. In order to do this, we will need to import the following SCM API methods :-

 

[DllImport('advapi32.dll', SetLastError = true)]
function OpenSCManager(lpMachineName, lpSCDB: string; scParameter: Integer): IntPtr; external;

[DllImport('advapi32.dll', SetLastError = true)]
function CreateService(SC_HANDLE: IntPtr; lpSvcName, lpDisplayName: string;
  dwDesiredAccess, dwServiceType, dwStartType, dwErrorControl: Integer;
  lpPathName, lpLoadOrderGroup: string; lpdwTagId: Integer;
  lpDependencies, lpServiceStartName, lpPassword: string): IntPtr; external;

[DllImport('advapi32.dll', SetLastError = true)]
procedure CloseServiceHandle(SCHANDLE: IntPtr); external;

[DllImport('advapi32.dll', SetLastError=true)]
function OpenService(SCHANDLE: IntPtr; lpSvcName: string; dwNumServiceArgs: Integer): IntPtr; external;

[DllImport('advapi32.dll', SetLastError = true)]
function DeleteService(SVHANDLE: IntPtr): Integer; external;

 

These imported methods can then be wrapped up in methods to perform the installation and uninstallation. Here is my implementation for installing a Windows service, along with the declaration of the constants which are referenced by it, or can be used when passing parameters to it :-

const
  SC_MANAGER_CREATE_SERVICE = $0002;
  SERVICE_WIN32_OWN_PROCESS = $00000010;
  SERVICE_ERROR_NORMAL = $00000001;
  SERVICE_INTERACTIVE_PROCESS = $00000100;

  STANDARD_RIGHTS_REQUIRED = $F0000;
  SERVICE_QUERY_CONFIG = $0001;
  SERVICE_CHANGE_CONFIG = $0002;
  SERVICE_QUERY_STATUS = $0004;
  SERVICE_ENUMERATE_DEPENDENTS = $0008;
  SERVICE_START = $0010;
  SERVICE_STOP = $0020;
  SERVICE_PAUSE_CONTINUE = $0040;
  SERVICE_INTERROGATE = $0080;
  SERVICE_USER_DEFINED_CONTROL = $0100;

  SERVICE_ALL_ACCESS =
    STANDARD_RIGHTS_REQUIRED or
    SERVICE_QUERY_CONFIG or
    SERVICE_CHANGE_CONFIG or
    SERVICE_QUERY_STATUS or
    SERVICE_ENUMERATE_DEPENDENTS or
    SERVICE_START or
    SERVICE_STOP or
    SERVICE_PAUSE_CONTINUE or
    SERVICE_INTERROGATE or
    SERVICE_USER_DEFINED_CONTROL;

  SERVICE_BOOT_START = 0;
  SERVICE_SYSTEM_START = 1;
  SERVICE_AUTO_START = 2;
  SERVICE_DEMAND_START = 3;
  SERVICE_DISABLED = 4;

class procedure TServiceManager.DoInstallService(APath, AName, ADisplayName: string;
  AStartMode: ServiceStartMode; AServiceType: Integer);
var
  lControllerHandle: IntPtr;
  lServiceHandle: IntPtr;
begin
  lControllerHandle := OpenSCManager(nil, nil, SC_MANAGER_CREATE_SERVICE);
  if not (lControllerHandle.Equals(FNullPointer)) then
    try
      lServiceHandle := CreateService(
        lControllerHandle, AName, ADisplayName, SERVICE_ALL_ACCESS, AServiceType,
        ConvertStartMode(AStartMode), SERVICE_ERROR_NORMAL, APath,
        nil, 0, nil, nil, nil);
      if lServiceHandle.Equals(FNullPointer) then
        ThrowWin32Error('Error installing service')
      else
        CloseServiceHandle(lServiceHandle);
    finally
      CloseServiceHandle(lControllerHandle);
    end
  else
    ThrowWin32Error('Error obtaining handle to Service Control Manager');
end;

class procedure TServiceManager.ThrowWin32Error(AErrorText: string);
begin
  raise TServiceManagerException.Create(AErrorText,
    Win32Exception.Create(Marshal.GetLastWin32Error));
end;

class function TServiceManager.ConvertStartMode(AStartMode: ServiceStartMode): Integer;
begin
  case AStartMode of
    ServiceStartMode.Manual: Result := SERVICE_DEMAND_START;
    ServiceStartMode.Automatic: Result := SERVICE_AUTO_START;
  else
    Result := SERVICE_DISABLED;
  end;
end;

 

First we need to need to obtain a handle to the Service Control Manager, by calling OpenSCManager. If for some reason the handle is unable to be obtained, a null pointer will be returned. We check this by comparing the result to the FNullPointer field, which is a field variable initialised by calling IntPtr.Create(0). All successful calls to OpenSCManager need to be accompanied with a corresponding call to CloseServiceHandle, in order to avoid a resource leak.

Once we have successfully obtained a handle to the SCM, we can then call CreateService to register the service with the SCM. The implementation above doesn’t allow all parameters to be specified, and instead uses what I deemed to be the most appropriate values for the dwDesiredAccess and dwErrorControl parameters for my use (of course, YMMV, and you may want to make these variable too). Full details of the possible values for all the CreateService parameters can be found in the MSDN documentation.

As with our call to OpenSCManager, we will be returned a handle to the created service if the call to CreateService was successful. And we will also need to match up any successful CreateService call with a call to CloseServiceHandle.

The ThrowWin32Error method referenced in the above code is used to obtain details of why a call to OpenSCManager or CreateService failed, and rethrow a custom exception containing these details.

And here is my implementation to uninstall a Windows service, along with the constants referenced by it :-

 

const
  GENERIC_WRITE = $40000000;
  DELETE = $10000;

class procedure TServiceManager.UninstallService(AServiceName: string);
var
  lControllerHandle: IntPtr;
  lServiceHandle: IntPtr;
begin
  lControllerHandle := OpenSCManager(nil, nil, GENERIC_WRITE);
  if not lControllerHandle.Equals(FNullPointer) then
  try
    lServiceHandle := OpenService(lControllerHandle, AServiceName, DELETE);
    if not (lServiceHandle.Equals(FNullPointer)) then
    try
      if DeleteService(lServiceHandle) = 0 then
        ThrowWin32Error('Error uninstalling service');
    finally
      CloseServiceHandle(lServiceHandle);
    end
    else
      ThrowWin32Error('Error opening service for deletion');
  finally
    CloseServiceHandle(lControllerHandle);
  end
  else
    ThrowWin32Error('Error obtaining handle to Service Control Manager');
end;

 

As with the DoInstallService method, we first need to obtain a handle to the SCM. We then call OpenService to obtain a handle to the service we want to delete. If this handle is successfully retrieved, we then call DeleteService to perform the service deletion. And of course, all the handles retrieved in the SCM API calls need to be closed with calls to CloseServiceHandle in order to prevent resource leaks.

Hopefully this post has provided enough information to allow other .NET developers to implement their own custom service installation strategies, should they feel the default .NET mechanism is not quite to their tastes. If any of the above doesn’t make sense or requires clarification, feel free to ask. I tried to ensure that (apart from the SCM API calls of course) there were no ‘black box’ methods left in the above code snippets, but I’m only on my first cuppa this morning, so no 100% money back guarantees will be offered. :-)

Posted by David Clegg on January 23rd, 2008 under .NET | 3 Comments »


Bill Gates’ last day at Microsoft

A mate of mine sent me a link to this video showing Bill Gates’ last day at Microsoft, and I felt it was too funny not to share.  My favourite moment was when Bill rang up Bono from U2 to convince him that Bill should replace The Edge in the band because he got a high score in Guitar Hero. :-)

Posted by David Clegg on January 7th, 2008 under Humour | Comment now »


Movember Fulltime Score

As promised in my initial Movember blog post, here is the final photo showing how I fared in my Movember quest

movember-final

And here is the one of my fellow Team Delphi team mate, Stuart Clennett

stu-final

Thank you to everybody who generously donated to this worthwhile cause. I’d also like to say a huge thanks to Stuart for joining Team Delphi in this effort. Without his contribution and fundraising efforts, the final Team Delphi donation tally would not have looked anywhere near as good as it did.  

Posted by David Clegg on December 3rd, 2007 under General | Comment now »


CodeRage II free for all

Now I don’t mean that CodeRage II will be a free for all, with topics such as ‘Migrating to Visual Basic 6′ and ‘Writing an Object Persistence Framework in COBOL.NET’, but that free CodeRage admission is now available to everyone.

If you haven’t RSVP’ed to this party yet, I’d strongly urge you to do so now. I’d hate for you to get there late and miss out on all the smoked salmon and nibbly things.

Posted by David Clegg on November 19th, 2007 under General | 1 Comment »


Movember Halftime Progress Report

Well, I’ve reached the halfway point in my quest to increase awareness for mens health issues, so thought I’d provide a quick update as to my progress so far. Here is a photo which captures the current state of my furry little lip dweller.

movember-halfway

I would also like to take the opportunity to issue a huge thanks to Stuart Clennett for taking up the challenge and joining Team Delphi in this noble cause. He has provided me with some photos demonstrating how well he has progressed, and has kindly given me permission to share this progress with you all.

stu_small stu_mo_progress

To sponsor Stuart, navigate to the Movember sponsorship search page, enter his registration number, which is 144896, and provide your credit card details from there. You can also sponsor me by navigating to the same page and entering my registration number, 132268. Alternatively, you can send a cheque made payable to the "Prostate Cancer Foundation of New Zealand" clearly marking the donation as being for Stuarts or my registration number. Please mail cheques to: Movember, PO Box 87 150, Meadowbank 1742, Auckland, New Zealand.

I would also like to point out that its not too late to get in on the fun and join Team Delphi in its Movember quest. For full instructions on how to join, see my previous Movember blog post.

Posted by David Clegg on November 15th, 2007 under General | Comment now »




Server Response from: blog1.codegear.com