Chapter 4
Building applications, components, and libraries

This chapter provides an overview of how to use Delphi to create applications, libraries, and components.

Creating applications

The main use of Delphi is designing and building Windows applications. There are three basic kinds of Windows application:

Windows applications

When you compile a project, an executable (.EXE) file is created. The executable usually provides the basic functionality of your program, and simple programs often consist of only an EXE. You can extend the application by calling DLLs, packages, and other support files from the executable.

Windows offers two application UI models:

In addition to the implementation model of your applications, the design-time behavior of your project and the runtime behavior of your application can be manipulated by setting project options in the IDE.

User interface models

Any form can be implemented as a multiple document interface (MDI) or single document interface (SDI) form. In an MDI application, more than one document or child window can be opened within a single parent window. This is common in applications such as spreadsheets or word processors. An SDI application, in contrast, normally contains a single document view. To make your form an SDI application, set the FormStyle property of your Form object to fsNormal.

For more information on developing the UI for an application, see "Developing the application user interface."

SDI Applications

To create a new SDI application,

  1. Select File|New to bring up the New Items dialog.
  2. Click on the Projects page and select SDI Application.
  3. Click OK.

By default, the FormStyle property of your Form object is set to fsNormal, so Delphi assumes that all new applications are SDI applications.

MDI applications

To create a new MDI application,

  1. Select File|New to bring up the New Items dialog.
  2. Click on the Projects page and select MDI Application.
  3. Click OK.

MDI applications require more planning and are somewhat more complex to design than SDI applications. MDI applications spawn child windows that reside within the client window; the main form contains child forms. Set the FormStyle property of the TForm object to specify whether a form is a child (fsMDIForm) or main form (fsMDIChild). It is a good idea to define a base class for your child forms and derive each child form from this class, to avoid having to reset the child form's properties.

Setting IDE, project, and compilation options

Use Project|Project Options to specify various options for your project. For more information, see the online Help.

Setting default project options

To change the default options that apply to all future projects, set the options in the Project Options dialog box and check the Default box at the bottom right of the window. All new projects will now have the current options selected by default.

Programming templates

Programming templates are commonly used "skeleton" structures that you can add to your source code and then fill in. For example, if you want to use a for loop in your code, you could insert the following template:

for  :=  to  do
begin

end;

To insert a code template in the Code editor, press Ctrl-j and select the template you want to use. You can also add your own templates to this collection. To add a template:

  1. Select Tools|Environment Options.
  2. Click the Code Insight tab.
  3. In the templates section click Add.
  4. Choose a shortcut name and enter a brief description of the new template.
  5. Add the template code to the Code text box.
  6. Click OK.

Console applications

Console applications are 32-bit Windows programs that run without a graphical interface, usually in a console window. These applications typically don't require much user input and perform a limited set of functions.

To create a new console application, choose File|New and select Console Wizard from the New Items dialog box.

Service applications

Service applications take requests from client applications, process those requests, and return information to the client applications. They typically run in the background, without much user input. A web, FTP, or e-mail server is an example of a service application.

To create an application that implements a Win32 service, Choose File|New, and select Service Application from the New Items page. This adds a global variable named Application to your project, which is of type TServiceApplication.

Once you have created a service application, you will see a window in the designer that corresponds to a service (TService). Implement the service by setting its properties and event handlers in the Object Inspector. You can add additional services to your service application by choosing Service from the new items dialog. Do not add services to an application that is not a service application. While a TService object can be added, the application will not generate the requisite events or make the appropriate Windows calls on behalf of the service.

Once your service application is built, you can install its services with the Service Control Manager (SCM). Other applications can then launch your services by sending requests to the SCM.

To install your application's services, run it using the /INSTALL option. The application installs its services and exits, giving a confirmation message if the services are successfully installed. You can suppress the confirmation message by running the service application using the /SILENT option.

To uninstall the services, run it from the command line using the /UNINTALL option. (You can also use the /SILENT option to suppress the confirmation message when uninstalling).

Example: This service has a TServerSocket whose port is set to 80. This is the default port for Web Browsers to make requests to Web Servers and for Web Servers to make responses to Web Browsers. This particular example produces a text document in the C:\Temp directory called WebLogxxx.log (where xxx is the ThreadID). There should be only one Server listening on any given port, so if you have a web server, you should make sure that it is not listening (the service is stopped).

To see the results: open up a web browser on the local machine and for the address, type 'localhost' (with no quotes). The Browser will time out eventually, but you should now have a file called weblogxxx.log in the C:\temp directory.

  1. To create the example, choose File|New and select Service Application from the New Items dialog. You will see a window appear named Service1. From the Internet page of the component palette, add a ServerSocket component to the service window (Service1).
  2. Next, add a private data member of type TMemoryStream to the TService1 class. The interface section of your unit should now look like this:
    interface
    
    uses
      Windows, Messages, SysUtils, Classes, Graphics, Controls, SvcMgr, Dialogs,
      ScktComp;
    
    type
      TService1 = class(TService)
        ServerSocket1: TServerSocket;
        procedure ServerSocket1ClientRead(Sender: TObject;
          Socket: TCustomWinSocket);
        procedure Service1Execute(Sender: TService);
      private
        { Private declarations }
        Stream: TMemoryStream; // Add this line here
      public
        function GetServiceController: PServiceController; override;
        { Public declarations }
      end;
    
    var
      Service1: TService1;
    
  3. Next, select ServerSocket 1, the component you added in step 1. In the Object Inspector, double click the OnClientRead event and add the following event handler:
    procedure TService1.ServerSocket1ClientRead(Sender: TObject;
      Socket: TCustomWinSocket);
    var
      Buffer: PChar;
    
    begin
      Buffer := nil;
    
    while Socket.ReceiveLength > 0 do begin
        try
          Buffer := AllocMem(Socket.ReceiveLength);
          Socket.ReceiveBuf(Buffer^, Socket.ReceiveLength);
          Stream.Write(Buffer^, StrLen(Buffer));
        finally
          FreeMem(Buffer);
        end;
    
      Stream.Seek(0, soFromBeginning);
        Stream.SaveToFile('c:\Temp\Weblog' + IntToStr(ServiceThread.ThreadID) + '.log');
      end;
    end;
    
  4. Finally, select Service1 by clicking in the window's client area (but not on the ServiceSocket). In the Object Inspector, double click the OnExecute event and add the following event handler:
    procedure TService1.Service1Execute(Sender: TService);
    begin
      Stream := TMemoryStream.Create;
      try
        ServerSocket1.Port := 80; // WWW port
        ServerSocket1.Active := True;
    
        while not Terminated do begin
          ServiceThread.ProcessRequests(False);
        end;
    
        ServerSocket1.Active := False;
      finally
        Stream.Free;
      end;
    end;
    

When writing your service application, you should be aware of:

Service threads

Each service has its own thread (TServiceThread), so if your service application implements more than one service you must ensure that the implementation of your services is thread-safe. TServiceThread is designed so that you can implement the service in the TService OnExecute event handler. The service thread has its own Execute method which contains a loop that calls the service's OnStart and OnExecute handlers before processing new requests. Because service requests can take a long time to process and the service application can receive simultaneous requests from more than one client, it is more efficient to spawn a new thread (derived from TThread, not TServiceThread) for each request and move the implementation of that service to the new thread's Execute method. This allows the service thread's Execute loop to process new requests continually without having to wait for the service's OnExecute handler to finish. The following example demonstrates.

Example: This service beeps every 500 milliseconds from within the standard thread. It handles pausing, continuing, and stopping of the thread when the service is told to pause, continue, or stop.

  1. Choose File|New and select Service Application from the New Items dialog. You will see a window appear named Service1.
  2. In the interface section of your unit, declare a new descendant of TThread named TSparkyThread. This is the thread that does the work for your service. The declaration should appear as follows:
    TSparkyThread = class(TThread)
        public
          procedure Execute; override;
      end;
    
  3. Next, in the implementation section of your unit, create a global variable for a TSparkyThread instance:
    var
      SparkyThread: TSparkyThread;
    
  4. Add the following code to the impelementation section for the TSparkyThread Execute method (the thread function):
    procedure TSparkyThread.Execute;
    begin
      while not Terminated do
      begin
        Beep;
        Sleep(500);
      end;
    end;
    
  5. Select the Service window (Service1), and double-click the OnStart event in the Object Inspector. Add the following OnStart event handler:
    procedure TService1.Service1Start(Sender: TService; var Started: Boolean);
    begin
      SparkyThread := TSparkyThread.Create(False);
      Started := True;
    end;
    
  6. Double-click the OnContinue event in the Object Inspector. Add the following OnContinue event handler:
    procedure TService1.Service1Continue(Sender: TService; var Continued: Boolean);
    begin
      SparkyThread.Resume;
      Continued := True;
    end;
    
  7. Double-click the OnPause event in the Object Inspector. Add the following OnPause event handler:
    procedure TService1.Service1Pause(Sender: TService; var Paused: Boolean);
    begin
      SparkyThread.Suspend;
      Paused := True;
    end;
    
  8. Finally, double-click the OnStop event in the Object Inspector and add the following OnStop event handler:
    procedure TService1.Service1Stop(Sender: TService; var Stopped: Boolean);
    begin
      SparkyThread.Terminate;
      Stopped := True;
    end;
    

When developing server applications, choosing to spawn a new thread depends on the nature of the service being provided, the anticipated number of connections, and the expected number of processors on the computer running the service.

Service name properties

The VCL provides classes for creating service applications. These include TService and TDependency. When using these classes, the various name properties can be confusing. This section describes the differences.

Services have user names (called Service start names) that are associated with passwords, display names for display in manager and editor windows, and actual names (the name of the service). Dependencies can be services or they can be load ordering groups. They also have names and display names. And because service objects are derived from TComponent, they inherit the Name property. The following sections summarize the name properties:

TDependency properties

The TDependency DisplayName is both a display name and the actual name of the service. It is nearly always the same as the TDependency Name property.

TService name properties

The TService Name property is inherited from TComponent. It is the name of the component, and is also the name of the service. For dependencies that are services, this property is the same as the TDependency Name and DisplayName properties.

TService's DisplayName is the name displayed in the Service Manager window. This often differs from the actual service name (TService.Name, TDependency.DisplayName, TDependency.Name). Note that the DisplayName for the Dependency and the DisplayName for the Service usually differ.

Service start names are distinct from both the service display names and the actual service names. A ServiceStartName is the user name input on the Start dialog selected from the Service Control Manager.

Debugging services

The simplest way to debug your service application is to attach to the process when the service is running. To do this, choose Run|Attach To Process and select the service application from the list of available processes.

In some cases, this may fail, due to insufficient rights. If that happens, you can use the Service Control Manager to enable your service to work with the debugger:

  1. First create a key called Image File Execution Options in the following registry location:
    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion 
    
  2. Create a subkey with the same name as your service (for example, MYSERV.EXE). To this subkey, add a value of type REG_SZ, named Debugger. Use the full path to the debugger as the string value.
  3. In the Services control panel applet, select your service, click Startup and check Allow Service to Interact with Desktop.

Creating packages and DLLs

Dynamic link libraries (DLLs) are modules of compiled code that work in conjunction with an executable to provide functionality to an application.

Packages are special DLLs used by Delphi applications, the IDE, or both. There are two kinds of packages: runtime packages and design-time packages. Runtime packages provide functionality to a program while that program is running. Design-time packages extend the functionality of the IDE.

For more information on packages, see "Working with packages and components".

When to use packages and DLLs

For most applications written in Delphi, packages provide greater flexibility and are easier to create than DLLs. However, there are several situations where DLLs would be better suited to your projects than packages:

Writing database applications

One of Delphi's strengths is its support for creating advanced database applications. Delphi includes built-in tools that allow you to connect to Oracle, Sybase, Informix, dBASE, Paradox, or other servers while providing transparent data sharing between applications. The Borland Database Engine (BDE) supports scaling from desktop to client/server applications.

Tools, such as the Database Explorer, simplify the task of writing database applications. The Database Explorer is a hierarchical browser for inspecting and modifying database server-specific schema objects including tables, fields, stored procedure definitions, triggers, references, and index descriptions.

Through a persistent connection to a database, Database Explorer lets you

See "Developing database applications" in this manual for details on how to use Delphi to create both database client applications and application servers.

Building distributed applications

Distributed applications are applications that are deployed to various machines and platforms and work together, typically over a network, to perform a set of related functions. For instance, an application for purchasing items and tracking those purchases for a nationwide company would require individual client applications for all the outlets, a main server that would process the requests of those clients, and an interface to a database that stores all the information regarding those transactions. By building a distributed client application (for instance, a web-based application), maintaining and updating the individual clients is vastly simplified.

Delphi provides several options for the implementation model of distributed applications:

Distributing applications using TCP/IP

TCP/IP is a communication protocol that allows you to write applications that communicate over networks. You can implement virtually any design in your applications. TCP/IP provides a transport layer, but does not impose any particular architecture for creating your distributed application.

The growth of the Internet has created an environment where most computers already have some form of TCP/IP access, which simplifies distributing and setting up the application.

Applications that use TCP/IP can be message-based distributed applications (such as Web server applications that service HTTP request messages) or distributed object applications (such as distributed database applications that communicate using Windows sockets).

The most basic method of adding TCP/IP functionality to your applications is to use client or server sockets. Delphi also provides support for applications that extend Web servers by creating CGI scripts or DLLs. In addition, Delphi provides support for TCP/IP-based database applications .

Using sockets in applications

Two VCL classes, TClientSocket and TServerSocket, allow you to create TCP/IP socket connections to communicate with other remote applications. For more information on sockets, see "Working with sockets."

Creating Web server applications

To create a new Web server application, select File|New and select Web Server Application in the New Items dialog box. Then select the Web server application type:

CGI and Win-CGI applications use more system resources on the server, so complex applications are better created as ISAPI or NSAPI applications.

For more information on building Web server applications, see "Creating Internet server applications".

ISAPI and NSAPI Web server applications

Selecting this type of application sets up your project as a DLL. ISAPI or NSAPI Web server applications are DLLs loaded by the Web server. Information is passed to the DLL, processed, and returned to the client by the Web server.

CGI stand-alone Web server applications

CGI Web server applications are console applications that receive requests from clients on standard input, processes those requests, and sends back the results to the server on standard output to be sent to the client.

Win-CGI stand-alone Web server applications

Win-CGI Web server applications are Windows applications that receive requests from clients from an INI file written by the server and writes the results to a file that the server sends to the client.

Distributing applications using COM and DCOM

COM is the Component Object Model, a Windows-based distributed object architecture designed to provide object interoperability using predefined routines called interfaces. .COM applications use objects that are implemented by a different process or, if you use DCOM, on a separate machine.

COM and DCOM

Delphi has classes and wizards that make it easy to create the essential elements of a COM, OLE, or ActiveX application. Using Delphi to create COM-based applications offers a wide range of possibilities, from improving software design by using interfaces internally in an application, to creating objects that can interact with other COM-based API objects on the system, such as the Win95 Shell extensions and DirectX multimedia support.

For more information on COM and Active X controls, see "Overview of COM technologies," "Creating an ActiveX control," and "Distributing a client application as an ActiveX control".

For more information on DCOM, see "Using DCOM connections".

MTS

The Microsoft Transaction Server (MTS) is a an extension to DCOM that provides transaction services, security, and resource pooling for distributed COM applications.

For more information on MTS, see "Creating MTS objects", "Using MTS".

Distributing applications using CORBA

Common Object Request Broker Architecture (CORBA) is a method of using distributed objects in applications. The CORBA standard is used on many platforms, so writing CORBA applications allows you to make use of programs that are not running on a Windows machine.

Like COM, CORBA is a distributed object architecture, meaning that client applications can make use of objects that are implemented on a remote server.

For more information on CORBA, see "Writing CORBA applications".

For instructions on distributing applications using CORBA, see "Deploying CORBA applications".

Distributing database applications

Delphi provides support for creating distributed database applications using the MIDAS technology. This powerful technology includes a coordinated set of components that allow you to build a wide variety of multi-tiered database applications. Distributed database applications can be built on a variety of communications protocols, including DCOM, CORBA, TCP/IP, and OLEnterprise.

For more information about building distributed database applications, see "Creating multi-tiered applications".

Distributing database applications often requires you to distribute the Borland Database Engine (BDE) in addition to the application files. For information on deploying the BDE, see "Deploying database applications".