http://www.delphisources.ru/pages/faq/master-delphi-7/content/LiB0104.html

Beside this introductory example, you can do a few extra things with dynamic libraries in Delphi. You can use some new compiler directives to affect the name of the library, you can call a DLL at run time, and you can place entire Delphi forms inside a dynamic library. These are the topics of the following sections.

Changing Project and Library Names

For a library, as for a standard application, you end up with a library name matching a Delphi project filename. Following a technique similar to that introduced in Kylix for compatibility with standard Linux naming conventions for shared object libraries (the Linux equivalent of Windows DLLs), Delphi 6 introduced special compiler directives you can use in libraries to determine their executable filename. Some of these directives make more sense in the Linux world than on Windows, but they've all been added anyway:

  • $LIBPREFIX is used to add something in front of the library name. Paralleling the Linux technique of adding lib in front of library names, this directive is used by Kylix to add bpl at the beginning of package names. It is necessary because Linux uses a single extension (.SO) for libraries, whereas in Windows you can have different library extensions, something Borland uses for packages (.BPL).

  • $LIBSUFFIX is used to add text after the library name and before the extension. This text can be used to specify versioning information or other variations on the library name and can be quite useful on Windows.

  • $LIBVERSION is used to add a version number after the extension—something very common in Linux, but that you should generally avoid on Windows.

These directives can be set in the IDE from the Application page of the Project Options dialog box, as you can see in Figure 10.3. As an example, consider the following directives, which generate a library calledMarcoNameTest60.dll:

library NameTest;
{$LIBPREFIX 'Marco'}
{$LIBSUFFIX '60'}

Figure 10.3: The Application page of the Project Options dialog box now has a Library Name section.
 
Delphi 6 packages introduced the extensive use of the $LIBSUFFIX directive. For this reason, the VCL package now generates the VCL.DCP file and the VCL70.BPL file. The advantage of this approach is that you won't need to change the requires portions of your packages for every new version of Delphi. Of course, this is helpful when you move projects from Delphi 6 to Delphi 7, because past versions of Delphi didn't provide this feature. When you reopen Delphi 5 packages you still have to upgrade their source code, an operation the Delphi IDE does automatically for you

Calling a DLL Function at Run Time

Up to now, you've referenced in your code the functions exported by the libraries, so the DLLs were loaded along with the program. I mentioned earlier that you can also delay the loading of a DLL until the moment it is needed, so you can use the rest of the program in case the DLL is not available.

Dynamic loading of a DLL in Windows is accomplished by calling the LoadLibrary API function, which searches for the DLL in the program folder, in the folders on the path, and in some system folders. If the DLL is not found, Windows will show an error message, something you can skip by calling Delphi'sSafeLoadLibrary function. This function has the same effect as the API it encapsulates, but it suppresses the standard Windows error message and should be the preferred way to load libraries dynamically in Delphi.

If the library is found and loaded (something you know by checking the return value of LoadLibrary orSafeLoadLibrary), a program can call the GetProcAddress API function, which searches the DLL's exports table, looking for the name of the function passed as a parameter. If GetProcAddress finds a match, it returns a pointer to the requested procedure. Now you can cast this function pointer to the proper data type and call it.

Whichever loading functions you've used, don't forget to call FreeLibrary at the end, so that the DLL can be properly released from memory. In fact, the system uses a reference-counting technique for libraries, releasing them when each loading request has been followed by a freeing request.

The example I've built to show dynamic DLL loading is named DynaCall. It uses the FirstDLL library built earlier in this chapter (to make the program work, you have to copy the DLL from its source folder into the folder as the DynaCall example). Instead of declaring the Double and Triple functions and using them directly, this example obtains the same effect with somewhat more complex code. The advantage, however, is that the program will run even without the DLL. Also, if new compatible functions are added to the DLL, you won't have to revise the program's source code and recompile it to access those new functions. Here is the core code of the program:

type
TIntFunction = function (I: Integer): Integer; stdcall; const
DllName = 'Firstdll.dll'; procedure TForm1.Button1Click(Sender: TObject);
var
HInst: THandle;
FPointer: TFarProc;
MyFunct: TIntFunction;
begin
HInst := SafeLoadLibrary (DllName);
if HInst > then
try
FPointer := GetProcAddress (HInst,
PChar (Edit1.Text));
if FPointer <> nil then
begin
MyFunct := TIntFunction (FPointer);
SpinEdit1.Value := MyFunct (SpinEdit1.Value);
end
else
ShowMessage (Edit1.Text + ' DLL function not found');
finally
FreeLibrary (HInst);
end
else
ShowMes

As the library uses the Borland memory manager, the program dynamically loading it must do the same. So you need to add the ShareMem unit in the project of the DynaCall example. Oddly enough, this was not so with past versions of Delphi, in case the library didn't effectively use strings. Be warned that if you omit this inclusion, you'll get a harsh system error, which can even stall the debugger on the FreeLIrbary call.

How do you call a procedure in Delphi, once you have a pointer to it? One solution is to convert the pointer to a procedural type and then call the procedure using the procedural-type variable, as in the previous listing. Notice that the procedural type you define must be compatible with the definition of the procedure in the DLL. This is the Achilles' heel of this method—there is no actual check of the parameter types.

What is the advantage of this approach? In theory, you can use it to access any function of any DLL at any time. In practice, it is useful when you have different DLLs with compatible functions or a single DLL with several compatible functions, as in this case. You can call the Double and Triple methods by entering their names in the edit box. Now, if someone gives you a DLL with a new function receiving an integer as a parameter and returning an integer, you can call it by entering its name in the edit box. You don't even need to recompile the application.

With this code, the compiler and the linker ignore the existence of the DLL. When the program is loaded, the DLL is not loaded immediately. You might make the program even more flexible and let the user enter the name of the DLL to use. In some cases, this is a great advantage. A program may switch DLLs at run time, something the direct approach does not allow. Note that this approach to loading DLL functions is common in macro languages and is used by many visual programming environments.

Only a system based on a compiler and a linker, such as Delphi, can use the direct approach, which is generally more reliable and also a little faster. In my opinion, the indirect loading approach of the DynaCall example is useful only in special cases, but it can be extremely powerful. On the other hand, I see a lot of value in using dynamic loading for packages including forms, as you'll see toward the end of this chapter.

Placing Delphi Forms in a Library

Besides writing a library with functions and procedures, you can place a complete form built with Delphi into a dynamic library. This can be a dialog box or any other kind of form, and it can be used not only by other Delphi programs, but also by other development environments or macro languages with the ability to use dynamic link libraries. Once you've created a new library project, all you need to do is add one or more forms to the project and then write exported functions that will create and use those forms.

For example, a function activating a modal dialog box to select a color could be written like this:

function GetColor (Col: LongInt): LongInt; cdecl;
var
FormScroll: TFormScroll;
begin
// default value
Result := Col;
try
FormScroll := TFormScroll.Create (Application);
try
// initialize the data
FormScroll.SelectedColor := Col;
// show the form
if FormScroll.ShowModal = mrOK then
Result := FormScroll.SelectedColor;
finally
FormScroll.Free;
end;
except
on E: Exception do
MessageDlg ('Error in library: ' + E.Message, mtError, [mbOK], );
end;
end;

What makes this different from the code you generally write in a program is the use of exception handling:

  • A try/except block protects the whole function.
    Any exception generated by the function will be trapped, and an appropriate message will be displayed.
    You handle every possible exception because the calling application might be written in any language—
    in particular, one that doesn't know how to handle exceptions.
    Even when the caller is a Delphi program, it is sometimes helpful to use the same protective approach.

  • A try/finally block protects the operations on the form,
    ensuring that the form object will be properly destroyed even when an exception is raised.

By checking the return value of the ShowModal method, the program determines the result of the function.

I've set the default value before entering the try block to ensure that it will always be executed

(and also to avoid the compiler warning indicating that the result of the function might be undefined).

You can find this code snippet in the FormDLL and UseCol projects, available in the FormDLL folder.

(There's also a WORDCALL.TXT file showing how to call the routine from a Word macro.).

The example also shows that you can add a modeless form to the DLL, but doing so causes far too much trouble.

The modeless form and the main form are not synchronized,

because the DLL has its own global Application object in its own copy of the VCL.

This situation can be partially fixed by copying the Handle of the application's Application object

to the Handle of the library's Application object.

Not all of the problems are solved with the code that you can find in the example.

A better solution might be to compile the program and the library to use Delphi packages,

so that the VCL code and data won't be duplicated.

But this approach still causes a few troubles:

it's generally advised that you don't use Delphi DLLs and packages together.

So what is the best suggestion I can give you?

For making the forms of a library available to other Delphi programs, use packages instead of plain DLLs!

Advanced Features of Delphi DLLs的更多相关文章

  1. Language and Compiler Features Since Delphi 7

    from: http://edn.embarcadero.com/cn/article/34324 Language and Compiler Features Since Delphi 7   In ...

  2. [Angular] Angular Advanced Features - ng-template , ng-container, ngTemplateOutlet

    Previously we have tab-panel template defined like this: <ul class="tab-panel-buttons" ...

  3. [Python] Advanced features

    Slicing 12345 L[:10:2] # [0, 2, 4, 6, 8]L[::5] # 所有数,每5个取一个# [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, ...

  4. Massive Collection Of Design Patterns, Frameworks, Components, And Language Features For Delphi

    Developer beNative over on GitHub has a project called Concepts which is a massive collection of Del ...

  5. [转]Advanced Oracle SQL Developer Features

    本文转自:http://www.oracle.com/technetwork/cn/server-storage/linux/sqldev-adv-otn-092384.html Advanced O ...

  6. 在delphi中嵌入脚本语言--(译)RemObjects Pascal Script使用说明(1)(译)

    翻譯這篇文章源於我的一個通用工資計算平台的想法,在工資的計算中,不可避免的需要使用到自定義公式,然而對於自定義公式的實現,我自己想了一些,也在網上搜索了很多,解決辦法大致有以下幾種: 1. 自己寫代碼 ...

  7. Delphi XE5教程3:实例程序

    内容源自Delphi XE5 UPDATE 2官方帮助<Delphi Reference>,本人水平有限,欢迎各位高人修正相关错误! 也欢迎各位加入到Delphi学习资料汉化中来,有兴趣者 ...

  8. delphi中DLL编程详解

    10.1 Windows的动态链接库原理 动态链接库(DLLs)是从C语言函数库和Pascal库单元的概念发展而来的.所有的C语言标准库函数都存放在某一函数库中,同时用户也可以用LIB程序创建自己的函 ...

  9. File Input Features

    文件输入功能 1.该插件将将一个简单的 HTML 文件输入转换为高级文件选取器控件.将有助于对不支持 JQuery 或 Javascript 的浏览器的正常 HTML 文件输入进行回退. 2.文件输入 ...

随机推荐

  1. socket 开发 - 那些年用过的基础 API

    ---------------------------------------------------------------------------------------------------- ...

  2. SilverLight 浏览器出现滚动条

    照网上说的很多解决方案要不得,最后想了下,直接在body上面加 style="overflow:hidden"解决问题,真觉得微软管理混乱,很多它自己的东西都不支持了.

  3. WordPress插件:自定义登录注册插件:DX Login Register

    众所周知,wordpress自带的注册系统比较简单,需要接收邮件密码才能完成.不过对于国内的站长来说,会碰到不少麻烦.首先个人站长一般都使用虚拟主机,有不少还是使用国外的,你的服务器不一定会提供邮件发 ...

  4. 关于Android不同系统版本的市场占比情况详解

    一,google官方统计的不同Android版本市场的占比强开 google统计的数据情况 这个是google官方对于不同版本的市场占比情况.这个是针对全世界所有的Android手机占比情况. 二,友 ...

  5. SGU 217. Two Cylinders

    题意:给空间内两根圆柱,求轴线垂直相交时公共部分的体积. 暴力积分即可. ID: Date'n'Time: Name: Task: .Ext: Status: Time: Memory: 158937 ...

  6. SGU 206. Roads

    206. Roads time limit per test: 0.5 sec. memory limit per test: 65536 KB input: standard output: sta ...

  7. iis应用池内存溢出卡死优化

    1.修改回收阀值memoryLimit 在ASP.NET Web服务器上,ASP.NET所能够用到的内存,通常不会等同于所有的内存数量.在machine.config(C:/WINDOWS/Micro ...

  8. LR-事务

    一.对事务的理解 在LR中什么是事务,事务是记录从客户端到服务器端,服务器端返回到客户端应答的时间,可以反映出一个操作所用的时间.那么事务的时间主要是由响应时间.事务自身时间.浪费时间(wasted ...

  9. 15、Spark Streaming源码解读之No Receivers彻底思考

    在前几期文章里讲了带Receiver的Spark Streaming 应用的相关源码解读,但是现在开发Spark Streaming的应用越来越多的采用No Receivers(Direct Appr ...

  10. CF815C Karen and Supermarket [树形DP]

    题目传送门 Karen and Supermarket On the way home, Karen decided to stop by the supermarket to buy some gr ...