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 :-
unitCDN.Eco.StringAsDbxVarChar;interfaceusesSystem.Globalization, Eco.Persistence.Default, Eco.Persistence, System.Data;typeStringAsDbxVarChar =class(AbstractStringSingleColumnAttribute, ISingleColumnAttributeMapping)protectedfunctionGetDbType: DbType;virtual;publicfunctionValueType: System.Type;functionColumnType(ALength: Integer):string;procedureValueToParameter(AValue: System.Object; AParameter: IDataParameter);procedureStringToParameter(AValue:string; AParameter: IDataParameter);functionColumnToValue(AColumnValue: TObject): System.Object;override;end; AnsiStringAsDbxVarChar =class(StringAsDbxVarChar)protectedfunctionGetDbType: DbType;override;end;implementationfunctionStringAsDbxVarChar.ValueType: System.Type;beginResult := typeof(System.String);end;functionStringAsDbxVarChar.ColumnType(ALength: Integer):string;beginifALength > 0thenResult := System.String.Format(CultureInfo.InvariantCulture,'VARCHAR({0:d})', [TObject(ALength)])elseResult :='VARCHAR';end;functionStringAsDbxVarChar.GetDbType: DbType;beginResult := DbType.StringFixedLength;end;procedureStringAsDbxVarChar.ValueToParameter(AValue: System.Object; AParameter: IDataParameter);beginifnotAssigned(AParameter)thenraiseArgumentNullException.Create('AParameter'); EnsureType(AValue, typeof(System.String)); AParameter.DbType := GetDbType;ifnotAssigned(AValue)thenAParameter.Value := DbNull.ValueelseAParameter.Value := AValue;end;procedureStringAsDbxVarChar.StringToParameter(AValue:string; AParameter: IDataParameter);beginValueToParameter(AValue, AParameter);end;functionStringAsDbxVarChar.ColumnToValue(AColumnValue: System.Object): System.Object;beginEnsureType(AColumnValue, typeof(System.String));if(DBNull.Value.Equals(AColumnValue))thenResult :=nilelseResult := AColumnValue;end;{ AnsiStringAsDbxVarChar }functionAnsiStringAsDbxVarChar.GetDbType: DbType;beginResult := 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
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.
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’ ![]()
Share This | Email this page to a friend
Posted by David Clegg on March 18th, 2008 under .NET, Database, ECO, Delphi | 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 :-
procedureTForm1.ThreadExecute(State: TObject);varCon: DbConnection; Cmd: DbCommand; Reader: DbDataReader; DS: DataSet; Factory: DbProviderFactory; ConnectionString:string;beginFactory := DbProviderFactories.GetFactory( ConfigurationManager.AppSettings['ProviderName']); ConnectionString := ConfigurationManager.AppSettings['ConnectionString'];whileFRunningdobeginCon := Factory.CreateConnection;tryCon.ConnectionString := ConnectionString; Con.Open;tryCmd := Con.CreateCommand;tryCmd.CommandText :='SELECT * FROM EMPLOYEE'; Reader := Cmd.ExecuteReader;tryDS := DataSet.Create;tryDS.Load(Reader, LoadOption.OverwriteChanges, ['TableName']);finallyDS.Dispose;end;finallyReader.Close; Reader.Dispose;end;finallyCmd.Dispose;end;finallyCon.Close;end;finallyCon.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 :-
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 :-
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 :-
procedureTObjectHelper.Free;varFreeNotify: IFreeNotify;beginif(Self <>nil)and(SelfisIDisposable)thenbeginFreeNotify := IFreeNotify(Self);ifFreeNotify <>nilthenbeginFreeNotify.BeforeFree; FreeNotify :=nil;end;ifAssigned(VCLFreeNotify)thenVCLFreeNotify(Self); (SelfasIDisposable).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 :-
procedureComponent.Dispose;beginself.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:-
procedureComponent.Finalize;begintryself.Dispose(false)finallyinheritedFinalizeendend;
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 :-
programDisposeTest;{$APPTYPE CONSOLE}usesSystem.ComponentModel, System.Reflection, SysUtils;typeTDontSelfDestruct =classend; TNothingsFinal =classpublicdestructorDestroy;override;end; TIndisposed =class(Component)publicdestructorDestroy;override;end; TDisposed =class(TIndisposed) strictprotectedprocedureDispose(ADisposing: Boolean);override;end; TTheFinalWord =class(TNothingsFinal) strictprotectedprocedureFinalize;override;publicdestructorDestroy;override;end;destructorTNothingsFinal.Destroy;beginWriteLn(' TNothingsFinal.Destroy called');inherited;end;destructorTIndisposed.Destroy;beginWriteLn(' TIndisposed.Destroy called');inherited;end;procedureTDisposed.Dispose(ADisposing: Boolean);begininherited; WriteLn(System.String.Format(' TDisposed.Dispose called: {0}', ADisposing.ToString));end;{ TTheFinalWord }destructorTTheFinalWord.Destroy;beginWriteLn(' TTheFinalWord.Destroy called'); GC.SuppressFinalize(Self);inherited;end;procedureTTheFinalWord.Finalize;beginWriteLn(' TTheFinalWord.Finalize called');if(SelfisIDisposable)then(SelfasIDisposable).Dispose;inherited;end;procedureTestClass(AClassType: System.Type);varInstance: TObject; DisposeMethod: MethodInfo;beginWriteLn('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);ifInstanceisIDisposablethen(InstanceasIDisposable).DisposeelseWriteLn(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');ifAssigned(DisposeMethod)thenbeginInstance := Activator.CreateInstance(AClassType); DisposeMethod.Invoke(Instance, []);endelseWriteLn(System.String.Format(' {0} doesn''t expose a Dispose method', AClassType.Name)); WriteLn;end;beginTestClass(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 :-
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.
Share This | Email this page to a friend
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)]functionOpenSCManager(lpMachineName, lpSCDB:string; scParameter: Integer): IntPtr;external; [DllImport('advapi32.dll', SetLastError = true)]functionCreateService(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)]procedureCloseServiceHandle(SCHANDLE: IntPtr);external; [DllImport('advapi32.dll', SetLastError=true)]functionOpenService(SCHANDLE: IntPtr; lpSvcName:string; dwNumServiceArgs: Integer): IntPtr;external; [DllImport('advapi32.dll', SetLastError = true)]functionDeleteService(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 :-
constSC_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_REQUIREDorSERVICE_QUERY_CONFIGorSERVICE_CHANGE_CONFIGorSERVICE_QUERY_STATUSorSERVICE_ENUMERATE_DEPENDENTSorSERVICE_STARTorSERVICE_STOPorSERVICE_PAUSE_CONTINUEorSERVICE_INTERROGATEorSERVICE_USER_DEFINED_CONTROL; SERVICE_BOOT_START = 0; SERVICE_SYSTEM_START = 1; SERVICE_AUTO_START = 2; SERVICE_DEMAND_START = 3; SERVICE_DISABLED = 4;classprocedureTServiceManager.DoInstallService(APath, AName, ADisplayName:string; AStartMode: ServiceStartMode; AServiceType: Integer);varlControllerHandle: IntPtr; lServiceHandle: IntPtr;beginlControllerHandle := OpenSCManager(nil,nil, SC_MANAGER_CREATE_SERVICE);ifnot(lControllerHandle.Equals(FNullPointer))thentrylServiceHandle := CreateService( lControllerHandle, AName, ADisplayName, SERVICE_ALL_ACCESS, AServiceType, ConvertStartMode(AStartMode), SERVICE_ERROR_NORMAL, APath,nil, 0,nil,nil,nil);iflServiceHandle.Equals(FNullPointer)thenThrowWin32Error('Error installing service')elseCloseServiceHandle(lServiceHandle);finallyCloseServiceHandle(lControllerHandle);endelseThrowWin32Error('Error obtaining handle to Service Control Manager');end;classprocedureTServiceManager.ThrowWin32Error(AErrorText:string);beginraiseTServiceManagerException.Create(AErrorText, Win32Exception.Create(Marshal.GetLastWin32Error));end;classfunctionTServiceManager.ConvertStartMode(AStartMode: ServiceStartMode): Integer;begincaseAStartModeofServiceStartMode.Manual: Result := SERVICE_DEMAND_START; ServiceStartMode.Automatic: Result := SERVICE_AUTO_START;elseResult := 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 :-
constGENERIC_WRITE = $40000000; DELETE = $10000;classprocedureTServiceManager.UninstallService(AServiceName:string);varlControllerHandle: IntPtr; lServiceHandle: IntPtr;beginlControllerHandle := OpenSCManager(nil,nil, GENERIC_WRITE);ifnotlControllerHandle.Equals(FNullPointer)thentrylServiceHandle := OpenService(lControllerHandle, AServiceName, DELETE);ifnot(lServiceHandle.Equals(FNullPointer))thentryifDeleteService(lServiceHandle) = 0thenThrowWin32Error('Error uninstalling service');finallyCloseServiceHandle(lServiceHandle);endelseThrowWin32Error('Error opening service for deletion');finallyCloseServiceHandle(lControllerHandle);endelseThrowWin32Error('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.
Share This | Email this page to a friend
Posted by David Clegg on January 23rd, 2008 under .NET | 2 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.
Share This | Email this page to a friend
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
And here is the one of my fellow Team Delphi team mate, Stuart Clennett
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.
Share This | Email this page to a friend
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.
Share This | Email this page to a friend
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.
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.
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.
Share This | Email this page to a friend
Posted by David Clegg on November 15th, 2007 under General | Comment now »ECO: The next big thing in comedy
I often feel a lot of different emotions when using ECO, including happiness, smugness (knowing all the other suckers are coding the hard
way) and frustration (usually due to inexperience :-)). But today it invoked another emotion… laughter.
I was attempting to use the IOclPsService to execute an OCL statement when I was greeted with the following error
Now, I’m not sure what tickled me the most about this error message. The fact that ECO found the error to be strange, or that ECO couldn’t convert 116 to 116. But regardless of the cause, it did make me laugh.
As it turns out, the above error was once again a PEBKAC error, and a result of using DateTime.ToString(’yyyy-mm-dd’) when building an OCL statement, when DateTime.ToString(’yyyy-MM-dd’) was required (my programming equivilent of typing ‘teh’ instead of ‘the’ :-)).
Share This | Email this page to a friend
Posted by David Clegg on November 8th, 2007 under .NET, ECO, Delphi | 2 Comments »Why can’t we call ‘em as we see ‘em?
One of my pet peeves as a developer are exception messages that mask the true reason behind a particular exception. I was reminded of this just now after finally discovering the cause of a bug which I’ve been trying to stomp on for the last hour.
I’ve been writing some classes to talk to an Oracle database, and was having problems getting code calling an Oracle function to work. Every time I tried to execute the function, I was met with the error "‘MyFunctionName’ is not a procedure or is undefined". I double-checked that I was calling the function using the correct name (I was), triple-checked that the function actually existed (it did), and quadruple-checked that the user credentials I was using to log on to the database with had permission to execute the function (they did). I then asked my trusty old friend Google if he could offer any insight into this problem (he could).
As it turns out, when calling an Oracle function using the System.Data.OracleCommand class you have to explictly create a parameter for the return value, even if you aren’t interested in the result. Failure to do so results in the exception I mentioned earlier.
Now, I have no problems with the principle behind this exception, but I do object to the quite misleading text that accompanied it. If I had been told "I’m sorry, Dave. I’m afraid I can’t do that. You must provide me with a parameter for the return value", or even a more concise "Parameter mismatch", I would have been able to identify the true nature of the problem a lot sooner.
So the moral of this story, boys and girls, is that if you have to tell your users that a spade is required, do not throw an EMissingRakeException.
Share This | Email this page to a friend
Posted by David Clegg on November 7th, 2007 under .NET, Database, Delphi | 6 Comments »Is it Movember again already?
During Movember (the month formerly known as November) I’ll be growin a Mo. That’s right I’m bringing the Mo back because I’m passionate about men’s health and the fight against prostate cancer. Why?
- Every year in New Zealand 2,656 men are diagnosed with prostate cancer and about 600 die of the disease, making prostate cancer the second largest cause of male cancer deaths, after lung cancer.
- The average life expectancy of a man in New Zealand is 4 years less than a woman.
Don’t let the above statistics fool you, this is a worldwide problem, and this initiative is a worldwide effort. These stats are simply the ones that were provided to me in the "heres how to plead for help in this worthy cause" e-mail sent to me as part of the registration process.
To sponsor my Mo please go here, enter my registration number which is 132268 and your credit card details. Or you can sponsor me by cheque made payable to the "Prostate Cancer Foundation of New Zealand" clearly marking the donation as being for my Registration Number: 132268. Please mail cheques to: Movember, PO Box 87 150, Meadowbank 1742, Auckland, New Zealand.
I’d also like to throw the gauntlet down to others in the Delphi & CodeGear communities to also support this worthy cause, and grow a bit of face fungus alongside me. To that end I’ve set up a team called Team Delphi, which anyone can easily join. To join Team Delphi, simply follow these steps.
- Point your browser to one of the following links, depending on your country of residence.
People in other countries who may want to participate, feel free to use the New Zealand link above, and click the ‘Use Captain’s Details’ checkbox.
- Enter my registration number, 132268, in the Captains Rego field
- Enter my e-mail address, dclegg@gmail.com, in the Captains’ Email field
- Click the ‘Continue’ link to continue with the registration process and specify your individual details
- Stop shaving
- Start fundraising
If anyone does join Team Delphi in its support for this worthy cause, I’d also love to see you blog about it, with "before" and "after" photos strongly encouraged. To get the ball rolling on this point, here is my "before" photo. I’ll be sure to follow it up with the "after" photo at the end of Movember.
All donations are made directly to the Prostate Cancer Foundation of New Zealand (or their respective equivalent, if registration is done via one of the other international links above), who will use the funds to create awareness, increase support networks for those men who suffer from prostate cancer, fund research and scholarship programs.
For those that have supported Movember in previous years you can be very proud of the impact it has had and can check out the detail at Fundraising Outcomes.
Share This | Email this page to a friend
Posted by David Clegg on November 1st, 2007 under General | 3 Comments »Server Response from: dnrh2.codegear.com

