⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch19.htm

📁 delphi自学的好教材!特别适合刚刚起步学习delphi的人员!同样对使用者具有参考价值!
💻 HTM
📖 第 1 页 / 共 5 页
字号:
you need it, so you are making more efficient use of memory. Another advantage isthat your application will load more quickly when using dynamic loading because notall the code needed to run the program is loaded when the application initially loads.</P><P>The primary disadvantage to using the dynamic loading approach is that it is abit more work for you. First, you need to load the DLL with the Windows API functionLoadLibrary. 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 GetProcAddressfunction to obtain a pointer to the function or procedure you want to call. To saythat this approach can be a tad confusing would be an understatement. The followingsection describes how to call procedures and functions in DLLs using both staticand dynamic loading.</P><P><H2><A NAME="Heading13"></A>Calling Functions and Procedures Located in DLLs</H2><P>The method you use to call a function or procedure in a DLL depends on whetherthe DLL was statically or dynamically loaded.</P><P><H3><A NAME="Heading14"></A>Calling Using Static Loading</H3><PRE>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:</PRE><PRE>procedure SayHello(AForm : TForm); external `testdll.dll';</PRE><P>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 anyother:</P><P><PRE>SayHello(Self);</PRE><P>After you have properly imported the function or procedure, you call it like anyother function or procedure. This step presumes, of course, that the procedure hasbeen exported from the DLL as described earlier.</P><BLOCKQUOTE>	<P><HR><strong>CAUTION:</strong> When you declare functions and procedures contained in a DLL,	be sure you get spelling <I>and</I> 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. <HR></P>	<P>	<BR><HR><B>USING THE </B>external<B> KEYWORD</B> </P>	<P>The external keyword has three flavors. Using external you can import a procedure	or function in one of three ways:<BR>	</P>	<UL>	<LI>By actual name<BR>		<LI>By ordinal value<BR>		<LI>By renaming	</UL>	<P></P>	<P>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, 	<PRE>procedure SayHello(AForm : TForm); external `testdll.dll';</PRE></BLOCKQUOTE><PRE></PRE><BLOCKQUOTE>	<P>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: 	<PRE>procedure SomeOrdinalProcedure;  external `testdll.dll' index 99;</PRE></BLOCKQUOTE><PRE></PRE><BLOCKQUOTE>	<P>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.<BR>	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:	<PRE>procedure CoolProcedure;   external `testdll.dll' name `DoSomethingReallyCool';</PRE></BLOCKQUOTE><PRE></PRE><BLOCKQUOTE>	<P>Here I am importing a procedure called DoSomethingReallyCool and renaming it to	CoolProcedure.<BR>	</P>	<P>Of these three methods, the first, importing by name, is the most commonly used.	<HR></BLOCKQUOTE><P>The trick in writing and using DLLs, then, is in getting the imports and exportsright. Otherwise, there's nothing to it. Unless you need the flexibility that dynamicloading provides, you should almost always opt for static loading.</P><P><H3><A NAME="Heading15"></A>Calling Functions and Procedures Using Dynamic Loading</H3><P>Calling functions and procedures in DLLs that are dynamically loaded isn't a lotof fun. It requires that you declare a pointer to the function or procedure in theDLL, and pointers to functions can be confusing. To illustrate, let's say you havea procedure in a DLL called SayHello (the SayHello procedure gets a workout in thischapter). It would look like this in the DLL's source code:</P><P><PRE>procedure SayHello(AForm : TForm);begin  MessageBox(AForm.Handle, `Hello From a DLL!',    `DLL Message Box', MB_OK or MB_ICONEXCLAMATION);end;</PRE><P>To call this procedure from your program, you first have to declare a type thatdescribes the procedure:</P><P><PRE>type  TSayHello = procedure(AForm : TForm);</PRE><P>Now that you've done that, you must load the DLL, use GetProcAddress to get apointer to the procedure, call the procedure, and, finally, unload the DLL. Here'show the whole operation looks:</P><P><PRE>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;</PRE><P>As I said, loading a DLL dynamically is a bit more work. Still, when you needto load a DLL at runtime, that's the way you have to do it. Note that this code ispared down a bit for clarity. You will almost always add some error-checking codeto 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:</P><P><PRE>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 &lt;&gt; nil then    SayHello(Self)  else    MessageDlg(`Unable to locate procedure.', mtError, [mbOK], 0);  FreeLibrary(DLLInstance);end;</PRE><P>As you can see, you probably won't use dynamic loading of DLLs unless you absolutelyhave to.</P><P><H2><A NAME="Heading16"></A>Creating a DLL Project with the Object Repository</H2><P>Creating a DLL in Delphi is accomplished through the Object Repository. (The ObjectRepository is covered on Day 8, &quot;Creating Applications in Delphi.&quot;). Tocreate a DLL project, follow these steps:</P><DL>	<DT></DT>	<DD><B>1. </B>Choose File|New to display the Object Repository.	<P>	<DT></DT>	<DD><B>2. </B>Double-click the DLL icon.	<P></DL><P>Were you expecting it to be more complicated than that? Delphi creates the DLLproject and displays the Code Editor. The file in the edit window looks like this:</P><P><PRE>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;beginend.</PRE><P>Now you can begin adding code to the DLL. Be sure to add any exported procedureor function names to the exports section. Write any standalone functions or proceduresthat you need in your DLL. When you are finished adding code, you can choose Compileor Build from the Project menu to build the DLL.</P><BLOCKQUOTE>	<P><HR><B>COMMENTING THE DLL UNIT</B> </P>	<P>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: <BR>	</P>	<UL>	<LI>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.<BR>		<LI>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.	</UL>	<P></P>	<P>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 	<PRE>procedure MyProcedure(var S : string);begin  { Procedure code here. }end;</PRE></BLOCKQUOTE><PRE></PRE><BLOCKQUOTE>	<P>use this: 	<PRE>procedure MyFunction(S : PChar);begin</PRE>	<PRE>{ Procedure code here. }<B></B></PRE>	<P>	<PRE>end;</PRE></BLOCKQUOTE><PRE></PRE><BLOCKQUOTE>	<P><BR>	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. <HR></P>	<P><HR><strong>NOTE:</strong> 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. <HR></BLOCKQUOTE><P>The next three listings contain code that illustrates the concepts discussed thusfar. 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 applicationand implements a DLLProc. Finally, Listing 19.5 contains a program that calls thetwo DLLs. The program has a form with four buttons that call various procedures inthe two DLLs. The book's code (available at http://www.mcp.com/info) contains thesample programs for the projects in these three listings.</P><P><H4>LISTING 19.3. TestDll.dpr.</H4><PRE>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);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -