Delphi Programming Guide
Delphi Programmer 

Menu  Table of contents

Part I - Foundations
  Chapter 1 – Delphi 7 and Its IDE
  Chapter 2 – The Delphi Programming Language
  Chapter 3 – The Run-Time Library
  Chapter 4 – Core Library classes
  Chapter 5 – Visual Controls
  Chapter 6 – Building the User Interface
  Chapter 7 – Working with Forms
Part II - Delphi Object-Oriented Architectures
  Chapter 8 – The Architecture of Delphi Applications
  Chapter 9 – Writing Delphi Components
  Chapter 10 – Libraries and Packages
  Chapter 11 – Modeling and OOP Programming (with ModelMaker)
  Chapter 12 – From COM to COM+
Part III - Delphi Database-Oriented Architectures
  Chapter 13 – Delphi's Database Architecture
  Chapter 14 – Client/Server with dbExpress
  Chapter 15 – Working with ADO
  Chapter 16 – Multitier DataSnap Applications
  Chapter 17 – Writing Database Components
  Chapter 18 – Reporting with Rave
Part IV - Delphi, the Internet, and a .NET Preview
  Chapter 19 – Internet Programming: Sockets and Indy
  Chapter 20 – Web Programming with WebBroker and WebSnap
  Chapter 21 – Web Programming with IntraWeb
  Chapter 22 – Using XML Technologies
  Chapter 23 – Web Services and SOAP
  Chapter 24 – The Microsoft .NET Architecture from the Delphi Perspective
  Chapter 25 – Delphi for .NET Preview: The Language and the RTL
       
  Appendix A – Extra Delphi Tools by the Author
  Appendix B – Extra Delphi Tools from Other Sources
  Appendix C – Free Companion Books on Delphi
       
  Index    
  List of Figures    
  List of tables    
  List of Listings    
  List of Sidebars  

 
Previous Section Next Section

Creating a DLL in Delphi

Besides using DLLs written in other environments, you can use Delphi to build DLLs that can be used by Delphi programs or with any other development tool that supports DLLs. Building DLLs in Delphi is so easy that you might overuse this feature. In general, I suggest you try to build packages instead of plain DLLs. As I'll discuss later in this chapter, packages often contain components, but they can also include plain noncomponent classes, allowing you to write object-oriented code and to reuse it effectively. Of course, packages can contain also simple routines, constants, variables, etc.

As I've already mentioned, building a DLL is useful when a portion of a program's code is subject to frequent changes. In this case, you can often replace the DLL, keeping the rest of the program unchanged. Similarly, when you need to write a program that provides different features to different groups of users, you can distribute different versions of a DLL to those users.

Your First Delphi DLL

As a starting point in exploring the development of DLLs in Delphi, I'll show you a library built in Delphi. The primary focus of this example will be to show the syntax you use to define a DLL in Delphi, but it will also illustrate a few considerations involved in passing string parameters. To start, select the File ® New ® Other command and choose the DLL option in the New page of the Object Repository. Doing so creates a very simple source file that begins with the following definition:

library Project1;

The library statement indicates that you want to build a DLL instead of an executable file. Now you can add routines to the library and list them in an exports statement:

function Triple (N: Integer): Integer; stdcall;
begin
  try
    Result := N * 3;
  except
    Result := -1;
  end;
end;
   
function Double (N: Integer): Integer; stdcall;
begin
  try
    Result := N * 2;
  except
    Result := -1;
  end;
end;
   
exports
  Triple, Double;

In this basic version of the DLL, you don't need a uses statement; but in general, the main project file includes only the uses and exports statements, whereas the function declarations are placed in a separate unit. In the final source code of the FirstDll example, I've changed the code slightly from the version listed here, to show a message each time a function is called. You can accomplish this two ways; the simplest is to use the Dialogs unit and call the ShowMessage function.

The code requires Delphi to link a lot of VCL code into the application. If you statically link the VCL into this DLL, the resulting size will be a few hundred KB. The reason is that the ShowMessage function displays a VCL form that contains VCL controls and uses VCL graphics classes; those indirectly refer to things like the VCL streaming system and the VCL application and screen objects. In this case, a better alternative is to show the messages using direct API calls, using the Windows unit and calling the MessageBox function, so that the VCL code is not required. This code change brings the size of the application down to less than 50 KB.

Note 

This huge difference in size underlines the fact that you should not overuse DLLs in Delphi, to avoid compiling the VCL code in multiple executable files. Of course, you can reduce the size of a Delphi DLL by using run-time packages, as detailed later in this chapter.

If you run a test program like the CallFrst example (described later) using the API-based version of the DLL, its behavior won't be correct. In fact, you can click the buttons that call the DLL functions several times without first closing the message boxes displayed by the DLL. This happens because the first parameter of the MessageBox API call is zero. Its value should instead be the handle of the program's main form or the application form— information you don't have at hand in the DLL.

Overloaded Functions in Delphi DLLs

When you create a DLL in C++, overloaded functions use name mangling to generate a different name for each function. The type of the parameters is included right in the name, as you saw in the CppDll example.

When you create a DLL in Delphi and use overloaded functions (that is, multiple functions using the same name and marked with the overload directive), Delphi allows you to export only one of the overloaded functions with the original name, indicating its parameters list in the exports clause. If you want to export multiple overloaded functions, you should specify different names in the exports clause to distinguish the overloads. This technique is demonstrated by this portion of the FirstDLL code:

function Triple (C: Char): Integer; stdcall; overload;
function Triple (N: Integer): Integer; stdcall; overload;
   
exports
  Triple (N: Integer),
  Triple (C: Char) name 'TripleChar';
Note 

The reverse is possible as well: You can import a series of similar functions from a DLL and define them all as overloaded functions in the Delphi declaration. Delphi's OpenGL.PAS unit contains a series of examples of this technique.

Exporting Strings from a DLL

In general, functions in a DLL can use any type of parameter and return any type of value. There are two exceptions to this rule:

  • If you plan to call the DLL from other programming languages, you should try using Windows native data types instead of Delphi-specific types. For example, to express color values, you should use integers or the Windows ColorRef type instead of the Delphi native TColor type, doing the appropriate conversions (as in the FormDLL example, described in the next section). For compatibility, you should avoid using some other Delphi types, including objects (which cannot be used by other languages) and Delphi strings (which can be replaced by PChar strings). In other words, every Windows development environment must support the basic types of the API, and if you stick to them, your DLL will be usable with other development environments. Also, Delphi file variables (text files and binary file of record) should not be passed out of DLLs, but you can use Win32 file handles.

  • Even if you plan to use the DLL only from a Delphi application, you cannot pass Delphi strings (and dynamic arrays) across the DLL boundary without taking some precautions. This is the case because of the way Delphi manages strings in memory—allocating, reallocating, and freeing them automatically. The solution to the problem is to include the ShareMem system unit both in the DLL and in the program using it. This unit must be included as the first unit of each of the projects. Moreover, you have to deploy the BorlndMM.DLL file (the name stands for Borland Memory Manager) along with the program and the specific library.

In the FirstDLL example, I've included both approaches: One function receives and returns a Delphi string, and another receives as parameter a PChar pointer, which is then filled by the function. The first function is written as usual in Delphi:

function DoubleString (S: string; Separator: Char): string; stdcall;
begin
  try
    Result := S + Separator + S;
  except
    Result := '[error]';
  end;
end;

The second function is quite complex because PChar strings don't have a simple + operator, and they are not directly compatible with characters; the separator must be turned into a string before being adding. Here is the complete code; it uses input and output PChar buffers, which are compatible with any Windows development environment:

function DoublePChar (BufferIn, BufferOut: PChar;
  BufferOutLen: Cardinal; Separator: Char): LongBool; stdcall;
var
  SepStr: array [0..1] of Char;
begin
  try
    // if the buffer is large enough
    if BufferOutLen > StrLen (BufferIn) * 2 + 2 then
    begin
      // copy the input buffer in the output buffer
      StrCopy (BufferOut, BufferIn);
      // build the separator string (value plus null terminator)
      SepStr [0] := Separator;
      SepStr [1] := #0;
      // append the separator
      StrCat (BufferOut, SepStr);
      // append the input buffer once more
      StrCat (BufferOut, BufferIn);
      Result := True;
    end
    else
      // not enough space
      Result := False;
  except
    Result := False;
  end;
end;

This second version of the code is certainly more complex, but the first can be used only from Delphi. Moreover, the first version requires you to include the ShareMem unit and to deploy the file BorlndMM.DLL, as discussed earlier.

Calling the Delphi DLL

How can you use the library you've just built? You can call it from within another Delphi project or from other environments. As an example, I've built the CallFrst project (stored in the FirstDLL directory). To access the DLL functions, you must declare them as external, as with the C++ DLL. This time, however, you can copy and paste the definition of the functions from the source code of the Delphi DLL, adding the external clause, as follows:

function Double (N: Integer): Integer;
  stdcall; external 'FIRSTDLL.DLL';

This declaration is similar to those used to call the C++ DLL. This time, however, you have no problems with function names. Once they are redeclared as external, the functions of the DLL can be used as if they were local functions. Here are two examples, with calls to the string-related functions (an example of the output is visible in Figure 10.2):

Click To expand
Figure 10.2: The output of the CallFrst example, which calls the DLL you've built in Delphi
procedure TForm1.BtnDoubleStringClick(Sender: TObject);
begin
  // call the DLL function directly
  EditDouble.Text := DoubleString (EditSource.Text, ';');
end;
   
procedure TForm1.BtnDoublePCharClick(Sender: TObject);
var
  Buffer: string;
begin
  // make the buffer large enough
  SetLength (Buffer, 1000);
  // call the DLL function
  if DoublePChar (PChar (EditSource.Text), PChar (Buffer), 1000, '/') then
    EditDouble.Text := Buffer;
end;

 
Previous Section Next Section


 


 

Delphi Sources


Copyright © 2004-2024 "Delphi Sources" by BrokenByte Software. Delphi Programming Guide
ร๐๓๏๏เ ยส๎ํ๒เ๊๒ๅ   Facebook   ั๑๛๋๊เ ํเ Twitter