在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
一 Dll的制作一般分为以下几步:
ptr^:=LongInt(mainForm);//用主调程序的mainForm替换DLL的MainForm。MainForm是特殊的WINDOW,它专门管理Application中的Forms资源. //为什么不直接Application.MainForm := mainForm,因为Application.MainForm是只读属性 Form1:=TForm1.Create(mainForm);//用参数建立
library Project2; uses SysUtils, Classes, Dialogs, Forms, Unit2 in 'Unit2.pas' {Form2}; {$R *.RES} var ccc: Pchar; procedure OpenForm(mainForm:TForm);stdcall; var Form1: TForm1; ptr:PLongInt; begin ptr:=@(Application.MainForm); ptr^:=LongInt(mainForm); Form1:=TForm1.Create(mainForm); end; procedure InputCCC(Text: Pchar);stdcall; begin ccc := Text; end; procedure ShowCCC;stdcall; begin ShowMessage(String(ccc)); end; exports OpenForm; InputCCC, ShowCCC; begin end. 调用方源代码: unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; Edit1: TEdit; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.DFM} procedure OpenForm(mainForm:TForm);stdcall;External'project2.dll'; procedure ShowCCC;stdcall;External'project2.dll'; procedure InputCCC(Text: Pchar);stdcall;External'project2.dll'; procedure TForm1.Button1Click(Sender: TObject); var Text: Pchar; begin Text := Pchar(Edit1.Text); // OpenForm(Application.MainForm);//为了调MDICHILD InputCCC(Text);//为了实验DLL中的全局变量是否在各个应用程序间共享 end; procedure TForm1.Button2Click(Sender: TObject); begin ShowCCC;//这里表明WINDOWS 32位应用程序DLL中的全局变量也是在应用程序地址空间中,16位应用程序或许不同,没有做实验。 end; How to create DLL s using DelphiIntroduction As the name implies , dll is a collection of functions and procedures in a place which can be used in other applications. Basic idea of creating DLLs is to share functions and procedures across languages. DLL files will have an extension of .dll. Using Delphi it's very easy. Select the menu File -> New -> Other... from Delphi IDE. This will open a dialog box. From there select the "DLL Wizard" icon . A new project will be opened with a new unit. This unit file will be little bit different from the normal unit files. In the normal units , the first line will contain a keyword unit and the respective unitname. But the unit created in dll , the first keyword will be library instead of unit as given below. library DLLProject; uses SysUtils, Classes; {$R *.RES} begin end. Now you can add as many functions and procedures under the uses clause. The functions and procedures that are required to be called from other applications should be exported using the clause Exports. The name used along with the library clause will become the name of the dll. In this case our dll will be DLLProject.dll Function SubFunc():Integer; begin ... end; Procedure SubProc(); begin ... end; Function MainFunc():Integer; begin ... SubFunc; SubProc; ... end; Exports MainFunc; begin end; Here we have 2 functions and one procedure . The MainFunc is the main function which calls other 2 functions and here we are exporting MainFunc only. That means MainFunc is the only function which can be called from outside. If you need to export any other function or procedure , you have to include it under the Exports clause. The dll function/procedure has to be declared in the global section [before implementation section] of the unit file where the it has to be called. The syntax should be var procedure MainFunc ():Integer; external 'DLLProject.dll'; Implementation. end; This kind of loading a dll is called static loading. Because compiler try to load the dll when it loads the application itself. So if the dll is missing in the search path , you'll get an erroro message while starting your application itself. This kind of loading require extra commands to load dll , call your dll function and release the dll from memory. The dll will be loaded in the memory whenever you call the LoadLibrary function. It won't be loaded when your application starts. So if the dll is missing in your search path, you won't get any error message during the start up of your application. If there is any problem in loading, the handle will return 0. Once dll is loaded properly in to the memory, we have to gather the address of the function\procedure to be called. This can be done using the function GetProcAddress which returns the address of the dll function. To receive the return value , you have to have a variable of type function or procedure [TDllAddr = function : Integer; ]. type TDllAddr = function : Integer; var DllAddr : TDllAddr; begin DllHandle := LoadLibrary("DLLProject.dll"); if DllHandle <> 0 then begin @DllAddr := GetProcAddress(DllHandle , 'MainProc'); DllAddr(); // Calling dll function end; end; Finally, the memory allocated for the dll has to be explicitly released using the FreeLibrary function. The dll function parameters should be windows types. If you have any String type parameter in dll function/procedure , then you have include ShareMem unit in the dll. Not only that, Sharemem unit should be the first unit in the uses clause.
Note: Libraries are significantly more limited than packages in what they can export. Libraries cannot export constants, types, and normal variables. That is, class types defined in a library will not be seen in a program using that library. To export items other than simple procedures and functions, packages are the recommended alternative. Libraries should only be considered when interoperability with other programming is a requirement. The following topics describe elements of writing dynamically loadable libraries, including
Using Export Clause in LibrariesThe main source for a dynamically loadable library is identical to that of a program, except that it begins with the reserved word library (instead of program). Only routines that a library explicitly exports are available for importing by other libraries or programs. The following example shows a library with two exported functions, Min and Max: library MinMax; function Min(X, Y: Integer): Integer; stdcall; begin if X < Y then Min := X else Min := Y; end; function Max(X, Y: Integer): Integer; stdcall; begin if X > Y then Max := X else Max := Y; end; exports Min, Max; begin end.
If you want your library to be available to applications written in other languages, it's safest to specify stdcall in the declarations of exported functions. Other languages may not support Delphi's default register calling convention. Libraries can be built from multiple units. In this case, the library source file is frequently reduced to a uses clause, an exports clause, and the initialization code. For example: library Editors;
You can put exports clauses in the interface or implementation section of a unit. Any library that includes such a unit in its uses clause automatically exports the routines listed the unit's exports clauses without the need for an exports clause of its own.
exports entry1, ..., entryn;
where each entry consists of the name of a procedure, function, or variable (which must be declared prior to the exports clause), followed by a parameter list (only if exporting a routine that is overloaded), and an optional name specifier. You can qualify the procedure or function name with the name of a unit. (Entries can also include the directive resident, which is maintained for backward compatibility and is ignored by the compiler.) On the Win32 platform, an index specifier consists of the directive index followed by a numeric constant between 1 and 2,147,483,647. (For more efficient programs, use low index values.) If an entry has no index specifier, the routine is automatically assigned a number in the export table. Note: Use of index specifiers, which are supported for backward compatibility only, A name specifier consists of the directive name followed by a string constant. If an entry has no name specifier, the routine is exported under its original declared name, exports DoSomethingABC name 'DoSomething'; When you export an overloaded function or procedure from a dynamically loadable library, For example: exports Divide(X, Y: Integer) name 'Divide_Ints', Divide(X, Y: Real) name 'Divide_Reals';
On Win32, do not include index specifiers in entries for overloaded routines. An exports clause can appear anywhere and any number of times in the declaration part of a program or library, or in the interface or implementation section of a unit. Programs seldom contain an exports clause. Library Initialization CodeThe statements in a library's block constitute the library's initialization code. These statements are executed once every time the library is loaded. They typically perform tasks like registering window classes and initializing variables. Library initialization code can also install an entry point procedure using the DllProc variable. The DllProc variable is similar to an exit procedure, which is described in Exit procedures; the entry point procedure executes when the library is loaded or unloaded. Library initialization code can signal an error by setting the ExitCode variable to a nonzero value. ExitCode is declared in the System unit and defaults to zero, indicating successful initialization. If a library's initialization code sets ExitCode to another value, the library is unloaded and the calling application is notified of the failure. Similarly, if an unhandled exception occurs during execution of the initialization code, the calling application is notified of a failure to load the library. Here is an example of a library with initialization code and an entry point procedure: library Test; var SaveDllProc: Pointer;
DllProc is called when the library is first loaded into memory, when a thread starts or stops, or when the library is unloaded. The initialization parts of all units used by a library are executed before the library's initialization code, and the finalization parts of those units are executed after the library's entry point procedure.
Global Variables in a Library
Global variables declared in a shared library cannot be imported by a Delphi application.
A library can be used by several applications at once, but each application has a copy of the library in its own process space with its own set of global variables. For multiple libraries - or multiple instances of a library - to share memory, they must use memory-mapped files. Refer to the your system documentation for further information.
Libraries and System Variables
Several variables declared in the System unit are of special interest to those programming libraries. Use IsLibrary to determine whether code is executing in an application or in a library; IsLibrary is always False in an application and True in a library. During a library's lifetime, HInstance contains its instance handle. CmdLine is always nil in a library.
The DLLProc variable allows a library to monitor calls that the operating system makes to the library entry point. This feature is normally used only by libraries that support multithreading. DLLProc is used in multithreading applications. You should use finalization sections, rather than exit procedures, for all exit behavior.
To monitor operating-system calls, create a callback procedure that takes a single integer parameter, for example: procedure DLLHandler(Reason: Integer);
and assign the address of the procedure to the DLLProc variable. When the procedure is called, it passes to it one of the following values.
Exceptions and Runtime Errors in LibrariesWhen an exception is raised but not handled in a dynamically loadable library, it propagates out of the library to the caller. If the calling application or library is itself written in Delphi, the exception can be handled through a normal try...except statement. On Win32, if the calling application or library is written in another language, the exception can be handled as an operating-system exception with the exception code $0EEDFADE. The first entry in the ExceptionInformation array of the operating-system exception record contains the exception address, and the second entry contains a reference to the Delphi exception object. Generally, you should not let exceptions escape from your library. Delphi exceptions map to the OS exception model. If a library does not use the SysUtils unit, exception support is disabled. In this case, when a runtime error occurs in the library, the calling application terminates. Because the library has no way of knowing whether it was called from a Delphi program, it cannot invoke the application's exit procedures; the application is simply aborted and removed from memory. Shared-Memory ManagerOn Win32, if a DLL exports routines that pass long strings or dynamic arrays as parameters or function results (whether directly or nested in records or objects), then the DLL and its client applications (or DLLs) must all use the ShareMem unit. The same is true if one application or DLL allocates memory with New or GetMem which is deallocated by a call to Dispose or FreeMem in another module. ShareMem should always be the first unit listed in any program or library uses clause where it occurs. ShareMem is the interface unit for the BORLANDMM.DLL memory manager, which allows modules to share dynamically allocated memory. BORLANDMM.DLL must be deployed with applications and DLLs that use ShareMem. When an application or DLL uses ShareMem, its memory manager is replaced by the memory manager in BORLANDMM.DLL.
Static vs. Dynamic Dynamic Link Library Loading - A ComparisonA DLL or a dynamic link library acts as a shared library of function, that can be called by applications and by other DLLs. Using Delphi, you can create and use our own DLLs, you can call functions in DLLs developed with other programming languages / by other developers. If you are new to working with DLLs, make sure you read Introduction to DLLs. Static or Dynamic Loading?When you want to call a function exported by a DLL, one question comes up: should you use static or dynamic DLL loading? . Before you can call routines defined in DLL, you must import them. Functions exported from a DLL can be imported in two ways: by declaring an external procedure or function (static), or by direct calls to DLL specific API functions (dynamic). Let's create a simple DLL. Here's the code to the "circle.dll" exporting one function "CircleArea" which calculates the area of a circle using the given radius: library circle;
Note: if you need help with creating a DLL using Delphi, read the How to create (and use) a DLL in Delphi . Once you have the circle.dll you can use the exported "CircleArea" function from your application. Static LoadingThe simplest way to import a procedure or function is to declare it using the external directive: function CircleArea(const radius : double) : double; external 'circle.dll';
If you include this declaration in the interface part of a unit, circle.dll is loaded once, when the program starts. Throughout execution of the program, the function CircleArea is available to all units that use the unit where the above declaration is. Dynamic LoadingYou can access routines in a library through direct calls to Win32 APIs, including LoadLibrary , FreeLibrary , andGetProcAddress . These functions are declared in Windows.pas. Here's how to call the CircleArea function using dynamic loading: type TCircleAreaFunc = function (const radius: double) : double; stdcall; var dllHandle : cardinal; circleAreaFunc : TCircleAreaFunc; begin dllHandle := LoadLibrary('circle.dll') ; if dllHandle <> 0 then begin @circleAreaFunc := GetProcAddress(dllHandle, 'CircleArea') ; if Assigned (circleAreaFunc) then circleAreaFunc(15) //call the function else ShowMessage('"CircleArea" function not found') ; FreeLibrary(dllHandle) ; end else begin ShowMessage('circle.dll not found / not loaded') ; end; end;
When you import using dynamic loading, the DLL is not loaded until the call to LoadLibrary. The library is unloaded by the call to FreeLibrary. With static loading the DLL will be loaded and its initialization sections will execute before the calling application's initialization sections are executed. With dynamic loading, this is reversed. Static or DynamicLet's now compare static and dynamic DLL loading to see what are advantages and disadvantages of both. Static loading PROS:
Static loading CONS:
Dynamic loading PROS:
Dynamic loading CONS:
I hope differences are clear and that you will know what type of DLL loading to use for your next project ;) If you think some PROS or CONS are missing, feel free to let me know - I'll add it to the list.
Libraries and Packages (Delphi)A dynamically loadable library is a dynamic-link library (DLL) on Windows, or a DYLIB on Mac. It is a collection of routines that can be called by applications and by other DLLs or shared objects. Like units, dynamically loadable libraries contain sharable code or resources. But this type of library is a separately compiled executable that is linked, at run time, to the programs that use it. Delphi programs can call DLLs and assemblies written in other languages, and applications written in other languages can call DLLs or assemblies written in Delphi. Calling Dynamically Loadable LibrariesYou can call operating system routines directly, but they are not linked to your application until run time. This means that the library need not be present when you compile your program. Also, there is no compile-time validation of attempts to import a routine. Before you can call routines defined in DLL or assembly, you must import them. This can be done in two ways: by declaring an external procedure or function, or by direct calls to the operating system. Whichever method you use, the routines are not linked to your application until run time. Delphi does not support importing variables from DLLs or assemblies. Static LoadingThe simplest way to import a procedure or function is to declare it using the external directive. For example: procedure DoSomething; external 'MYLIB.DLL';
If you include this declaration in a program, MYLIB.DLL is loaded once, when the program starts. Throughout the execution of the program, the identifier DoSomething always refers to the same entry point in the same shared library. Declarations of imported routines can be placed directly in the program or unit where they are called. To simplify maintenance, however, you can collect external declarations into a separate "import unit" that also contains any constants and types required for interfacing with the library. Other modules that use the import unit can call any routines declared in it. Dynamic Loading (Windows-only)You can access routines in a library through direct calls to Windows APIs, including LoadLibrary, FreeLibrary, and GetProcAddress. These functions are declared in Windows.pas. In this case, use procedural-type variables to reference the imported routines. For example: uses Windows, ...;
type
TTimeRec = record
Second: Integer;
Minute: Integer;
Hour: Integer;
end;
TGetTime = procedure(var Time: TTimeRec);
THandle = Integer;
var
Time: TTimeRec;
Handle: THandle;
GetTime: TGetTime;
.
.
.
begin
Handle := LoadLibrary('libraryname');
if Handle <> 0 then
begin
@GetTime := GetProcAddress(Handle, 'GetTime');
if @GetTime <> nil then
begin
GetTime(Time);
with Time do
Writeln('The time is ', Hour, ':', Minute, ':', Second);
end;
FreeLibrary(Handle);
end;
end;
When you import routines this way, the library is not loaded until the code containing the call to LoadLibrary executes. The library is later unloaded by the call to FreeLibrary. This allows you to conserve memory and to run your program even when some of the libraries it uses are not present. Delayed LoadingThe delayed directive can be used to decorate an external routine to delay the loading of the library containing the routine. The actual loading happens when the routine is called for the first time. The following example demonstrates the use of the delayed directive: function GetSomething: Integer; external 'somelibrary.dll' delayed;
In the example above, the GetSomething routine is imported from the somelibrary.dll library. The delayed directive ensures thatsomelibrary.dll is not statically linked to the application, but rather dynamically. The delayed directive is useful in the case where the imported routines do not exist on the target operating system on which the application is run. Statically imported routines require that the operating system find and load the library when the application is started. If the routine is not found in the loaded library, or the library does not exist, the Operating System halts the execution of the application. Using the delayed directive enables you to check, at run time, whether the Operating System supports the required APIs; only then you can call the imported routines. Another potential use for the delayed directive is related to the memory footprint of the application: decorating the less probably to be used routines, as delayed may decrease the memory footprint of the application, because the libraries are loaded only when required. The abusive use of delayed can damage the speed performance of the program (as perceived by the end user). Note: Trying to call a delayed routine that cannot be resolved results in a run-time error (or an exception, if the SysUtils unit is loaded). In order to fine-tune the delay-loading process used by the Delphi Run-time Library, you can register hook procedures to oversee and change its behavior. To accomplish this, use SetDliNotifyHook2 and SetDliFailureHook2, declared in the SysInit unit. Also see the code example at Delayed Loading (Delphi).This example demonstrates the fine tuning of the delay loading mechanism. Using the provided functionality, you can hook-up various steps in the delay loading process. This example defines three cases, one of which is correct, and two incorrect. Note: At the XE2 release, the delayed loading mechanism for Delphi was refactored and moved From the System unit into the SysInit unit. For example, System.dliNotification became SysInit.dliNotification, and System.DliNotifyHook became SysInit.DliNotifyHook2. This code example has not yet been revised to use the new delayed loading. program TestDelayLoad; {$APPTYPE CONSOLE} uses Winapi.Windows, System.SysUtils; function GetDesktopWindow: HWND; stdcall; external user32 name 'GetDesktopWindow' delayed; function GetFooBar: Integer; stdcall; external kernel32 name 'GetFooBar' delayed; function GetFooBarBar: Integer; stdcall; external 'kernel33' name 'GetFooBarBar' delayed; var LOldNotifyHook, LOldFailureHook: TDelayedLoadHook; { for storing the old hook pointers } { Utility function to retrieve the name of the imported routine or its ordinal } function ImportName(const AProc: TDelayLoadProc): String; inline; begin if AProc.fImportByName then Result := AProc.szProcName else Result := '#' + IntToStr(AProc.dwOrdinal); end; function MyDelayedLoadHook(dliNotify: dliNotification; pdli: PDelayLoadInfo): Pointer; stdcall; begin { Write a message for each dli notification } case dliNotify of dliNoteStartProcessing: WriteLn('Started the delayed load session for "', pdli.szDll, '" DLL'); dliNotePreLoadLibrary: WriteLn('Starting to load "', pdli.szDll, '" DLL'); dliNotePreGetProcAddress: WriteLn('Want to get address of "', ImportName(pdli.dlp), '" in "', pdli.szDll, '" DLL'); dliNoteEndProcessing: WriteLn('Ended the delaay load session for "', pdli.szDll, '" DLL'); dliFailLoadLibrary: WriteLn('Failed to load "', pdli.szDll, '" DLL'); dliFailGetProcAddress: WriteLn('Failed to get proc address for "', ImportName(pdli.dlp), '" in " 全部评论
专题导读
热门推荐
热门话题
阅读排行榜
|
请发表评论