日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Delphi DLL制作和加载 Static, Dynamic, Delayed 以及 Shared-Memory Manager

發布時間:2024/4/14 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Delphi DLL制作和加载 Static, Dynamic, Delayed 以及 Shared-Memory Manager 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一 Dll的制作一般分為以下幾步:
1 在一個DLL工程里寫一個過程或函數
2 寫一個Exports關鍵字,在其下寫過程的名稱。不用寫參數和調用后綴。
二 參數傳遞
1 參數類型最好與window C++的參數類型一致。不要用DELPHI的數據類型。
2 最好有返回值[即使是一個過程],來報出調用成功或失敗,或狀態。成功或失敗的返回值最好為1[成功]或0[失敗].一句話,與windows c++兼容。
3 用stdcall聲明后綴。
4 最好大小寫敏感。
5 無須用far調用后綴,那只是為了與windows 16位程序兼容。

三 DLL的初始化和退出清理[如果需要初始化和退出清理]
1 DLLProc[SysUtils單元的一個Pointer]是DLL的入口。在此你可用你的函數替換了它的入口。但你的函數必須符合以下要求[其實就是一個回調函數]。如下:
procedure DllEnterPoint(dwReason: DWORD);far;stdcall;
dwReason參數有四種類型:
DLL_PROCESS_ATTACH:進程進入時
DLL_PROCESS_DETACH進程退出時
DLL_THREAD_ATTACH 線程進入時
DLL_THREAD_DETACH 線程退出時
在初始化部分寫:
DLLProc := @DLLEnterPoint;
DllEnterPoint(DLL_PROCESS_ATTACH);
2 如Form上有TdcomConnection組件,就Uses Activex,在初始化時寫一句CoInitialize (nil);
3 在退出時一定保證DcomConnection.Connected := False,并且數據集已關閉。否則報地址錯。

四 全局變量的使用
在widnows 32位程序中,兩個應用程序的地址空間是相互沒有聯系的。雖然DLL在內存中是一份,但變量是在各進程的地址空間中,因此你不能借助dll的全局變量來達到兩個應用程序間的數據傳遞,除非你用內存映像文件。

五 調用靜態載入
1 客戶端函數聲名:
1)大小寫敏感。
2)與DLL中的聲明一樣。
如: showform(form:Tform);Far;external'yproject_dll.dll';
3)調用時傳過去的參數類型最好也與windows c++一樣。
4)調用時DLL必須在windows搜索路徑中,順序是:當前目錄;Path路徑;windows;widows\system;windows\ssystem32;

六 調用動態載入
1 建立一種過程類型[如果你對過程類型的變量只是一個指針的本質清楚的話,你就知道是怎么回事了]。如:
type
mypointer=procedure(form:Tform);Far;external;
var
Hinst:Thandle;
showform:mypointer;
begin
Hinst:=loadlibrary('yproject_dll');//Load一個Dll,按文件名找。
showform:=getprocaddress(Hinst,'showform');//按函數名找,大小寫敏感。如果你知道自動化對象的本質就清楚了。
showform(application.mainform);//找到函數入口指針就調用。
Freelibrary(Hinst);
end;

七 在DLL建立一個TForM
1 把你的Form Uses到Dll中,你的Form用到的關聯的單元也要Uses進來[這是最麻煩的一點,因為你的Form或許Uses了許多特殊的單元或函數]
2 傳遞一個Application參數,用它建立Form.

八 在DLL中建立一個TMDIChildForM
1 Dll中的MDIForm.FormStyle不用為fmMDIChild.
2 在CreateForm后寫以下兩句:


function ShowForm(mainForm:TForm):integer;stdcall
var
Form1: TForm1;
ptr:PLongInt;
begin
ptr:=@(Application.MainForm);//先把dll的MainForm句柄保存起來,也無須釋放,只不過是替換一下

ptr^:=LongInt(mainForm);//用主調程序的mainForm替換DLL的MainForm。MainForm是特殊的WINDOW,它專門管理Application中的Forms資源.

//為什么不直接Application.MainForm := mainForm,因為Application.MainForm是只讀屬性

Form1:=TForm1.Create(mainForm);//用參數建立
end;


備注:參數是主調程序的Application.MainForm

九 示例:
DLL源代碼:

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;interfaceuses 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 Delphi

Introduction

DLL means Dynamic Link Libraries.

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.

How to create a dll using Delphi?

It's not so hard to create a dll as you heard.

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

Suppose you have 3 functions as shown below.

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.

Dll loading

Now we can see how these dll [exported] functions can be called from other applications.

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.

Now we'll see another kind of dll loading named Dynamic loading.

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.

The dll should be loaded using the command?LoadLibrary?which returns the handle to the dll once it's loaded to the memory.

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;?].

Have a look at the example shown below.

typeTDllAddr = function : Integer;var DllAddr : TDllAddr;beginDllHandle := 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.

Note?:

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.

?

Writing Dynamically Loaded Libraries

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

  • The exports clause.
  • Library initialization code.
  • Global variables.
  • Libraries and system variables.

Using Export Clause in Libraries

The 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; beginif X < Y then Min := X else Min := Y; end; function Max(X, Y: Integer): Integer; stdcall; beginif X > Y then Max := X else Max := Y; end; exportsMin,Max;beginend.

?

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;
uses
EdInit, EdInOut, EdFormat, EdPrint;
exportsInitEditors,DoneEditors name Done,InsertText name Insert,DeleteSelection name Delete,FormatSelection,PrintSelection name Print,...SetErrorHandler;
beginInitLibrary;end.

?

?

?

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.

?


A routine is exported when it is listed in an?exports?clause, which has the form:

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,
is discouraged and may cause problems for other development tools.

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,
with the same spelling and case. Use a?name?clause when you want to export a routine under a different name.
For example:

exports DoSomethingABC name 'DoSomething';

When you export an overloaded function or procedure from a dynamically loadable library,
you must specify its parameter list in the?exports?clause.

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 Code

The 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; varSaveDllProc: Pointer;
procedure LibExit(Reason: Integer); beginif Reason = DLL_PROCESS_DETACH thenbegin.. // library exit code.end;SaveDllProc(Reason); // call saved entry point procedure end;
begin.. // library initialization code.SaveDllProc := DllProc; // save exit procedure chainDllProc := @LibExit; // install LibExit exit procedure end.

?

?

?

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.

DLL_PROCESS_DETACH

Indicates that the library is detaching from the address space of the calling process as a result of a clean exit or a call to?FreeLibrary.

DLL_PROCESS_ATTACH

Indicates that the library is attaching to the address space of the calling process as the result of a call to?LoadLibrary.

DLL_THREAD_ATTACH

Indicates that the current process is creating a new thread.

DLL_THREAD_DETACH

Indicates that a thread is exiting cleanly.


In the body of the procedure, you can specify actions to take depending on which parameter is passed to the procedure.

Exceptions and Runtime Errors in Libraries

When 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 Manager

On 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 Comparison

A?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;

uses SysUtils, Classes, Math;

{$R *.res}

function CircleArea(const radius : double) : double; stdcall;
begin
result := radius * radius * PI;
end;

exports CircleArea;

begin
end.

?

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 Loading

The 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 Loading

You 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 Dynamic

Let's now compare static and dynamic DLL loading to see what are advantages and disadvantages of both.

Static loading PROS:

  • More easy for a beginner developer, no "ugly" API calls.
  • DLLs loaded once, when the program starts.

Static loading CONS:

  • The application will NOT start if any DLLs are missing (cannot be found).
    When you run the program you will see an ugly message:?
    "This application has failed to start because 'missing.dll' was not found. Re-installingn the application may fix this problem".?
    By design the DLL search order with static linking includes:
    the directory from which the application loaded,
    the system directory,
    the Windows directory,
    directories listed in the PATH environment variable.?

    Note also that the search order might be different for various Windows versions.?
    The safest is to always expect to have all the DLLs in the directory where the calling application is.

  • More memory used, as all DLLs are loaded even if you will not use some of the functions.

Dynamic loading PROS:

  • You can run your program even when some of the libraries it uses are not present.
  • Smaller memory consumption - DLLs used when needed.
  • You can specify the full path to the DLL.
  • Use for functionality that is rarely needed by the application.
  • Could be used for modular applications. The application only exposes (loads) modules (dlls) "approved" for the user.
  • The ability to load and unload library dynamically,
    is the foundation of a plug-in system that allow a developer to add extra functionality to programs.
  • Backwards compatibility with older Windows versions,
    in which system dlls may not support the same functions or in the same way.
    Detecting the Windows version first, then dynamic linking based on what your app is running on,
    allows you to support more versions of Windows and provide work arounds for older OSs,
    or at the very least gracefully disabling features you can't support.

Dynamic loading CONS:

  • Requires more code, not trivial for a beginner developer.

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 Libraries

You 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 Loading

The 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, ...;typeTTimeRec = recordSecond: 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 Loading

The?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}usesWinapi.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;varLOldNotifyHook, 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; beginif AProc.fImportByName thenResult := AProc.szProcNameelseResult := '#' + IntToStr(AProc.dwOrdinal); end;function MyDelayedLoadHook(dliNotify: dliNotification; pdli: PDelayLoadInfo): Pointer; stdcall; begin{ Write a message for each dli notification }case dliNotify ofdliNoteStartProcessing: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 "', pdli.szDll, '" DLL');end;{ Call the old hooks if they are not nil }{ This is recommended to do in case the old hook do further processing }if dliNotify in [dliFailLoadLibrary, dliFailGetProcAddress] thenbeginif Assigned(LOldNotifyHook) thenLOldFailureHook(dliNotify, pdli);end elsebeginif Assigned(LOldNotifyHook) thenLOldNotifyHook(dliNotify, pdli);end;Result := nil; end;begin{ Install new delayed loading hooks }LOldNotifyHook := SetDliNotifyHook2(MyDelayedLoadHook);LOldFailureHook := SetDliFailureHook2(MyDelayedLoadHook);{ Calling an existing delayed external routine }GetDesktopWindow;try{ Calling an unexisting delayed external routine in an existing library }GetFooBar;exceptend;try{ Calling an unexisting delayed external routine in an unexisting library }GetFooBarBar;exceptend;{ Reset the hooks }SetDliNotifyHook2(LOldNotifyHook);SetDliFailureHook2(LOldFailureHook);Readln; end.

?

SysInit.dliNotification

The following table lists the cases in which the registered hook is called by the delay load helper.

Value of?dliNotifyparameterDescription

dliNoteStartProcessing

Sent to a delayed-load notification hook when a delayed-load session is starting. Called before the library containing the delay loaded external procedure is processed. Used to bypass or notify helper only.

dliNotePreLoadLibrary

Sent before LoadLibrary is called, allowing a new HMODULE to be returned. Called before the library containing the delay loaded external procedure is loaded. Can override with the new HMODULE return value.

dliNotePreGetProcAddress

Sent before GetProcAddress, allowing for a new procedure address to be returned if desired. Called before the address of the delay loaded procedure is found. Can override with new HMODULE return value.

dliNoteEndProcessing

Sent to a delayed-load notification hook when all delayed-load processing completes. Cannot be bypassed except for raise or RaiseException.

dliFailLoadLibrary

Sent to a delayed-load failure hook when LoadLibrary fails; allows you to specify a different valid HMODULE handle.

dliFailGetProcAddress

Sent to a delay-load failure hook when GetProcAddress fails; allows you to replace the procedure address with the address of a valid procedure.

DelayLoadProc = recordfImportByName: LongBool;case Byte of0: (szProcName: _PAnsiChr);1: (dwOrdinal: LongWord);end;

?

Record that holds information for a procedure that is delay loaded.

The meaning of each field in the?DelayLoadProc?record is given in the following table.

?

FieldMeaning

szProcName

The name of the procedure.

dwOrdinal

The ordinal of the procedure.

advantages and disadvantages of delay load (LoadLibrary)

Some knowledgeable people posted the following wisdom about delay load. Archived here to share.?

The advantages are that dlls get loaded only when they are used,

and you can “statically” bind to an import that may not exist at runtime,

and as long as you are careful not to call it, the program will still work downlevel.?

The disadvantage is:

1)??????Some DLLs don’t work DelayLoaded (as mentioned in the limitations of LoadLibrary).?
In particular, any dll that uses __declspec(thread), or any dll that you want to import data from.

2)??????You can’t call any API that might be delay loaded in your DllMain (since you can’t call LoadLibrary during DllMain).?
Probably not a big deal since you’re generally not supposed to call any APIs in DllMain.

3)??????The DLLs in your process are now initialized in random order.?
There may be some orders that cause bizarre bugs.

4)??????In particular, if you “statically” bind to a dll, then your dll is uninitialized before that other dll.?
If you delayload, then you will be uninitialized after your delayloaded dlls.

5)??????Since DLLs may be loaded “late”, their preferred base address may be used already by stack or heap data,
resulting in a rebasing performance penalty that wouldn’t happen if the Dll was loaded at boot.

6)??????If the LoadLibrary fails for some reason (at the delay loading site), then your program just crashes.?
You can catch this exception, but it is difficult to recover from and definitely the API callsite that was expecting
to make a call into this delayed dll will have no direct way of detecting failures.?

The last one is the biggest burden. ?

It can mean random crashes when your app is under stress (because you’re out of memory to load that DLL).?

Unless your feature is specifically designed to deal with that DLL not loading

(and you’d have to put this code around every place where you call a delayed API), you run this random crash risk.?

Delay Loading definitely has its uses and its benefits, but the limitations means it is not correct to blindly delayload everything.

Only 1 is a real disadvantage.2,3,4,5 in most cases causes no problems.6 is a real advantage, because instead of receiving error in the loader you can get the error in the runtime and fix it. On the contrary, 6 is no advantage at all. A hard program crash is never a good situation to be in if it can be avoided. If you want the same behaviour, then throw your own exception or abort by some other easy to trace & debug mechanism when LoadLibrary fails. At least with that approach you can choose to fail gracefully.1 is a problem, but if you're doing that sort of thing you probably shouldn't be delay loading anyway. 3/4 is also a big problem when you have an API which then has an API built on top of it, the latter being delay loaded because it may or may not be there. In 6 you have the same code for both.LL+GetProcAddress:HMODULE h = LoadLibrary(...);if(!h) throw Exception;W/ DL:CallFunction() // If it fails you get exceptionRegular call:CallFuUnction() // If it fails in the loader your program cannot run at all !!3. You must not rely on dll load order. It is not good.4. Yes, this can cause problem in some times, I hope it won't cause problems for me :)

Delphi 2010 Delayed Dynamic Link Libraries

Delphi 2010 Delayed Dynamic Link Libraries
Traditionally, Dynamic Link Libraries (DLLs) can be loaded in two different ways:

implicit or explicit.

In this article, I’ll create a simple DLL, and show how this DLL can be loaded implicitly as well as explicitly.

I’ll then move to a new feature introduced with Delphi 2010:

the delayed loading of DLLs, which offers the best of both worlds and more, as we’ll see.

Example DLL
The example DLL should be as simple as possible,

yet introducing another nice feature that perhaps not all of the readers may know:

the concept of function overloading in DLLs, and how to export overloaded functions correctly.

The example I’m going to use her is a DLL with two “Add” methods,

one for adding integers, and one for adding doubles.

Since the function names are the same, we must decorate them with the overload keyword.

In order to export them, we must make sure each function is exported with a unique name,

so in case of the Add for doubles, I’ll export it by name “AddFloat”.?

The source code for the library eBob42 is as follows:

library eBob42;function Add(X,Y: Integer): Integer; overload; stdcall;beginResult := X + Yend;function Add(X,Y: Double): Double; overload; stdcall;beginResult := X + Yend;exportsAdd(X,Y: Integer) name 'Add',Add (X,Y: Double) name 'AddFloat';end.

When compiling this source code, we’ll end up with a eBob42.DLL that we can import and use in different ways:

implicit, explicit or with the new delayed technique, offered by Delphi 2010.

Implicit

The implicit import of functions from DLLs is the easiest way to use a DLL.

All you have to do is repeat the function definition, including the calling convention (plus overload when needed),

and add the “external” keyword plus the name of the DLL where the function can be found.

For an overloaded function, we should also include the name keyword again,

followed by the correct name under which the function was exported.

All in all, not very complex, and for the eBob42.DLL, the Implicit import unit could be implemented as follows:

unit eBob42Implicit;interfaceconstDLL = 'eBob42.DLL';function Add(X,Y: Integer): Integer; overload; stdcall external DLL;function Add(X,Y: Double): Double; overload; stdcall external DLL name 'AddFloat';implementationend.

?

The biggest disadvantage of using the implicit import of DLLs technique is the fact that you’ll get an error message when trying to load an application that requires a DLL, and that DLL cannot be found. In that situation, the application will be unable to start, so the error message is a fatal one, and without the DLL, the application itself is useless.

Explicit

The main alternative for implicit import is the explicit loading and import of functions from a DLL.

This takes more code, but it allows us to give a nice error message

when the DLL cannot be loaded and/or when a function from the DLL cannot be found,

without keeping the application itself from running.

So even without the DLL being present, the application can be used (albeit without the functionality from the DLL).?

As an example of an explicit import unit, where we explicitly need to load the DLL using

LoadLibrary and get a handle to the functions using GetProcAddress, is as follows:

unit eBob42Explicit;interfaceconstDLL = 'eBob42.DLL';varAdd: function(X,Y: Integer): Integer; stdcall = nil;AddFloat: function(X,Y: Double): Double; stdcall = nil;implementationusesWindows;vareBob42Handle: Cardinal;initializationeBob42Handle := LoadLibrary(DLL);if eBob42Handle <= 32 thenMessageBox(0,Error: could not load ' + DLL, 'Oops!', MB_OK)elsebeginAdd := GetProcAddress(eBob42Handle, 'Add');AddFloat := GetProcAddress(eBob42Handle, 'AddFloat')endfinalizationFreeLibrary(eBob42Handle)end.

?

Obviously, the unit eBob42Explicit is a lot bigger and complex than the simple unit eBob42Implicit.

And each additional function from the DLL will make this difference bigger,

because eBob42Implicit only needs to list the function (with the external keyword),

while eBob42Explicit needs to declare a function pointer and assign a value to that pointer using GetProcAddress.?

The biggest advantage of explicit importing is the fact that the application

will be able to load and start even if the DLL that we’re trying to use cannot be found (or loaded).

We’ll see an error message when the LoadLibrary or GetProcAddress fails, but the application itself will still run.?

The disadvantage is that the code for the explicit import unit is a lot more complex,

and when we call the imported functions through the function pointers,

we should check if the function pointers are actually assigned

(otherwise we might still get a run-time error or access violation).?

Although neither of these approaches appears perfect,

Delphi 2010 now supports a third method which combines the strength and best of both worlds, and then some.

The technique is known as delayed loading.

Delay Load
Delphi 2010 introduces a new keyword:?

delayed.

In fact, it’s so new that the online help, the wiki and even the syntax highlighter don’t know about it, yet.

The only source of information that I could find was the blog post of Allen Bauer of the Delphi R&D Team itself.?

The basic idea of the solution is the fact that the DLL will not be loaded right away (which is the case for implicit linking),

but only when needed.

So potentially “delayed”, and hence the name “delay loading”.?

The syntax of using the delayed approach is actually quite similar to the implicit import unit,

with the exception that we now add the delayed keyword to the function definition

(and since neither code insight and syntax highlighting seem to know about this new keyword,

you’ll have to trust on the compiler to tell you when it’s right:

after the name of the DLL, without semi-colon between the name of the DLL and the delayed keyword itself).?

unit eBob42Delayed;interfaceconstDLL = 'eBob42.DLL';function Add(X,Y: Integer): Integer; overload; stdcall external DLL delayed;function Add(X,Y: Double): Double; overload; stdcall external DLL name 'AddFloat' delayed;implementationend.

?

When compiling unit eBob42Delayed, you get two warnings about the delayed keyword,

telling you that the symbol DELAYED is specific to a platform.

Yeah right, that doesn’t matter to much to me to be honest.

What matters is that we now have the ease of implicit importing with the robustness of explicit importing.

The best of both worlds:

unit eBob42Delayed is as short as unit eBob42Implicit,

and yet the application will start normally even if the DLL cannot be found.?

There is one thing left to test:

imagine what would happen if we use the eBob42Delayed unit, and start the application

without the DLL being present (or found), and then call the function Add?

The good news is that the application can be started just fine, and will remain up-and-running.

The bad news is that the user will see a not very user-friendly error message, namely:

I can imagine that for the average user this error message will not be fully clear,

so the user may not know what the actual problem is.

Of course, we can catch this EExternalException in a try-except block,

but the problem is that we do not know if the error is caused by a missing DLL,

or perhaps by the function which was not found in the DLL

(for example if an incorrect version of the DLL was loaded with the correct name, but without the required function exported).

?

DelayedLoadHook

Based on a blog post from – again – Allen Bauer,

we could read that there is actually a way to handle the specific errors that can occur

when (delay) loading a DLL or obtaining a function pointer using GetProcAddress.

The delay loading itself is done in an old (but well-tested) delayhpl.c file from the C++RTL,

which offers the option to “hook” to the notification messages from this process

by defining a DelayedLoadHook function that we can install using the SetDliNotifyHook function.

The DelayedLoadHook function should be defined as follows (according to line 2392 of system.pas):

DelayedLoadHook = function (dliNotify: dliNotification; pdli: PDelayLoadInfo): Pointer; stdcall;

?The records dliNotification and PDelayLoadInfo are also interesting,

and contain the information we need to determine the nature of the notification (and possibly error).

?

Again a little snippet from system.pas:

dliNotification = (dliNoteStartProcessing, { used to bypass or note helper only }dliNotePreLoadLibrary, { called just before LoadLibrary, can }{ override w/ new HMODULE return val }dliNotePreGetProcAddress, { called just before GetProcAddress, can }{ override w/ new Proc address return }{ value }dliFailLoadLibrary, { failed to load library, fix it by }{ returning a valid HMODULE }dliFailGetProcAddress, { failed to get proc address, fix it by }{ returning a valid Proc address }dliNoteEndProcessing { called after all processing is done, }{ no bypass possible at this point }{ except by raise, or RaiseException });

?

Based on the value of dliFailLoadLibrary,?we can raise an exception to explain to the user in detail that a DLL could not be loaded.

And based on the value dliFailGetPRocAddress, we can tell the user that the DLL could be loaded,

but the specific function could not be found in this DLL.

In order to determine the name of the DLL and when needed the name of the function,

we should examine the DelayLoadInfo record, which is defined as follows:

DelayLoadInfo = recordcb: LongWord; { size of structure }pidd: PImgDelayDescr; { raw form of data (everything is there) }ppfn: Pointer; { points to address of function to load }szDll: PAnsiChar; { name of dll }dlp: TDelayLoadProc; { name or ordinal of procedure }hmodCur: HMODULE; { the hInstance of the library we have loaded }pfnCur: Pointer; { the actual function that will be called }dwLastError: LongWord; { error received (if an error notification) }end;

?

For the name of the DLL itself, we can use the field szDll,

and for the name of the function (or the value of the export index from the function in the DLL)
we have to look a little bit further (or deeper) to the dlp structure of type TDelayLoadProc.

The type DelayLoadProc in turn is defined in a variant record as follows:

DelayLoadProc = recordfImportByName: LongBool;case Byte of0: (szProcName: PAnsiChar);1: (dwOrdinal: LongWord);end;

If the variant record field fImportByName is true, then we should look at the szProcName field,

otherwise the value of the dwOrdinal field must be used

(in case the function was imported by index number instead of by name).?

With this information at our disposal, we can write a procedure DelayedHandlerHook (see listing 5)

with arguments dliNotification and PDelayLoadInfo,

and inside this procedure we can raise an exception with detailed information if the specific error situations have occurred.

DelayedHandler
For my own convenience, I’ve placed the procedure DelayedHandlerHook inside its own unit DelayedHandler,

making sure that the DelayedHandlerHook is installed by calling the SetDliFailureHook function

in the initialization section of the unit, and uninstalling it again in the finalization section again.

As a result, you only need to add this unit to the uses clause of any project that uses the “delayed” external references,

and needs to be able to raise specific exceptions for the situations

where the DLL could not be found (or loaded) or when a specific function (or index) could not be found in the DLL.?

The two specific exceptions are of type ELoadLibrary and EGetProcAddress and also defined in Listing 5:

unit DelayedHandler;interfaceusesSysUtils;typeELoadLibrary = class(Exception);EGetProcAddress = class(Exception);implementationfunction DelayedHandlerHook(dliNotify: dliNotification; pdli: PDelayLoadInfo): Pointer; stdcall;beginif dliNotify = dliFailLoadLibrary thenraise ELoadLibrary.Create('Could not load ' + pdli.szDll)elseif dliNotify = dliFailGetProcAddress thenif pdli.dlp.fImportByName thenraise EGetProcAddress.Create('Could not load ' + pdli.dlp.szProcName + ' from ' + pdli.szDll)elseraise EGetProcAddress.Create('Could not load index ' + IntToStr(pdli.dlp.dwOrdinal) + ' from ' + pdli.szDll)end;initializationSetDliFailureHook(DelayedHandlerHook);finalizationSetDliFailureHook(nil);end.

?

This time, when the DLL could not be found or loaded,

we’ll get a far more descriptive exception which can be displayed in a ShowMessage box as follows:

My final tests show that each time we call the (delay loaded) Add function,

the application tries to load the DLL (if not already loaded).

This means that if the DLL could not be found the first time this function was called,

then during the second attempt, it will again try to load the DLL.

And if we’ve found and for example installed the DLL in the meantime, then this means the second call will succeed!?

This is another, perhaps unexpected, advantage of the delayed loading approach compared to the explicit importing approach.

Unless we extend the explicit importing approach to also try to load the DLL

when we make a function call, the delay loading will be more robust and able to connect to a DLL even after the application has started.?

As final test, let’s see what happens if the DLL is present but we want to import and call a function with an incorrect name.

As a test, we should modify the eBob42Delayed unit for the Add Float function as follows:

function Add(X,Y: Double): Double; overload; stdcall external DLL name 'AddDouble' delayed; // was ‘AddFloat’

?

This will lead to a nice exception to inform us that the function AddDouble could not be found in the eBob42.DLL.

Note that the exception is displayed using a call to ShowMessage,?by our client code, inside a try-except block.

But you can use this technique also in web server application

(where you don’t want to use a ShowMessage) by handling the exception inside a try-except block in another way.

Summary

Where the implict import of DLLs is easy but not always very convenient or robust?(when the DLL is not present, the application won’t start),

and the explicit import of DLLs is robust but more complex (where you should also always check before calling a function pointer that this function pointer is actually assigned),

there the delayed loading technique of DLLs offers the best of both worlds.

The easy of declaration (with the additional delayed keyword) with the easy and robustness of use,

plus the ability to “connect” to the DLL at a later time, even after the application was started and the DLL wasn’t found the first time.

In combination with the unit DelayedHandler, for specific exception raising in case the DLL could not be found or loaded,

or a specific function was not found, we now have the ultimate way of importing and using DLLs from now on

(although this functionality is only supported by Delphi 2010).

References

Allen Bauer,?Procrastinators Unite… Eventually!?
Allen Bauer,?Exceptional Procrastination

?

Procrastinators Unite… Eventually!

We’re all taught at an early age to “Never put off until tomorrow that which can be done today.”

In general, that is wise advice. However there are some cases where you do want to wait

until the last possible moment to do (or not do) something.

In fact, that is one of the overall tenets of? Agile Programming;

delay decisions until the last possible moment

because you always know more about a problem tomorrow

than you do today and can make a better,?more informed decision.

But, I digress. I’m not here to talk about philosophies of life,

or to introduce another “agile methodology” or even about a new weight loss plan based on bacon, lard and cheese puffs.

How many of you have written this same bit of boilerplate code or something similar over and over again??

if OSVersion >= 5.0 then beginModule = LoadLibrary('kernel32.dll');if Module <> 0 thenbeginProc := GetProcAddress(Module, 'APICall');if Assigned(Proc) thenProc(Param1, Param2);end; end else{ gracefully fallback }What if you could just do this?if OSVersion >= 5.0 thenAPICall(Param1, Param2); else{ gracefully fallback }

The astute among you would immediately see that with previous Delphi releases,

the second form, while certainly preferable, at some point the call to “APICall”

would eventually have to effectively go through some bit of code similar to the first bit of code.

Normally, you would declare an external API reference like this:?

procedure APICall(Param1, Param2: Integer); stdcall; external 'kernel32.dll' name 'APICall';

?

?That will cause the linker to emit a external reference into the executable binary that is resolved?at load time.?

Therein lies the problem.

That is the situation that the first bit of code above was designed to avoid.

If APICall didn’t existing in ‘kernel32.dll’ at load time, the whole application would fail to load.

Game over.

Thanks for playing.

Now, what if you could write code similar the second block of code above?and?still declare your external API calls in a manner similar to above?

Starting with Delphi 2010, you can do exactly the scenario I describe.

To make the second code block work even if “APICall” isn’t available on the version of the OS on which your application is currently running,

we’ve introduced a new directive to be used only in the above context,?delayed;

procedure APICall(Param1, Param2: Integer); stdcall; external 'kernel32.dll' name 'APICall' delayed;

Simply put, by adding the delayed directive, that instructs the linker to generate the external API reference differently in the executable binary.

Now Delphi’s RTL will take care of all that ugly “late-binding” boilerplate code for you.

Rather than generating the import in the normal “Imports” section of the executable’s PE file,

it is generated into the “Delayed Imports” section following the published PE spec.

This also requires that the RTL now has a generic function that does the proper lookups

and binds the API whenever it is called the first time.

Subsequent calls are just as fast as a normal import.

This is different than similar functionality available in ILink32 from C++Builder

wherein you can only specify an entire dll in which?all?references are delay loaded.

Also, in C++ you cannot specify kernel32.dll or even ntdll.dll to be delay loaded,

since the very startup of any application or dll requires them to already be loaded.

In Delphi you can, since it is on an API-by-API basis.

The intent of this feature was to make managing all the new APIs

from Windows Vista and now Windows 7 much easier without having to continuously

and manually write all the delay loaded boilerplate code.

Throughout VCL, we can simply make runtime decisions on

which APIs to call without always going through those manually coded “thunks.”

They are now simply handled by the compiler/linker

and the source code barely reveals the fact that something is late-bound.

In a future release, we are considering adding

both the API-by-API capability to C++Builder?and?

a way to specify an entire dll to be late-bound in Delphi.

Exceptional Procrastination

I kept putting this post off… ok, ok… that was a really bad pun…

Seems there was a little bit of concern about my?

last post regarding the new ‘delayed’ directive.?

Christian Wimmer had a few critiques?

regarding the implementation and how the exception

that was raised was an EExternalException with an obscure exception code.

There is a simple explanation for this.

The delay load helper functions were taken directly from the C++Builder RTL.

By that I mean, the delayhlp.c file gets built to a .obj file

by the C++ compiler and then directly linked into the Delphi System unit.

There were several key reasons for this.

?

?

The first of which was the code was already written, has been in many versions of C++Builder RTL

(including back in the old Borland C++ days) and has been extensively tested.

?

Another reason is that in order for Delphi and C++Builder to “play-nice,”

when a Delphi unit is linked in C++Builder that contains a reference to a delay load import,

ILink32 takes over the duties of generating the proper delay load functionality into the PE file.

?

So the C++RTL version of delayhlp.c is what is used.

?

In order to things to remain consistent, this is the route taken.

Had there been two delay-load helper functions in the case of a C++Builder application,

then any existing C++ code that used the delay load hooks would only work on other C++ code and vice-versa.

?

Fear not, all is not lost.

To satisfy our good friend, Christian’s request, here is a unit that you can use to generate nice,

unique Delphi specific exceptions.

This is accomplished by leveraging the ability for the delayhlp.c code to be “hooked.”

This code also demonstrates another new Delphi language feature,

class constructors and destructors.

I will describe them in better detail in a subsequent post.

If you never reference any of the declared exception types in this unit,

the hook is not installed and the normal EExternalException is raised.

Presumably you would use this unit for the purpose of actually catching the exceptions

and doing something interesting with them.

Another thing to note is that you should also be able to add this unit to a C++Builder application

and it work for any delay-load functions done in the existing way for C++

and any Delphi units that reference Delphi imports with the delayed directive.

unit DelayExcept;interfaceuses SysUtils;typeEDliException = class(Exception)privateclass constructor Create;class destructor Destroy;end;EDliLoadLibraryExeception = class(EDliException)privateFDllName: string;publicconstructor Create(const ADllName: string); overload;property DllName: string read FDllName;end;EDliGetProcAddressException = class(EDliException)privateFDllName: string;FExportName: string;publicconstructor Create(const ADllName, AExportName: string); overload;constructor Create(const ADllName: string; AOrdinal: LongWord); overload;property DllName: string read FDllName;property ExportName: string read FExportName;end;implementation{ EDliLoadLibraryExeception }constructor EDliLoadLibraryExeception.Create(const ADllName: string); begininherited Create(Format('Unable to load ''%s''', [ADllName]));FDllName := ADllName; end;{ EDliGetProcAddressException }constructor EDliGetProcAddressException.Create(const ADllName, AExportName: string); begininherited Create(Format('Unable to locate export name ''%s'' in ''%s''', [AExportName, ADllName]));FDllName := ADllName;FExportName := AExportName; end;constructor EDliGetProcAddressException.Create(const ADllName: string; AOrdinal: LongWord); begininherited Create(Format('Unable to locate export ordinal ''%d'' in ''%s''', [AOrdinal, ADllName]));FDllName := ADllName;FExportName := IntToStr(AOrdinal); end;function DelayLoadFailureHook(dliNotify: dliNotification; pdli: PDelayLoadInfo): Pointer; stdcall; beginResult := nil;if dliNotify = dliFailLoadLibrary thenraise EDliLoadLibraryExeception.Create(pdli.szDll);if dliNotify = dliFailGetProcAddress thenif pdli.dlp.fImportByName thenraise EDliGetProcAddressException.Create(pdli.szDll, pdli.dlp.szProcName)elseraise EDliGetProcAddressException.Create(pdli.szDll, pdli.dlp.dwOrdinal); end;{ EDliException }class constructor EDliException.Create; beginSetDliFailureHook(DelayLoadFailureHook); end;class destructor EDliException.Destroy; beginSetDliFailureHook(nil); end;end.

?

總結

以上是生活随笔為你收集整理的Delphi DLL制作和加载 Static, Dynamic, Delayed 以及 Shared-Memory Manager的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

黄毛片在线观看 | av天天澡天天爽天天av | 97视频在线观看成人 | av电影 一区二区 | av不卡免费在线观看 | 亚洲最大av在线播放 | a极黄色片 | 日韩精品免费一区二区 | www.伊人色.com| av福利在线| 久久人人爽人人人人片 | 日韩三级在线 | 在线观看亚洲精品 | 狠狠狠色丁香综合久久天下网 | 麻豆视频免费在线 | 中文字幕在线久一本久 | 国产精品欧美久久久久三级 | www四虎影院 | 最新高清无码专区 | 69av在线视频 | 久久国产精品视频观看 | 丁香免费视频 | 91精品成人| 成人精品视频 | 国产一区免费看 | 色就色,综合激情 | 日韩欧美精品在线观看 | 在线观看国产www | 91精品久久久久久久91蜜桃 | 日韩中字在线观看 | 天天弄天天干 | 国产精品一区二区av影院萌芽 | 免费在线观看成人小视频 | av一区在线 | 在线观看视频免费播放 | 成人在线黄色 | 天天色天天爱天天射综合 | 欧美日本高清视频 | 在线色亚洲 | 2023国产精品自产拍在线观看 | 国产男女爽爽爽免费视频 | 亚洲精品女人 | 成人在线观看你懂的 | 日韩,中文字幕 | 亚洲爱爱视频 | 日韩一区精品 | 手机av看片 | 六月丁香在线视频 | 日本精品一区二区 | 久久天堂影院 | 伊人宗合网 | 亚洲色图av | 久久精品第一页 | 久久狠狠婷婷 | 亚洲精品在线观看中文字幕 | 看国产黄色片 | 亚洲国产中文字幕 | 国产黄色在线看 | 在线看黄色的网站 | 黄色电影在线免费观看 | 久久一区91 | 在线99| 久久国内免费视频 | 911亚洲精品第一 | 欧美天天综合网 | 91爱爱中文字幕 | 精品在线免费观看 | 久久婷婷五月综合色丁香 | 中文字幕日韩精品有码视频 | 日韩精品中文字幕在线 | 天天操天天射天天爽 | 精品国产伦一区二区三区观看说明 | 粉嫩高清一区二区三区 | 精品中文字幕在线播放 | www.天天射 | 成人性生交大片免费观看网站 | 久久不射电影网 | 国产小视频在线看 | 亚洲成年人在线播放 | 亚洲jizzjizz日本少妇 | 国产高清免费视频 | 国产精品 视频 | 国产精品久久久久久一区二区 | 日韩午夜电影网 | 四虎永久免费在线观看 | 国产手机视频 | 精品久久国产精品 | 在线观看中文字幕第一页 | 天天射综合 | 国产小视频在线免费观看视频 | 日韩视频中文字幕在线观看 | 四虎国产精品永久在线国在线 | 豆豆色资源网xfplay | 天海翼一区二区三区免费 | 91精品一区二区三区久久久久久 | 黄色片网站大全 | 91豆花在线 | www国产亚洲精品久久麻豆 | 四虎海外影库www4hu | 精品产品国产在线不卡 | 狠狠色狠狠色综合系列 | 国产美女在线免费观看 | 亚洲情婷婷| 成人h动漫在线看 | av在线色 | 亚洲成a人片综合在线 | 亚洲成人av电影在线 | 欧美日韩国产综合网 | 精品国产观看 | 在线之家免费在线观看电影 | 一区二区在线不卡 | 天天操天天干天天干 | 在线 国产 日韩 | 色99中文字幕 | 亚洲精品乱码久久久久久高潮 | av免费片 | 午夜影院在线观看18 | 在线免费观看涩涩 | 久久黄色影院 | 麻豆高清免费国产一区 | 在线精品在线 | 久久久www成人免费毛片麻豆 | 福利视频一区二区 | 六月丁香六月婷婷 | 成人国产一区 | 三级在线播放视频 | www.91国产 | 欧美久久久久久久久久久 | 天天躁天天躁天天躁婷 | 久久国产精品免费 | 婷婷在线色| 射久久久 | 97精品在线 | 99精品国产高清在线观看 | 精品久久九九 | 一区中文字幕 | 国产亚洲精品美女 | 国产午夜三级一区二区三桃花影视 | 一级片免费视频 | 免费激情在线电影 | 久久精品理论 | 91视频久久久 | 国产精品综合在线 | 97国产一区二区 | 天天射天天添 | 99久久久成人国产精品 | 久久精品国产99国产 | 日本婷婷色 | 91人人射 | 成全免费观看视频 | 国产精品白浆视频 | 欧美激情综合色综合啪啪五月 | 五月网婷婷 | 四虎影视成人永久免费观看视频 | 久久伦理 | 国产污视频在线观看 | 欧美一级专区免费大片 | 在线观看你懂的网址 | 国产精品毛片一区二区 | 日韩高清观看 | 五月激情av | 免费日韩在线 | 波多野结衣在线观看一区 | 国产高清综合 | 亚洲国产精品视频 | 国产特级毛片aaaaaa毛片 | 国产一二区免费视频 | 精品91| 久久久亚洲成人 | 三级在线视频播放 | 97视频在线观看播放 | 99精品在线直播 | 日韩欧美精品在线观看视频 | 国产 一区二区三区 在线 | 日韩高清在线一区二区 | 欧美色图另类 | 亚洲综合欧美精品电影 | 婷婷伊人综合 | 天天综合网入口 | 天天操比 | 国产精品3 | 日日摸日日添夜夜爽97 | 9i看片成人免费看片 | 日韩欧美视频在线免费观看 | 日韩午夜精品 | 91免费观看国产 | 国内一级片在线观看 | av在线小说| 国内精品久久久久久久久 | 久久久久久久久久久网 | 欧美精品久久天天躁 | 中文字幕影片免费在线观看 | 天天色天天草天天射 | 精品久久一级片 | 欧美在线99 | 一区二区三区韩国免费中文网站 | 久久国产精品久久w女人spa | 成人在线黄色 | 五月天电影免费在线观看一区 | 婷婷激情影院 | 国产一区二区三区在线免费观看 | 免费高清无人区完整版 | 亚洲综合色视频在线观看 | 日日夜夜操操操操 | 国产精品丝袜久久久久久久不卡 | 久久精品视频国产 | 亚洲视频免费在线看 | 亚洲精品视频网 | 激情五月六月婷婷 | 日韩一级黄色大片 | 亚洲成年人av | 亚洲精品乱码久久久久久蜜桃不爽 | 午夜精品视频一区 | 日本电影黄色 | 亚洲精品福利在线观看 | 草在线视频 | 在线观看国产日韩欧美 | 欧美日韩性 | 91成人精品一区在线播放 | 国产黄a三级 | 国产精品自在线 | 最近中文字幕第一页 | 激情综合五月天 | 久热色超碰 | 中文在线最新版天堂 | 日韩免费网址 | 久久综合色播五月 | av成人动漫 | 欧美巨大荫蒂茸毛毛人妖 | 亚洲天天综合网 | 在线观看欧美成人 | 18性欧美xxxⅹ性满足 | 亚洲综合少妇 | 四虎永久视频 | 夜夜高潮夜夜爽国产伦精品 | 成人黄色在线看 | 91精品国产乱码久久桃 | 欧美日韩国产综合网 | 日韩在线在线 | 日韩专区在线 | 亚洲作爱视频 | 91成人在线观看喷潮 | 亚洲免费专区 | 狠狠狠色丁香婷婷综合激情 | 美女视频永久黄网站免费观看国产 | 日韩av免费在线看 | 国产精品高清在线 | 人人爽人人av | 亚洲一区欧美激情 | 97人人澡人人添人人爽超碰 | 91在线观看视频 | 欧美日韩国产网站 | 毛片基地黄久久久久久天堂 | 久久久久久国产精品久久 | av中文字幕在线看 | 日韩不卡高清 | 久久综合五月婷婷 | 久久精品8 | 十八岁以下禁止观看的1000个网站 | 亚洲三级在线播放 | 麻豆精品国产传媒 | 成av人电影 | 九九精品视频在线看 | 草久在线观看视频 | 亚洲精品字幕在线观看 | 激情综合狠狠 | 欧美一区中文字幕 | 午夜精品久久久 | 天天爱天天草 | 欧美综合色在线图区 | 一区免费视频 | 97成人精品区在线播放 | 日批网站在线观看 | 欧美精品一区二区免费 | 色播99| 国产99精品在线观看 | 亚洲国产精品99久久久久久久久 | 久久精品com| 99精品视频免费看 | 色九九影院 | 日韩精品一二三 | 在线 国产 亚洲 欧美 | 91在线porny国产在线看 | 精品在线视频一区 | 六月丁香综合网 | 色综合激情网 | 国内精品久久久久影院日本资源 | 激情五月播播久久久精品 | 亚洲专区视频在线观看 | 97精品在线| 中文字幕免费在线 | 麻豆 free xxxx movies hd | 一区二区三区四区五区在线 | 欧美日韩精品在线观看 | 日本精品免费看 | 国产在线视频一区二区三区 | 免费黄色网止 | 国产高清在线免费观看 | 成年人黄色大片在线 | 亚洲综合欧美日韩狠狠色 | 国产在线理论片 | 成人三级网站在线观看 | 久久精品国产成人精品 | 国产视频 久久久 | 九九精品毛片 | 精品免费久久 | 天天色天天操天天爽 | 亚洲精品乱码久久久久久蜜桃91 | 干干日日 | avove黑丝| 日韩电影中文 | 在线观看免费黄视频 | 免费在线观看视频a | 最新中文字幕在线播放 | 亚洲视频第一页 | 999色视频| 精品国产一区二区三区久久影院 | 国产精品色婷婷 | 97精品国产手机 | 91视频在线免费下载 | 亚洲国产片 | 国产成人久久精品亚洲 | 九九九在线观看视频 | 国产一二区精品 | 美女网站色在线观看 | 四虎在线观看视频 | 国产精品色婷婷视频 | 伊人黄| 日韩电影在线视频 | 丁香六月婷婷开心婷婷网 | 亚洲国产精品成人女人久久 | 国产九九九九九 | 综合天堂av久久久久久久 | 91中文字幕| 久久综合五月婷婷 | 天堂入口网站 | 草莓视频在线观看免费观看 | 亚洲美女视频在线观看 | av在线小说| 99在线热播精品免费 | 91豆花在线 | 能在线看的av | 国产精品综合久久久久久 | www亚洲视频 | www.99热精品| 久久久免费电影 | 日本中文在线 | 久久精品视频在线播放 | 极品嫩模被强到高潮呻吟91 | 国产麻豆精品在线观看 | 国产精品一区二区免费在线观看 | 五月婷婷在线视频观看 | 97在线观看免费观看高清 | 久久九九影院 | 久久久久久久久久久久久国产精品 | 亚洲观看黄色网 | 婷婷婷国产在线视频 | 国产婷婷久久 | 九九久久视频 | 米奇影视7777 | 中文在线 | 天天摸日日摸人人看 | 国产精品一区二区三区观看 | 黄色小网站免费看 | 免费在线激情视频 | 国产无套一区二区三区久久 | 日韩资源在线观看 | 夜夜天天干 | 国内久久视频 | 国产亚洲视频系列 | 国产精品成人久久 | 天天综合网 天天综合色 | 人人搞人人爽 | 国产日韩欧美视频在线观看 | 一区二区三区电影在线播 | 日韩精品中文字幕在线观看 | 在线91观看 | 成人丁香花| 一区二区在线影院 | 91在线观| 亚洲综合在线播放 | 天天天色综合 | 亚洲区视频在线观看 | 日韩网站在线 | 97视频入口免费观看 | 久久人人爽人人爽 | 色诱亚洲精品久久久久久 | 国产小视频在线观看免费 | 六月婷婷色 | 国产精品大片免费观看 | 天天艹天天 | 日日麻批40分钟视频免费观看 | 人人爱在线视频 | 久久久片 | 国产福利91精品一区 | 精品一二三四五区 | 国产正在播放 | 日本黄色大片免费看 | 中文字幕在线观看免费高清电影 | 99欧美精品 | 日日操夜夜操狠狠操 | 久久精品麻豆 | 天天综合网国产 | 色婷婷综合成人av | 国产亚洲精品久久久久动 | www.天天射.com | 国内成人综合 | 精品 一区 在线 | av在线电影网站 | av观看在线观看 | 亚洲三级网站 | 色偷偷888欧美精品久久久 | 精品国模一区二区 | 91av在线不卡 | 91亚洲影院 | 午夜精品成人一区二区三区 | 国产伦精品一区二区三区无广告 | 狠狠躁夜夜躁人人爽视频 | 丁香五月缴情综合网 | 亚洲最大在线视频 | a天堂中文在线 | 四虎影视成人永久免费观看亚洲欧美 | 国产又黄又爽又猛视频日本 | 日韩欧美网站 | 成人免费视频网站 | 日韩欧美视频在线观看免费 | 久草视频中文在线 | 久草精品资源 | 国产色网站| 五月婷婷免费 | 国产中文字幕三区 | 日韩欧美高清在线 | 亚洲成人av在线电影 | 在线视频专区 | 91精品一区国产高清在线gif | 国产一在线精品一区在线观看 | 欧美精品一区二区蜜臀亚洲 | 久久精品久久久久久久 | 国产99久久精品一区二区永久免费 | 精品久久久久久久久久国产 | 日韩欧美一区视频 | 国产爽妇网 | av福利网址导航 | 在线免费观看黄色 | 久久免费美女视频 | 在线观看国产麻豆 | 久久久久久免费 | 国产区精品区 | 中文字幕日韩在线播放 | 人人射人人 | 久久av在线 | 西西44人体做爰大胆视频 | 欧美 国产 视频 | 欧美日韩国产在线观看 | 日本九九视频 | 激情综合婷婷 | 成人欧美一区二区三区在线观看 | 99在线免费观看视频 | 天天爱天天操 | www.狠狠色| 97狠狠干| 欧美一级片播放 | 看国产黄色大片 | 国产精品久久久久久久久久久久午夜 | 伊人色播 | 超黄视频网站 | 亚州性色 | 一区二区在线不卡 | 少妇性aaaaaaaaa视频 | av电影一区二区 | 中文字幕中文字幕 | 久久毛片高清国产 | 免费亚洲片 | 最新色站 | 人人干网站 | 中文字幕一区二区三区在线观看 | 91在线视频在线观看 | 国产亚洲va综合人人澡精品 | 日本精品一区二区三区在线播放视频 | 午夜精品一区二区三区在线 | 91精彩视频在线观看 | 日韩,中文字幕 | 看污网站 | 日韩特级黄色片 | 热re99久久精品国产66热 | 美女很黄免费网站 | 国产精品无av码在线观看 | 亚洲美女免费精品视频在线观看 | 99久久99久久免费精品蜜臀 | 国产色啪 | 在线免费黄网站 | 日韩欧美精品免费 | 狠狠色综合网站久久久久久久 | 99视频在线播放 | 99久久精品日本一区二区免费 | 黄色日视频 | 国产精品不卡一区 | 91麻豆精品国产91久久久久久久久 | 亚洲欧美日韩精品久久奇米一区 | 免费成人av在线看 | 一级成人网 | 婷婷综合激情 | 天天操狠狠操 | 国产精品va在线观看入 | 国产成人精品一区二区三区免费 | 国内精品久久久久影院日本资源 | 97成人在线观看 | 日韩精品不卡在线观看 | 亚洲精品伦理在线 | 国产伦精品一区二区三区免费 | 五月天婷亚洲天综合网鲁鲁鲁 | 色婷婷狠狠五月综合天色拍 | 久久人人看 | 天天操天天插 | 免费色视频| 久青草影院 | 欧美成人h版在线观看 | 黄色av一区二区三区 | 国产欧美在线一区二区三区 | 在线观看韩日电影免费 | 高清av免费一区中文字幕 | 国产又粗又硬又长又爽的视频 | 成片人卡1卡2卡3手机免费看 | 婷婷在线不卡 | 精品久久久久久久久久 | 2023国产精品自产拍在线观看 | 在线观看 亚洲 | 精品国产a | 久草在在线 | 久久久久成 | 久久久高清视频 | 国产成人在线看 | 国产成人精品三级 | 欧美 亚洲 另类 激情 另类 | 久久亚洲精品电影 | 九九热精品视频在线观看 | av在线成人 | 三级黄色大片在线观看 | 亚洲精品小区久久久久久 | 国产麻豆精品一区二区 | 高清不卡一区二区在线 | 久久dvd| 综合婷婷| 日韩亚洲国产中文字幕 | 精品一区二区三区香蕉蜜桃 | 97av超碰| 中文字幕在线播放一区二区 | 黄色91在线 | 黄色三级在线看 | 99这里只有精品视频 | 国产精品观看在线亚洲人成网 | 欧美淫aaa免费观看 日韩激情免费视频 | 99爱在线观看 | 中文字幕久久网 | 欧美另类成人 | 91看片在线观看 | av网站地址 | 一区二区三区精品在线视频 | 国产99在线免费 | 亚洲精品乱码久久久久久按摩 | 婷婷五综合| 91精品视频免费在线观看 | 日韩视频中文字幕在线观看 | 91精品国产乱码在线观看 | 黄色成人91 | 日韩精品三区四区 | 国产在线观看中文字幕 | 久久不卡日韩美女 | 99精品国产99久久久久久福利 | 99热这里精品 | 又色又爽又激情的59视频 | 日韩黄色在线 | 一区二区理论片 | 91在线国产观看 | 九九热视频在线免费观看 | 精品影院一区二区久久久 | 激情婷婷av| 欧美精品一区二区免费 | 国产精品美女久久久久久2018 | 天天爱天天操 | 友田真希x88av | 99热99re6国产在线播放 | 久久久www免费电影网 | 午夜久久网 | 911香蕉视频 | 精品国产成人av | 91专区在线观看 | 涩涩网站在线 | 午夜电影一区 | 91精品小视频 | 久久精品免费播放 | 一区二区三区www | 国产精品电影一区二区 | 亚洲日本va在线观看 | 在线91观看 | 亚洲成人第一区 | 亚洲精品国产精品国产 | 在线免费观看成人 | 91成人短视频在线观看 | av蜜桃在线 | 91亚色在线观看 | 91免费观看视频在线 | 青青网视频 | 五月婷丁香网 | 欧美性脚交 | 一区二区影院 | 三上悠亚一区二区在线观看 | 超碰公开在线观看 | 久久99国产视频 | 777奇米四色| 婷婷六月天丁香 | 久久精品久久精品久久 | 亚洲免费在线 | 91高清视频免费 | 久久久久久久看片 | 国产精品日韩欧美一区二区 | 成人免费在线观看入口 | 亚洲精品美女视频 | 超碰97公开 | 99色婷婷| 国产一级在线播放 | 色狠狠综合天天综合综合 | 亚洲精品视 | 国产精品久久久视频 | 99国产一区二区三精品乱码 | 国产一级不卡视频 | 久久综合狠狠综合久久综合88 | www.午夜| 国产精品久久久久9999吃药 | 久久99精品久久久久久久久久久久 | 欧美视频在线观看免费网址 | 国产一级二级三级视频 | 天天操月月操 | 色狠狠婷婷 | 国产日韩欧美在线免费观看 | 中文字幕一区二区三 | 一级一片免费视频 | 人人干网 | 欧美日韩在线视频免费 | 国产午夜精品福利视频 | 欧美国产日韩一区二区三区 | 2023av| 欧美激情xxxx性bbbb | 中文国产在线观看 | 婷婷六月网 | 久久精品五月 | 欧美韩国在线 | 91天天操| 国产成人精品综合久久久 | 国产成人一区二区三区免费看 | 成人国产精品久久久春色 | 日本不卡视频 | 国产乱码精品一区二区三区介绍 | 日韩免费观看一区二区 | 成人av片免费观看app下载 | a级国产乱理论片在线观看 特级毛片在线观看 | 国产精品综合av一区二区国产馆 | 久久久久免费精品视频 | 免费高清av在线看 | 国产美女黄网站免费 | 久久精品九色 | 免费看的av片 | 免费又黄又爽的视频 | 午夜丁香视频在线观看 | 99久热在线精品视频观看 | 亚洲视屏一区 | 日日干av | 成人久久视频 | 九九免费在线观看 | 成人羞羞视频在线观看免费 | 亚洲视频免费在线 | 精品久久久久久一区二区里番 | av官网在线 | 免费观看www视频 | 偷拍精偷拍精品欧洲亚洲网站 | 欧美精品资源 | 国产一区网 | 91日韩在线专区 | 在线视频 成人 | 久久综合视频网 | 成人欧美一区二区三区在线观看 | 国产精品mv| 亚洲天天综合网 | 亚洲婷婷网 | 射综合网| 国产免费三级在线观看 | 欧美日韩首页 | 欧美另类z0zx | 亚洲美女在线国产 | 黄色aaa级片 | 久久视频在线免费观看 | 91九色视频在线 | 婷婷丁香久久五月婷婷 | 国产字幕在线播放 | 亚洲午夜不卡 | 蜜臀91丨九色丨蝌蚪老版 | 黄色视屏av | 免费在线色视频 | 久久99精品久久久久久秒播蜜臀 | 91麻豆精品国产91久久久久 | 亚洲最快最全在线视频 | 在线成人免费电影 | 久久私人影院 | 婷婷丁香激情五月 | www.久久爱.cn| 久草在线观 | 激情电影在线观看 | 色悠悠久久综合 | 91喷水 | 女人18毛片a级毛片一区二区 | 国产色拍 | 成人h在线播放 | 狠狠成人| 国产午夜精品福利视频 | www.午夜| 夜夜高潮夜夜爽国产伦精品 | 99精品视频在线观看 | www黄在线| 一级黄色大片在线观看 | 99精品视频在线观看视频 | 日批网站在线观看 | 亚洲精品乱码久久久久久蜜桃不爽 | 在线国产精品视频 | 综合伊人av | 黄色三级在线 | 国产99久久精品一区二区永久免费 | www色婷婷com | 国产综合婷婷 | 精品国产一二三四区 | 97在线精品视频 | 亚洲九九精品 | 久久免费视频99 | 国产 成人 久久 | 91精品国产综合久久福利不卡 | 午夜免费在线观看 | 免费影视大全推荐 | 中文字幕av免费在线观看 | aa级黄色大片 | 99人久久精品视频最新地址 | 黄色片网站免费 | 热久久电影 | av一级黄| 国产专区在线播放 | 久久乐九色婷婷综合色狠狠182 | 国产成人精品综合久久久 | 久久国产精彩视频 | 久久97久久97精品免视看 | 免费福利在线播放 | 久久久久久久网 | 成人黄色av免费在线观看 | 成人av电影免费 | 国产色视频一区二区三区qq号 | 久久久受www免费人成 | 亚洲国产影院av久久久久 | 久久国产欧美日韩精品 | 日日干日日色 | 欧美a级免费视频 | 91九色在线观看视频 | 久久视频在线视频 | 国产视频 久久久 | 三级av片 | 日韩精品免费一线在线观看 | 最新国产中文字幕 | 日韩av中文字幕在线免费观看 | 久久新 | 久久人人爽视频 | 亚洲1区 在线 | 欧美日韩国产亚洲乱码字幕 | 色婷婷狠狠干 | 99热这里精品 | 天天爱天天插 | 综合天天色 | 久久综合网色—综合色88 | 日本一区二区三区视频在线播放 | 人人躁| 国产亚洲精品无 | 国产精品不卡在线播放 | 久章操 | 亚洲一级黄色 | 亚洲精选视频免费看 | 免费在线观看av网址 | 激情综合色综合久久 | 在线观看日韩专区 | 狠狠躁夜夜躁人人爽超碰97香蕉 | 99免在线观看免费视频高清 | 亚洲爱视频| 国产97在线看 | 激情伊人五月天久久综合 | 丁香在线观看完整电影视频 | 久草视频免费在线播放 | 国产破处视频在线播放 | 成人在线观看免费 | 色综合天天色 | 国产高清视频 | 人人爽人人爽人人爽人人爽 | 亚洲成人一区 | 日韩一级成人av | 丰满少妇在线观看网站 | 国产精品99久久久 | 成年人在线免费视频观看 | 日韩精品一区二区在线观看 | 久久国产免费看 | 一区二区三区久久 | 久久久久成人精品 | 日韩欧美69| 亚洲精品国偷拍自产在线观看 | 日韩av高潮 | 伊人五月婷 | 欧美一级在线观看视频 | 日本中文字幕在线看 | 久久影视一区 | 精品久久久久久久久久久久久 | 日日操日日插 | 婷婷狠狠操 | 国产一级做a爱片久久毛片a | 久久9999久久免费精品国产 | 日韩欧美精品在线 | 国产精品高清一区二区三区 | 人人澡人人模 | 日日爽日日操 | 国产又粗又猛又黄又爽 | wwwww.国产 | 黄毛片在线观看 | 欧美激情第28页 | 精品在线一区二区 | 不卡的av | 亚洲国产丝袜在线观看 | 香蕉视频网站在线观看 | 国产69久久久欧美一级 | 欧洲亚洲国产视频 | 日韩欧美在线一区二区 | 国产无区一区二区三麻豆 | 精品一区二区综合 | 久久久五月婷婷 | 91精品国产欧美一区二区 | 麻豆91在线看 | 日韩高清黄色 | 免费在线播放av电影 | 免费av在线网站 | 国产精品成人一区二区三区吃奶 | 免费日韩一区二区三区 | 亚洲成人av一区 | 国产小视频精品 | 欧美激情精品久久久久久免费印度 | 欧美激情视频在线观看免费 | 免费日韩在线 | 欧美一级电影 | 成人免费91 | 国产精品美女视频网站 | 一二区精品 | 夜夜操网站| 91专区在线观看 | 西西4444www大胆视频 | 黄色特一级 | 毛片基地黄久久久久久天堂 | 91黄在线看 | 国产婷婷色 | 欧美日韩精品综合 | 久久久精品国产一区二区三区 | 亚洲欧美在线观看视频 | 免费看特级毛片 | 日韩丝袜在线观看 | 一区二区三区在线观看免费 | 国产亚洲精品久久久久动 | 国产中文字幕一区 | 欧美日韩视频免费看 | 狠狠的操狠狠的干 | 国产精品99免视看9 国产精品毛片一区视频 | 91成熟丰满女人少妇 | 天天摸天天操天天舔 | 久久综合亚洲鲁鲁五月久久 | 色综合小说 | 国产一区电影在线观看 | 国产夫妻性生活自拍 | 91精品啪在线观看国产线免费 | 日韩精品一区二区免费视频 | 久久久久在线 | www91在线观看| 国产专区在线看 | 最近中文字幕大全 | 日日色综合 | 久久久久久久久久影视 | 婷婷色综合网 | 国产精品女同一区二区三区久久夜 | 一区二区三区国 | 91最新网址在线观看 | av不卡免费看 | 97夜夜澡人人双人人人喊 | 午夜视频在线观看一区二区三区 | 免费成人黄色av | 久久这里只有精品视频99 | 夜添久久精品亚洲国产精品 | 国产一级视频在线观看 | 婷婷在线精品视频 | 国产麻豆精品一区二区 | 精品国产1区2区 | 一级免费观看 | 欧美在线观看视频 | 久久久久欧美精品 | 国产一区二区成人 | 亚洲aaa级 | 国产成人精品亚洲精品 | 成人午夜剧场在线观看 | 日韩欧美在线视频一区二区三区 | 久久午夜免费视频 | 亚洲另类视频 | 日韩h在线观看 | 美女久久久久 | 久久国产美女 | 国产视频亚洲精品 | 成人片在线播放 | 九草在线观看 | 日韩高清一区 | 国产精品久久二区 | 亚洲一二区精品 | 日韩欧美高清 | 国产精品99久久免费观看 | 久久 一区 | 久久精品欧美一 | 久久精品1区2区 | 日日夜夜干 | 夜夜操天天干, | 丁香五月网久久综合 | 最近中文字幕在线播放 | 人人爽人人爽人人爽学生一级 | 99视频在线免费播放 | 一区三区视频在线观看 | 丁香在线观看完整电影视频 | 免费观看黄色12片一级视频 | 丁香六月激情 | 亚洲永久免费av | 亚洲国产精品va在线 | 国产精品理论片在线播放 | 国产精品美女免费 | 亚洲综合视频网 | 中文字幕在线观看2018 | 久久久久久看片 | 亚洲第一av在线播放 | 超碰97在线资源 | 日韩午夜在线播放 | 欧美成人区 | 欧美91精品久久久久国产性生爱 | 久久久久久久久亚洲精品 | 国产精品18久久久久久久久久久久 | 欧美美女视频在线观看 | 在线黄色av电影 | 亚洲欧美偷拍另类 | 日韩av片免费在线观看 | 欧美精品亚洲精品日韩精品 | 欧美成人aa| 久久99精品国产 | 伊人天天狠天天添日日拍 | 免费视频成人 | 久久综合一本 | 亚洲精品色婷婷 | 亚洲最大成人免费网站 | 免费高清无人区完整版 | 91视频麻豆 | av在线播放不卡 | 一级黄色片在线播放 | 久久首页| 久久精品福利 | av导航福利 | 亚洲欧美日韩国产 | 国产在线播放观看 | 最新99热 | 色播五月激情五月 | 精品国产三级 | 在线视频免费观看 | 亚洲激情 欧美激情 | 五月天久久精品 | 激情五月伊人 | 欧美另类激情 | 天天干天天操av | 又粗又长又大又爽又黄少妇毛片 | 毛片网在线观看 | 热久久99这里有精品 | 国产精品久久久久高潮 | 国产精品久久久久久久久大全 | 伊人亚洲综合网 | 最近免费观看的电影完整版 | 久草www| 久久精品视频网站 | 天天综合网久久 | 欧美一级日韩三级 | 成人免费视频免费观看 | 久久国产高清 | a√天堂中文在线 | 依人成人综合网 | 免费黄色小网站 | 色婷丁香| 国产成人三级 | 国产伦精品一区二区三区照片91 | 国产免费观看久久黄 | 99热只有精品在线观看 | 欧美一区免费在线观看 | 久久视频99 | 免费在线观看黄 |