الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

درس كامل عن ال dll باللغة الإنكليزية

مغلق
بدأه imadouzoun في 29 أكتوبر 2005 · 0 رد · 478 مشاهدة · في لغة Delphi
مشاركة: واتساب X فيسبوك تيليجرام
#1
DLL Overview
1   What Is a DLL?
2   Why Should You Use a DLL?
3   Internationalization
4   
5   Anatomy of a DLL Unit
The Basics of DLL Writing
6   Functions and Procedures in DLLs
7   
The exports Keyword
8   Using a DLLProc
9   
Loading DLLs
10   Static Loading
11   Dynamic Loading
12   
Calling Functions and Procedures Located in DLLs
13   Calling Using Static Loading
14   Calling Functions and Procedures Using Dynamic Loading
15   
16   Creating a DLL Project with the Object Repository
Using Forms in DLLs
17   Writing a DLL Containing a Form
18   Calling an MDI Form in a DLL
Using Resources in DLLs
19   Creating a Resource DLL
20   Putting the Resource DLL to Work
21   
22   Summary
Workshop
23   Q&A
24   Quiz
25   Exercises
26   
If you use Windows, you can't escape dynamic
directories. I run Windows NT 4, and I count more
the facts. DLLs are part of everyday life in Windows.
The question, then, is whether you need to create
the following:
27   What DLLs are and why you use them
28   Writing DLLs
29   Calling function and procedures in DLLs
30   Using forms in DLLs
31   Using resource DLLs
By the end of today you will have a good idea of whether DLLs are for you. I suspect that DLLs will fit into your development
plan somewhere down the road.
DLL Overview
Simply put, DLLs are very useful. Today I discuss the benefits of DLLs and how this translates for you, the Delphi
programmer. First, let's explore exactly what a DLL is.
What Is a DLL?
New Term: A DLL (dynamic link library) is one or more pieces of code stored in a file with a .dll extension.
The DLL code can be called from executable programs, but the DLL itself isn't a stand-alone program. You can consider the
DLL a helper file for a main program. Applications that use code in DLLs are called calling applications because they call
functions or procedures in the DLL.
There are two types of DLLs: code DLLs and resource DLLs. This is not an exclusive arrangement. You can have both code
and resources in the same DLL, and there's no problem with that whatsoever. In some cases, though, it's convenient to store
your code in one DLL and your resources in another. I'll talk about why you might want to do that in the section "Using
Resources in DLLs."
Code contained in a DLL can be of two primary forms. The first form is a standalone function that you call from your main
application. You have already done that several times in this book. Remember the code from Day 14, "Advanced
Programming," as follows:
Screen.Cursors[myCursor] := LoadCursor(HInstance, `MYCURSOR');
or this, also from Day 14:
DrawText(Canvas.Handle, Panel.Text,
-1, Temp, DT_CENTER or DT_VCENTER or DT_SINGLELINE);
Both LoadCursor and DrawText are Windows API functions. You probably knew that. But did you know that these functions
are called from DLLs? When you think about it, these functions have to be stored somewhere. But where? In this case, both
LoadCursor and DrawText are contained in the Windows DLL called User32.dll.
And what about all those common dialog boxes you've been using, such as the File Open dialog box, the Print dialog box, and   
the Printer Setup dialog box? They have to come from somewhere. In this case, these dialog boxes are stored as resources in
the file called Comctl32.dll. So, without knowing it, you have been using DLLs all along.
The second form of code contained in a DLL consists of procedures and functions used internally by the DLL. These functions
and procedures perform some task in the DLL and are not visible to the outside world (applications using the DLL).
Using DLLs is one thing, but using and creating your own DLLs is an entirely different matter, right? Not really. After you
create a DLL, you can call functions and procedures in the DLL just as you do when calling Windows API functions. Creating
DLLs is not difficult, so there's nothing to stop you from putting DLLs to work for you.
Why Should You Use a DLL?
That's a good question. After all, what does a DLL give you? DLLs offer these benefits:
l   Effective code reuse
l   Capability to easily share code among many applications
l   Capability to compartmentalize code
l   Internationalization of your application
l   Effective use of Windows resources
Let's take a look at these points individually and see whether I can convince you what a good thing DLLs are.
Effective Code Reuse
Code reuse is a big part of object-oriented programming. After all, why reinvent the wheel? DLLs aid in code reuse in a big
way. Let's say you have a large amount of code you wrote to handle a specific task in Windows. You've worked hard to write
that section of code, so it would be nice if you could reuse it in any of your applications that need it. DLLs enable you to do
exactly that.
What you do is compile all of your code into a DLL. Then all you have to do in order to use the code in any of your programs
is basically load the DLL from an application and begin using the code. What could be easier? (Actually, there are a couple
other steps required, but they are simple and I don't want to get ahead of myself by explaining them now.)
Now you have this code at your fingertips any time you need it. Better yet, you can give the DLL to your programming pals,
and they can use the code, too. Hey, this is starting to sound good. Better yet, you can sell the DLL to other programmers. You
put all that work into it, so you might as well get out of it what you can!
NOTE: You can easily create DLLs that can be called from applications written in C++Builder, Visual Basic,
Visual C++, or other programming environments. Of course, this depends on the code contained in the DLL and
how that code is called, but it can be done.
So, do you see where this is heading? After writing a DLL, you can use it with ease whenever and wherever you want. Getting
at all the goodies contained in your DLL is just a few mouse clicks away.
Sharing Code Among Applications
Sharing code ties in with code reuse but takes it a step farther. Let's say that you are a programmer for a large corporation. You
might have hundreds of users, each with his or her own system. (I'll ignore networking considerations for this example.) Let's
say that you've written five applications for those users. Further, let's assume that each of those five applications uses a
common set of code that compiles to 100KB (a modest figure). If you don't use a DLL, you will have 100KB of code repeated
five times (once for each of the five applications) for a total of 500KB of code. That's code waste, folks.
A better approach is to put the classes in a DLL. Each of the five programs can use the same DLL for the common code. That's
part of the beauty of DLLs: After the DLL is written, all your applications can share it. Each user will get an additional 100KB
DLL on his or her machine, but each of the five applications will be reduced in size by 100KB. The bottom line is that each
user saves 400KB of disk space. Saving 400KB might not sound like a lot, but if you multiply it by hundreds of users, it starts
to add up from a corporate perspective (bean counters tend to look at those types of issues). This is just a simple example. In a
real-world situation, you could easily save several megabytes per user by using DLLs.
What if three of the five programs are running simultaneously? No problem. Each program pulls code from the DLL as
needed, and there are no conflicts. Windows keeps track of who's calling whom and makes sure that it all works together. All
you do is write the DLL and start using it. (If this discussion sounds familiar, it is probably because I used roughly this same
argument when discussing runtime packages on Day 8, "Creating Applications in Delphi.")
Compartmentalizing Code
By keeping your code compartmentalized, you can deal with it more easily when it comes time to update your code. I am not
advocating a separate DLL for each aspect of your program, but when and where it is sensible to break up your program into
related chunks of code, you should do so. There isn't much benefit in breaking an application into DLLs just for the sake of
compartmentalizing the code. If you have libraries of common code that you have written, though, it does make sense to keep
those libraries in separate DLLs. Again, this is most obvious when creating libraries that can be used by any of your programs.
One advantage to this approach is that your applications can be more easily upgraded if necessary. Let's face it. Even the best
of programs go to market with bugs. It's more a question of how many or how severe than a question of whether there are bugs
at all. If you discover bugs in your DLL, you can fix the bug and ship a new DLL to your users rather than recompile and ship
an entire executable file.
At first this might not appear to benefit the types of applications you have written so far. However, you might eventually write
programs several megabytes in size with dozens of source modules. In that case, compartmentalizing makes much more sense.
Internationalization
A few years ago, you could write a program and not worry about the international market. You would write the program and
create menu items, dialog boxes, hint text, error messages, and so forth in your native language, and that was that. You put the
program on the market and didn't think about it again.
The world, however, is becoming smaller. With the explosion of the Internet and the World Wide Web, things are completely
different than they were just a couple of years ago. You can create a demo of your program or maybe a shareware version, and
put it on the Internet. Within hours, minutes even, people all around the world have access to your program. It's exciting and
frightening at the same time. This phenomenon means that you must plan ahead and prepare for translating your program into
other languages.
One way you can do this is to create resource DLLs that contain string resources in various languages. You can have a separate
DLL for each language, or as an alternative, you can have all the string resources in a single DLL and load the correct
language version of each string at runtime. At runtime, you can use the Windows API function LoadString to load the strings
and assign them to various components, as needed.
NOTE: One disadvantage to the Delphi programming model is that it doesn't use traditional resources as other
programming environments do. Resources such as menus are not loaded as resources but rather are contained
within the form's resource. This makes internationalization more difficult and is one of the few times that this
model works against you.
Again, planning ahead can save you a lot of work later. If you plan your application with internationalization in mind, it is
much easier to translate your application into other languages when necessary.
Effective Use of Windows Resources
Today's systems are faster, have more RAM, and have more hard disk space than ever before. It's easy to fall into the mode of
"Hey, I'm only using 2MB of system RAM, so what's the big deal?" The truth is, you should always be conscious of how much
of your users' system resources you are consuming. Here again, DLLs can help. This is really a continuation of the discussion
on sharing code among different applications.
Let's go back to the example I used earlier. (Remember, you have five applications using some common code.) If you don't use
DLLs, when several of your programs are running at the same time, they will all load some of the same code into memory.
You are wasting system resources because each program loads its own copy of the exact same code. Rather than have each
program load the same code, you use a DLL and load the code once. All your applications can use the same code in memory
and reduce the drain on the system.
Anatomy of a DLL Unit
As with any other Pascal unit, a DLL unit follows a particular format. Listing 19.1 shows a minimal DLL unit.
LISTING 19.1. A BASIC DLL UNIT.
library TestDLL;
uses
SysUtils,
Classes,
Forms,
Windows;
procedure SayHello(AForm : TForm);
begin
MessageBox(AForm.Handle, `Hello From a DLL!',
`DLL Message Box', MB_OK or MB_ICONEXCLAMATION);
end;
exports
SayHello;
begin
end.
First notice the library keyword at the top of the unit. The library keyword identifies this unit as a DLL unit. This DLL has a
single procedure called SayHello. The SayHello procedure is no different than any other Object Pascal procedure.
Now turn your attention to the exports keyword near the bottom of the unit. Any procedure or function identifiers in the
exports section are exported from the DLL. In this case the SayHello procedure is exported. Exporting functions and
procedures is discussed in detail in the section "The exports Keyword."
Finally, at the end of the DLL unit you see begin and end keywords. This code block is the DLL's main code block and is
where you put any code that your DLL needs to execute when the DLL initially loads. In many cases (as in this example), you
don't need any initialization code at all, so this code block is empty.
The Basics of DLL Writing
Writing a DLL is not difficult. There are a couple of points to be aware of, but most of it is straight Object Pascal
programming. Let's start with a discussion on the basics of writing DLLs. After that, you can build your first DLL.
Functions and Procedures in DLLs
Functions and procedures in a DLL fall into two basic categories:
l   Functions and procedures local to the DLL
l   Functions and procedures exported from the DLL
A DLL might also contain classes that could, of course, have methods. I'm not going to talk about methods of a class contained
in a DLL at this time, but I'll discuss the other two types of functions and procedures next.
Functions and Procedures Local to the DLL
Functions and procedures called within a DLL require no special handling. You declare this type of function or procedure just
as you do any function or procedure. The function or procedure can be called by other functions within the DLL, but it can't be
called from outside the DLL. In other words, a calling application will not have access to these functions and procedures. They
can be considered private to the DLL, much as private methods of a class are private to the class in which they reside. In effect,
a calling application won't be able to "see" the functions and procedures to even know they exist.
NOTE: In addition to functions and procedures, a DLL can contain global data that all procedures in the DLL can
access. In 16-bit Windows, global data in a DLL is shared among all instances of the DLL. In other words, if one
program changed the global variable x to 100, x would have the value 100 for all other applications using the DLL
as well. In 32-bit Windows, this is not the case. In 32-bit Windows, a separate copy of a DLL's global data is
created for each process that attaches to the DLL.
Functions and Procedures Exported from the DLL
Another category of functions and procedures comprises those that can be called from outside the DLL. These are functions
and procedures that are made public by exporting them from the DLL. They can be called by other functions and procedures in
the DLL or by applications outside the DLL.
NOTE: Functions and procedures in a DLL can be called by executable applications and also by other DLLs. In
other words, one DLL can call functions and procedures in another DLL.
After a function or procedure has been exported, you call it from your application.
The exports Keyword
To export a function or procedure, you use the exports keyword in the DLL. Refer to Listing 19.1 for an example of a DLL
that exports a procedure called SayHello. Because the SayHello procedure is exported, it can be called from any Delphi
application.
Exporting by Name
The most common way of exporting functions and procedures is by name--for example,
exports
SayHello,
DoSomething,
DoSomethingReallyCool;
These procedures are exported by their identifier name. You might have noticed that the exports section has the same syntax as
a uses list. Each identifier to be exported is listed, separated by a comma. A semicolon follows the last identifier in the list.
Exporting by Ordinal Value
You can also export procedures and functions by ordinal value. Procedures and functions are exported by ordinal value by
implementing the index keyword like this:
exports
SayHello index 1,
DoSomething index 2,
DoSomethingReallyCool index 3;
When you import the function in the calling application, you specify the ordinal number (I'll discuss importing functions and
procedures later in the section, "Calling Using Static Loading"). Most of the time you will export functions and procedures by
name and won't bother with exporting by ordinal.
NOTE: Delphi automatically assigns an ordinal value to every exported function and procedure regardless of
whether you specify an index number. Specifying an index number lets you control the ordinal value of the
exported function or procedure.
Exporting a function or procedure is only half the story. When you build the application that calls the function or procedure,
you must import the functions and procedures you want to call from the DLL. I'll talk about importing functions and
procedures later in the section "Calling Functions and Procedures in DLLs."
NOTE: The global variable HInstance, when used in a DLL, will contain the instance handle of the DLL.
To find out whether your code is executing in a DLL or in an application, check the value of the global variable
IsLibrary. IsLibrary is True when called from a DLL and False when called from an application.
TIP: If you are having trouble exporting functions or procedures, run the TDUMP utility on the DLL. TDUMP
produces information containing a section on symbols exported from the DLL. Examining that section will give
you a better idea of where the problem lies. To see just the exported symbols, run TDUMP with the --ee
switch--for example,
tdump --ee mydll.dll
Remember, you can redirect the output of a command-line application to a text file with the > symbol:
tdump --ee mydll.dll > dump.txt
Using a DLLProc
As I mentioned earlier, any initialization code that the DLL needs to execute can be done in the DLL's main code block. That's
easy enough, but what about finalization code? DLLs don't have initialization and finalization sections as other units do. You
might dynamically allocate memory in the DLL's main code block, but where do you free it? The answer is a DLLProc. A
DLLProc is a procedure that is called at certain times during the life of a DLL. I'll explain how to use a DLLProc in just a bit,
but first let me tell you why DLLProc exists.
A DLL receives messages from Windows when it is loaded into memory and just before it is unloaded from memory. It also
receives messages when a process attaches to or detaches from an already-loaded DLL (as in the case of several applications
all using the same DLL). To get these messages, you create a procedure with a specific signature and assign the address of that
procedure to the global variable DLLProc. A typical DLLProc procedure might look like this:
procedure MyDLLProc(Reason: Integer);
begin
if Reason = DLL_PROCESS_DETACH then
{ DLL is unloading. Cleanup code here. }
end;
Just declaring a DLLProc procedure is not enough to ensure that the DLLProc will be used. You must also assign the address
of the procedure to the DLLProc global variable. You do that in the DLL's main code block. For example:
begin
DLLProc := @MyDLLProc;
{ More initialization code. }
end.
This code will be executed as soon as the DLL loads. The DLLProc is now installed and will be called automatically as
processes attach and detach from the DLL or when the DLL unloads. Listing 19.2 shows the source code for a DLL that
implements a DLLProc.
LISTING 19.2. A DLL UNIT THAT USES A DLLProc.
library TestDLL;
uses
SysUtils,
Classes,
Forms,
Windows;
var
SomeBuffer : Pointer;
procedure MyDLLProc(Reason: Integer);
begin
if Reason = DLL_PROCESS_DETACH then
{ DLL is unloading. Cleanup code here. }
FreeMem(SomeBuffer);
end;
procedure SayHello(AForm : TForm);
begin
MessageBox(AForm.Handle, `Hello From a DLL!',
`DLL Message Box', MB_OK or MB_ICONEXCLAMATION);
end;
{ More DLL code here that uses SomeBuffer. }
exports
SayHello;
begin
{ Assign our DLLProc to the DLLProc global variable. }
DLLProc := @MyDLLProc;
SomeBuffer := AllocMem(1024);
end.
As you might have surmised from Listing 19.2, the Reason parameter of the DLLProc procedure contains a value representing
the reason that the DLLProc is being called. Table 19.1 lists the possible values of the Reason parameter.
TABLE 19.1. DLLProc Reason VALUES.
Value Description
DLL_PROCESS_DETACH The DLL is about to unload from memory.
DLL_THREAD_ATTACH A process is attaching to the DLL.
DLL_THREAD_DETACH A process is detaching from the DLL.
NOTE: Earlier in this section I said that a DLL will receive a message from Windows when the DLL is initially
loaded into memory. It is reasonable to expect that when a DLL loads, the DLLProc is called with a Reason
parameter equal to DLL_PROCESS_ATTACH. That doesn't happen, however. Windows defines a
DLL_PROCESS_ATTACH message, but Object Pascal does not pass it on to the DLLProc. Instead, Object
Pascal calls the DLL's main code block when the DLL_PROCESS_ATTACH message is received. Because the
main block is called when DLL_PROCESS_ATTACH is received, it is not necessary for Object Pascal to pass
that message on to the DLLProc.
The DLL_PROCESS_DETACH message is received only once, just before the DLL unloads from memory. (The
DLL_THREAD_ATTACH and DLL_THREAD_DETACH messages might be received many times if a DLL is used by
several processes. Processes can include applications, multiple threads in a single application, or other DLLs.) You can use a
DLLProc, then, to perform any cleanup code required for the DLL.
Loading DLLs
Before you use a function or procedure in a DLL, you first have to load the DLL into memory. Loading DLLs at runtime can
be accomplished in two ways, as follows:
l   Static loading
l   Dynamic loading
Both methods have their advantages and disadvantages. I'll explain the differences between static loading and dynamic loading
next.
Static Loading
Static loading means that your DLL is automatically loaded when the application that calls the DLL is executed. To use static
loading, you declare a function or procedure that resides in the DLL with the external keyword (more on that in the section
"Calling Using Static Loading"). The DLL is automatically loaded when the application loads, and you call any functions or
procedures exported from the DLL just as you would any other function or procedure. This is by far the easiest way to use
code contained in a DLL. The disadvantage to this approach is that if a DLL that the program references is missing, the
program will refuse to load.
Dynamic Loading
Dynamic loading means that you specifically load the DLL when needed and unload it when you are done with it. This type of
DLL loading has its advantages and disadvantages, too. One advantage is that the DLL is in memory only as long as you need
it, so you are making more efficient use of memory. Another advantage is that your application will load more quickly when
using dynamic loading because not all the code needed to run the program is loaded when the application initially loads.
The primary disadvantage to using the dynamic loading approach is that it is a bit more work for you. First, you need to load
the DLL with the Windows API function LoadLibrary. Then, when you are done with the DLL, you unload it with
FreeLibrary. Further (and this is where the work comes in), you need to use the GetProcAddress function to obtain a pointer to
the function or procedure you want to call. To say that this approach can be a tad confusing would be an understatement. The
following section describes how to call procedures and functions in DLLs using both static and dynamic loading.
Calling Functions and Procedures Located in DLLs
The method you use to call a function or procedure in a DLL depends on whether the DLL was statically or dynamically
loaded.
Calling Using Static Loading
Calling functions and procedures from DLLs that are statically loaded is simple.
First the calling application must contain a declaration for the function or
procedure. After that, you call the function or procedure as you do a regular
function
or procedure. To import a function or procedure contained in a DLL, use the external
modifier in the function or procedure declaration. For example, given the SayHello
procedure shown earlier, the declaration in the calling application would look like
this:
procedure SayHello(AForm : TForm); external `testdll.dll';
The external keyword tells the compiler that the procedure can be found in a DLL (TESTDLL.DLL in this case). The actual
procedure call looks no different than any other:
SayHello(Self);
After you have properly imported the function or procedure, you call it like any other function or procedure. This step
presumes, of course, that the procedure has been exported from the DLL as described earlier.
CAUTION: When you declare functions and procedures contained in a DLL, be sure you get spelling and
capitalization right. This is one place in Object Pascal programming where capitalization counts! If you misspell
or improperly capitalize a function or procedure name, you will get an exception at runtime and the application
will refuse to load.
USING THE external KEYWORD
The external keyword has three flavors. Using external you can import a procedure or function in one of three
ways:
l   By actual name
l   By ordinal value
l   By renaming
The first way of importing, by actual name, is the method you have seen used up to this point. You simply declare
the name of the function or procedure exactly as it appears in the DLL--for example,
procedure SayHello(AForm : TForm); external `testdll.dll';
The second way of importing, by ordinal value, requires you to specify the ordinal value of the function or
procedure as exported from the DLL:
procedure SomeOrdinalProcedure; external `testdll.dll' index 99;
In this case, I am importing the procedure that is exported from the DLL as index 99. I can name the procedure
anything I want in the calling application as long as the signature matches that of the procedure in the DLL. I can
name the procedure anything I want because the procedure is exported by ordinal value and not by the procedure
name.
The third method, by renaming, enables me to import the procedure by it original name, but give the procedure a
new name in the calling application. It looks like this:
procedure CoolProcedure;
external `testdll.dll' name `DoSomethingReallyCool';
Here I am importing a procedure called DoSomethingReallyCool and renaming it to CoolProcedure.
Of these three methods, the first, importing by name, is the most commonly used.
The trick in writing and using DLLs, then, is in getting the imports and exports right. Otherwise, there's nothing to it. Unless
you need the flexibility that dynamic loading provides, you should almost always opt for static loading.
Calling Functions and Procedures Using Dynamic Loading
Calling functions and procedures in DLLs that are dynamically loaded isn't a lot of fun. It requires that you declare a pointer to
the function or procedure in the DLL, and pointers to functions can be confusing. To illustrate, let's say you have a procedure
in a DLL called SayHello (the SayHello procedure gets a workout in this chapter). It would look like this in the DLL's source
code:
procedure SayHello(AForm : TForm);
begin
MessageBox(AForm.Handle, `Hello From a DLL!',
`DLL Message Box', MB_OK or MB_ICONEXCLAMATION);
end;
To call this procedure from your program, you first have to declare a type that describes the procedure:
type
TSayHello = procedure(AForm : TForm);
Now that you've done that, you must load the DLL, use GetProcAddress to get a pointer to the procedure, call the procedure,
and, finally, unload the DLL. Here's how the whole operation looks:
var
DLLInstance : THandle;
SayHello : TSayHello;
begin
{ Load the DLL. }
DLLInstance := LoadLibrary(`testdll.dll');
{ Get the address of the procedure. }
@SayHello := GetProcAddress(DLLInstance, `SayHello');
{ Call the procedure. }
SayHello(Self);
{ Unload the DLL. }
FreeLibrary(DLLInstance);
end;
As I said, loading a DLL dynamically is a bit more work. Still, when you need to load a DLL at runtime, that's the way you
have to do it. Note that this code is pared down a bit for clarity. You will almost always add some error-checking code to
ensure that the DLL loads correctly and that GetProcAddress returns a good address. Here's how the code looks with error
checking code in place:
procedure TForm1.DynamicLoadBtnClick(Sender: TObject);
type
TSayHello = procedure(AForm : TForm);
var
DLLInstance : THandle;
SayHello : TSayHello;
begin
DLLInstance := LoadLibrary(`testdll.dll');
if DLLInstance = 0 then begin
MessageDlg(`Unable to load DLL.', mtError, [mbOK], 0);
Exit;
end;
@SayHello := GetProcAddress(DLLInstance, `SayHello');
if @SayHello <> nil then
SayHello(Self)
else
MessageDlg(`Unable to locate procedure.', mtError, [mbOK], 0);
FreeLibrary(DLLInstance);
end;
As you can see, you probably won't use dynamic loading of DLLs unless you absolutely have to.
Creating a DLL Project with the Object Repository
Creating a DLL in Delphi is accomplished through the Object Repository. (The Object Repository is covered on Day 8,
"Creating Applications in Delphi."). To create a DLL project, follow these steps:
1. Choose File|New to display the Object Repository.
2. Double-click the DLL icon.
Were you expecting it to be more complicated than that? Delphi creates the DLL project and displays the Code Editor. The file
in the edit window looks like this:
library Project2;
{ Important note about DLL memory management: ShareMem must be the
first unit in your library's USES clause AND your project's (select
View-Project Source) USES clause if your DLL exports any procedures or
functions that pass strings as parameters or function results. This
applies to all strings passed to and from your DLL--even those that
are nested in records and classes. ShareMem is the interface unit to
the DELPHIMM.DLL shared memory manager, which must be deployed along
with your DLL. To avoid using DELPHIMM.DLL, pass string information
using PChar or ShortString parameters. }
uses
SysUtils,
Classes;
begin
end.
Now you can begin adding code to the DLL. Be sure to add any exported procedure or function names to the exports section.
Write any standalone functions or procedures that you need in your DLL. When you are finished adding code, you can choose
Compile or Build from the Project menu to build the DLL.
COMMENTING THE DLL UNIT
I want to take a moment to explain the large comment block at the top of a DLL unit. This message is telling you
that if your DLL has exported functions and procedures that take a long string type as a parameter or if your
exported functions return a long string, you must do the following:
Put ShareMem at the beginning of the uses list in both the DLL source and the calling application's main
unit. Be sure that ShareMem comes before other units in the uses list.
l   
Ship the Borlndmm.dll file with your DLL. Notice that I said Borlndmm.dll and not Delphimm.dll, as the
comments in the DLL code indicate. Borland changed the name of the memory manager DLL but neglected
to change the comments generated when you create a new DLL unit. The comments are not completely
misleading, however, because both Delphimm.dll and Borlndmm.dll are included with Delphi 4.
l   
To avoid this requirement, just be sure that your DLL functions and procedures don't take any long string
parameters and that your DLL functions don't return a long string. Instead of using a long string, you can use a
PChar or a short string. For example, rather than use
procedure MyProcedure(var S : string);
begin
{ Procedure code here. }
end;
use this:
procedure MyFunction(S : PChar);
begin
{ Procedure code here. }
end;
This situation is easy enough to work around, so you should never have the need for Borlndmm.dll. You just need
to be aware of the restrictions placed on using the long string in DLL functions and procedures. Note that you can
use long strings in functions and procedures used within the DLL itself without the need for Borlndmm.dll. This
applies only to exported functions and procedures.
NOTE: I always remove the comments about Borlndmm.dll when I create a new DLL. You can certainly leave
them in the DLLs source code if you want. After you understand what the comments are saying, you won't need
them anymore, so you might as well remove them.
The next three listings contain code that illustrates the concepts discussed thus far. Listing 19.3 contains a DLL that will be
called statically from a calling application. Listing 19.4 contains a DLL that is called dynamically from the calling application
and implements a DLLProc. Finally, Listing 19.5 contains a program that calls the two DLLs. The program has a form with
four buttons that call various procedures in the two DLLs. The book's code (available at http://www.mcp.com/info) contains
the sample programs for the projects in these three listings.
LISTING 19.3. TestDll.dpr.
library TestDLL;
uses
SysUtils,
Classes,
Forms,
Windows;
procedure SayHello(AForm : TForm);
begin
MessageBox(AForm.Handle, `Hello From a DLL!',
`DLL Message Box', MB_OK or MB_ICONEXCLAMATION);
end;
procedure DoSomething;
begin
MessageBox(0, `This procedure was exported by ordinal.',
`DLL Message Box', MB_OK or MB_ICONEXCLAMATION);
end;
procedure DoSomethingReallyCool;
begin
MessageBox(0, `Something really cool.',
`DLL Message Box', MB_OK or MB_ICONEXCLAMATION);
end;
exports
SayHello,
DoSomething index 99,
DoSomethingReallyCool;
begin
end.
LISTING 19.4. DynLoad.dpr.
library TestDLL;
uses
SysUtils,
Classes,
Forms,
Windows;
procedure MyDLLProc(Reason: Integer);
begin
if Reason = DLL_PROCESS_DETACH then
{ DLL is unloading. Cleanup code here. }
MessageBox(0, `DLL is unloading!',
`DLL Message', MB_OK or MB_ICONEXCLAMATION);
end;
procedure SayHelloDyn(AForm : TForm);
begin
MessageBox(AForm.Handle, `Hello From a DLL!' + #13 +
`This DLL was loaded dynamically',
`DLL Message', MB_OK or MB_ICONEXCLAMATION);
end;
exports
SayHelloDyn;
begin
DLLProc := @MyDLLProc;
end.
LISTING 19.5. CallDllU.pas.
unit CallDLLU;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls;
type
TForm1 = class(TForm)
HelloBtn: TButton;
OrdBtn: TButton;
DynamicLoadBtn: TButton;
NamedBtn: TButton;
procedure HelloBtnClick(Sender: TObject);
procedure OrdBtnClick(Sender: TObject);
procedure DynamicLoadBtnClick(Sender: TObject);
procedure NamedBtnClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
{ A procedure imported by name. }
procedure SayHello(AForm : TForm);
external `testdll.dll';
{ A procedure imported by ordinal. }
procedure OrdinalProcedure;
external `testdll.dll' index 99;
{ A procedure imported and renamed. }
procedure CoolProcedure;
external `testdll.dll' name `DoSomethingReallyCool';
implementation
{$R *.DFM}
procedure TForm1.HelloBtnClick(Sender: TObject);
begin
SayHello(Self);
end;
procedure TForm1.OrdBtnClick(Sender: TObject);
begin
OrdinalProcedure;
end;
procedure TForm1.NamedBtnClick(Sender: TObject);
begin
CoolProcedure;
end;
procedure TForm1.DynamicLoadBtnClick(Sender: TObject);
type
TSayHello = procedure(AForm : TForm);
var
DLLInstance : THandle;
SayHello : TSayHello;
begin
{ Load the DLL. }
DLLInstance := LoadLibrary(`DynLoad.dll');
{ If loading fails then bail out. }
if DLLInstance = 0 then begin
MessageDlg(`Unable to load DLL.', mtError, [mbOK], 0);
Exit;
end;
{ Assign the procedure pointer. }
@SayHello := GetProcAddress(DLLInstance, `SayHelloDyn');
{ If the procedure was found then call it. }
if @SayHello <> nil then
SayHello(Self)
else
MessageDlg(`Unable to locate procedure.', mtError, [mbOK], 0);
{ Unload the DLL. }
FreeLibrary(DLLInstance);
end;
end.
Using Forms in DLLs
Your DLLs can contain forms as well as code. There isn't much difference in the way the form is created compared to the way
a form in an application is created. First, I'll explain how to write a DLL containing a form. After that, I'll talk about the special
case of using an MDI form in a DLL.
Writing a DLL Containing a Form
Writing a DLL that contains forms is not much more difficult than writing a DLL that
contains code only. I believe in learning by example, so in this section, you build a
DLL containing a form. Perform these steps:
1. Create a new DLL project (remove the comments at the top of the DLL, if desired). Save the DLL as MyForms.dpr.
2. Choose File|New Form to create a new form for the DLL project. Add any components you want to the form.
3. Change the Name property of the new form to DLLForm. Save the form as DLLFormU.pas.
4. Switch back to the DLL source unit. Add a function called ShowForm that creates and shows the form. Use this code:
function ShowForm : Integer; stdcall;
var
Form : TDLLForm;
begin
Form := TDLLForm.Create(Application);
Result := Form.ShowModal;
Form.Free;
end;
5. Add an exports section to the DLL and export the ShowForm function. Here's how the exports section should look:
exports
ShowForm;
6. Choose Project|Build MyForms to build the DLL. Save the DLL.
That's all there is to creating the DLL. Notice that the ShowForm function is declared with the stdcall keyword. This keyword
tells the compiler to export the function using the standard call calling convention. Exporting the function as stdcall enables
this DLL to be used by development environments other than Delphi.
New Term: Calling conventions specify how the compiler should pass arguments when calling functions and procedures. The
five primary calling conventions are stdcall, cdecl, pascal, register, and safecall. See the "Calling Conventions" topic in the
Delphi help file for more information on calling conventions.
Notice also that the return value of the DLL's ShowForm function is the value returned from the ShowModal function. This
enables you to return some status information to the calling application. Listing 19.6 shows the code for the DLL.
LISTING 19.6. THE FINISHED DLL.
library MyForms;
uses
SysUtils,
Classes,
Forms,
DLLFormU in `DLLFormU.pas' {DLLForm};
function ShowForm : Integer; stdcall;
var
Form : TDLLForm;
begin
Form := TDLLForm.Create(Application);
Result := Form.ShowModal;
Form.Free;
end;
exports
ShowForm;
begin
end.
Now the calling application can declare the ShowForm function and call the function. Listing 19.7 shows a Delphi application
that calls the MyForms DLL.
LISTING 19.7. AN APPLICATION TO CALL THE MyForms DLL.
unit TestAppU;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
function ShowForm : Integer; stdcall;
external `myforms.dll';
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowForm;
end;
end.
Notice that I used the stdcall keyword again when I declared the function in the calling application. Because the function was
exported from the DLL with stdcall, it must also be imported with stdcall. You must always import a function or procedure
with the same calling convention that you used to export it.
NOTE: If your Delphi DLLs are going to be used only by Delphi applications, you don't need to worry about
using stdcall when exporting your functions and procedures. If, however, there is a chance that your DLL will be
called from a wide range of applications, you should export your functions and procedures with stdcall.
Calling an MDI Form in a DLL
Having an MDI child form in a DLL is a special case. (MDI forms are discussed on Day 4, "The Delphi IDE Explored.") Let's
say you have a Delphi application and the main form is an MDI form. If you try to use an MDI child form contained in a DLL,
you will get an exception from VCL that says No MDI forms are currently active. "What? But I have an MDI form in my
application!" Not according to VCL, you don't. Here's what happens.
When you attempt to show your MDI child form, VCL checks whether the Application object's MainForm property is valid. If
the MainForm property is not valid, an exception is thrown. So what's the problem? The MainForm is valid, right? The
problem is that the DLL also contains an Application object, and it is the DLL Application object's MainForm that is checked,
not the application's MainForm. Because a DLL doesn't have a main form, this check will always fail.
The fix for this problem is to assign the DLL's Application object to the Application object of the calling application.
Naturally, this will work only if the calling application is a VCL application. That's not the whole story, though. Before the
DLL unloads, you must also set the DLL's Application object back to its original state. This enables the VCL memory manager
to clean up any memory allocated for the DLL. It means that you have to store the DLL's Application object pointer in a global
variable in the DLL so that it can be restored before the DLL unloads.
Let's back up for a moment and review the steps required to show an MDI child form in a DLL:
1. Create a global TApplication pointer in the DLL.
2. Save the DLL's Application object in the global TApplication pointer.
3. Assign the calling application's Application object to the DLL's Application object.
4. Create and show the MDI child form.
5. Reset the DLL's Application object before the DLL unloads.
The first step is easy. Just place the following code near the top of your DLL's source unit:
var
DllApp : TApplication;
Be sure that you place the var keyword below the uses list in the DLL source unit.
Next, create a procedure that will do the TApplication switch and create the child form. The procedure will look like this:
procedure ShowMDIChild(MainApp : TApplication);
var
Child : TMDIChild;
begin
if not Assigned(DllApp) then begin
DllApp := Application;
Application := MainApp;
end;
Child := TMDIChild.Create(Application.MainForm);
Child.Show;
end;
Examine this code for a moment. When you call this procedure, you will pass the Application object of the calling application.
If the DllApp pointer has not yet been assigned, you assign the DLL's Application object to the temporary pointer. Then you
assign the Application object of the calling application to the DLL's Application object. This check ensures that the
Application object is set only once. After that, the MDI child form is created, passing the calling application's MainForm as the
owner. Finally, the form is shown.
All that remains is to reset the DLL's Application pointer before unloading the DLL. You can use a DLLProc to restore the
DLL's application pointer:
procedure MyDLLProc(Reason: Integer);
begin
if Reason = DLL_PROCESS_DETACH then
{ DLL is unloading. Restore the Application pointer. }
if Assigned(DllApp) then
Application := DllApp;
end;
Remember, you saved the DLL's Application pointer earlier, so now you just restore it.
As you can see, placing an MDI child form in a DLL requires extra work, but it is certainly possible. The code for the book has
an application project called MDIApp and a DLL project called MyForms. These two projects illustrate using an MDI form in
a DLL.
Showing a DLL Form from a Non-Delphi Application
Calling a form from a non-VCL application requires a slightly different approach. What you have to do is create a standalone
function in the DLL that is called by the calling application. This function can be called from any program provided you
declare the function with stdcall. Within the function body, you then create and execute the form. The function looks like this:
function ShowForm : Integer; stdcall;
var
Form : TMyForm;
begin
Form := TMyForm.Create(Application);
Result := Form.ShowModal;
Form.Free;
end;
Notice that Application is passed for the form's parent. This is the DLL's Application object and serves as the owner of the
form. Although I explicitly free the TMyForm object, it isn't strictly necessary because the DLL's Application object will
delete the form if I neglect to.
Using Resources in DLLs
Sometimes it's convenient to have resources contained in a DLL. I've already mentioned internationalization and how a DLL
can be used to make your program more easily portable to languages other than the one for which it was designed. Let's say
you have an instructions screen for your application and that those instructions are contained in five strings in a DLL. The
strings might be named IDS_INSTRUCTION1, IDS_INSTRUCTION2, and so on. You could load and display the strings like
this:
LoadString(DllInstance, IDS_INSTRUCTION1, Buff, SizeOf(Buff));
InstructionLabel1.Caption := Buff;
The first parameter of the LoadString function contains the instance handle of the module where the strings can be found. The
second parameter contains the ID number of the resource to load. You could create DLLs that contain string resources in
several different languages and simply load the appropriate DLL based on a user selection. The code might look like the
following:
var
DLLName : String;
begin
case Language of
laFrench : DllName := `french.dll';
laGerman : DllName := `german.dll';;
laSpanish : DllName := `spanish.dll';
laEnglish : DllName := `english.dll';
end;
DllInstance := LoadLibrary(PChar(dllName));
end;
You only have to load the correct DLL; the rest of the code remains the same (assuming the strings have the same identifiers in
each of the DLLs, of course). This is just one example of using resources contained in a DLL. You will find many uses for this
technique.
Creating a Resource DLL
You can create a DLL that contains only resources, or you can mix code and resources in the same DLL. Placing resources in a
DLL is much the same as adding resources to an application. To create a resource DLL, start a new DLL project and then add a
line in the DLL to link the resources:
{$R RESOURC.RES}
That's all there is to creating a resource DLL. I discussed creating resource files on Day 8, so I won't go over that ground again
here.
Putting the Resource DLL to Work
You must have the DLL's instance handle before you can access resources contained in the DLL. If your DLL contains
resources only, you will load the DLL dynamically. If your DLL contains resources and code, you might choose to load the
DLL statically. If you load the DLL statically, you will still have to call LoadLibrary to get the instance handle:
DllInstance := LoadLibrary(`resource.dll');
Now you can use the instance handle wherever required. The following code loads a bitmap resource contained in a DLL into
an Image component:
procedure TMainForm.FormCreate(Sender: TObject);
begin
DLLInstance := LoadLibrary(`resource.dll');
if DLLInstance <> 0 then begin
Image.Picture.Bitmap.
LoadFromResourceName(DLLInstance, `ID_BITMAP1');
FreeLibrary(DLLInstance);
end else
MessageDlg(`Error loading resource DLL.',
mtError, [mbOk], 0);
end;
Actually, there's not much left to say. To review, though, you know that you can load resource DLLs either statically or
dynamically. Either way you have to use LoadLibrary to get the instance handle of the DLL. Don't forget to call FreeLibrary
when you are done with the DLL or before your application closes.
NOTE: Using dynamic loading has the added advantage of allowing your application to load faster. In many
cases you load the resource DLL only when it's needed and unload it when it's no longer needed. This results in
your application using less memory than when the resources are contained in the executable file. The downside to
static loading is that your users might see a slight pause when the resource DLL loads. Try to anticipate as much
as possible and load the resource DLL when it is least likely to be noticed.
Remember JumpingJack from Day 8? The book's code has a version of JumpingJack that loads the bitmap, sound, and string
resources from a DLL. Check out that program for an example of using resources in a DLL.
Summary
Using dynamic link libraries isn't as difficult as it might first appear. DLLs are a great way to reuse code. After you create a
DLL, you can call it from any application that needs the functionality the code in that DLL provides. Placing VCL forms in a
DLL and then calling those forms from a non-Delphi application is a powerful feature. This means that you can create forms
that others can call from just about any type of Windows application, whether it is an application written in OWL, MFC,
straight C, Visual Basic, and so on. Using resources in DLLs is effective if you have a lot of resources in your application and
you want to control when and where those resources are loaded.

تم تعديل هذه المشاركة بواسطة romanof في 29 أكتوبر 2005 في 02:41

دروس و كتب مفيدة----------------------كتيب الإجرائيات المخزنةتعرف على اللغة Transact SQL <<-->> كتيب عن تنصيب قواعد البيانات Sql Server باللغة العربية كتيب عن إنشاء قواعد البيانات في SQL Server باللغة العربية <<-->> درس مع الأمثلة عن المؤشرات و التعامل معها في الـ T-SQLالعبارات الشرطية و الحلقات في T-SQL <<-->> شرح لبعض الـ Extended Stored Procedure استخدام مكتبات الـ Dot Net الخاصة بك في الـ Sql Server 2005 <<-->> التعامل مع المصفوفات من خلال الـ T-SQLتعابير الجداول الشائعة في SQL Server 2005 <<-->>الاتصال بالأداة SQL Server Management Studioحلول سريعة و لمحات برمجية --------------------------------التصدير إلى إكسل : طريقة أولى : طريقة بسطر واحد من الكود <<-->> طريقة ثانية : طريقة تحتاج إنشاء إجرائية مخزنةً تجهيز بيانات جدول بالصيغة XML تمهيداً لحفظها كملف <<-->> تصدير البيانات إلى ملفات نصية برمجياً إغلاق جميع الإتصالات المفتوحة بقاعدة بيانات محددة .. تمهيداً لاسترجاعها أو حذفها <<-->> الاستعلام بناء على وقت مخزن بالصيغة العربية ضمن حقل نصي ..حذف السجلات المتكررة و إبقاء عدد محدد منها في جدول.. <<-->> أسرع طريقة لحذف سجلات جدول كيف احصل على اسم جهازي والباسوورد باستخدام T-SQL <<-->> حشر أنواع الصور المختلفة في حقل من نوع Image باستخدام إجرائية مخزنة .كيف تعرف أنواع الصور المخزنة في حقل من نوع Image .. <<-->> إجرائيات مخزنة ببارامترات ديناميكية مفيدة جداً للاستعلامات استخدام رقم الحقل بدلاً من الإسم في عبارات الإستعلام <<-->> من هو أضخم الجداول في قاعدة بياناتي ؟معرفة زمن تنفيذ الإجرائيات ( لتختار الطريقة الأسرع بين إجرائياتك المخزنة ) <<-->> التعامل مع الـ Rules برمجياً الحصول على آخر صف في الجدول <<-->> قراءة ملفات XML باستخدام T-SQL برمجياً (مثال مرفق)سحب بيانات ملف إكسل إلى الـ SQL Server <<-->> تشفير النسخة الاحتياطية بكلمة مرور <<-->> التخلص من البيانات المكررة <<-->> حل مشكلة التاريخ الهجريDifferential Backup بالتعريف---------- لا إله إلا الله .. محمد رسول الله

أخوكم في الله..  Imad Ozone

هذا الموضوع مغلق.

مواضيع مشابهة