2017年6月16日星期五

Jedi Conversion Tutorial

Draft 4
27 JANUARY, 1998
by Andreas Prucha


Contents
1. Anatomy of a C Header
2. Conversion Basics
1. Naming
2. Unit dependencies
3. #defines as constants
1. Hexadecimal values
3. Datatypes
1. Basic Data Types
2. Windows API Common Types
3. Arrays
4. Strings
5. Enumerations
6. Structures, Records
1. Simple Structures
2. Unions in Structures
3. Packed Records
4. Macros
5. Conditionals
6. Functions
1. Basics
2. Calling Conventions
7. Linking
1. Static Linking
2. Dynamic Linking
8. Jedi Common Support Unit


1. Anatomy of a C Header
Back to contents
## to do
2. Conversion Basics
Back to contents
2.1. Naming
Back to contents
The naming in converted header files should follow Borland's style as far as possible. This means, keep the original names, but make them more Delphi-like.
How should the C names be translated into Delphi?
The C-programmer usually uses upper case letters for type identifiers, e.g. MY_TYPE. In Delphi, a type identifier has a T-prefix followed by the name of the type in mixed (proper) case. Underscores are not used. The Delphi-like translation of the C type identifier MY_TYPE is TMyType.
In C older header files the pointer-type is named LPMY_TYPE. In translation to Delphi it should be PMyType to conform with Borland's style.
Constants are usually named identically to the original name, including upper case letters and underscores.
A few examples:


C
Delphi-Translation
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
}
IMAGE_FILE_HEADER,
*
PIMAGE_FILE_HEADER;
type
PImageFileHeader = ^TImageFileHeader;
TImageFileHeader = packed record
Machine: Word;
NumberOfSections: Word;
TimeDateStamp: DWORD;
PointerToSymbolTable: DWORD;
NumberOfSymbols: DWORD;
SizeOfOptionalHeader: Word;
Characteristics: Word;
end;

#define LANG_NEUTRAL 0x00
#define LANG_AFRIKAANS 0x36
#define LANG_ALBANIAN 0x1c
#define LANG_ARABIC 0x01
#define LANG_BASQUE 0x2d
#define LANG_BELARUSIAN 0x23
#define LANG_BULGARIAN 0x02
#define LANG_CATALAN 0x03
#define LANG_CHINESE 0x04
CONST
LANG_NEUTRAL = $00;
LANG_AFRIKAANS = $36;
LANG_ALBANIAN = $1c;
LANG_ARABIC = $01;
LANG_BASQUE = $2d;
LANG_BELARUSIAN = $23;
LANG_BULGARIAN = $02;
LANG_CATALAN = $03;
LANG_CHINESE = $04;
Back to contents
2.2. Unit dependencies
Back to contents
C and C++ use #include to include header files in another header file or a source file. Delphi refers to units (in the USES clause) instead of header files.
For example
D3D.H includes D3DTYPES.H, D3DCAPS.H
D3DTYPES.H includes DDRAW.H
One unit contains one translation of a header file, so incorporation of D3DTYPES.H and D3DCAPS.H is required when translating D3D.H.
Thus the translation of D3D.H (D3D.PAS) needs D3DTYPES.PAS and D3DCAPS.PAS in uses.
In Windows.pas Borland has already done a lot of work for us. Borland's windows unit contains most of the basic Windows datatypes, so any translated unit needing Windows datatypes needs the Windows unit in its uses clause.
Note: In Delphi 1.0, substitute WinProcs.pas and WinTypes.pas for Windows.pas, since these two units include for Delphi 16-bit what Delphi 32-bit defines in Windows.pas. In Delphi 2 these two files are also aliased to Windows.pas in Delphi 32-bit versions for backward compatibility.
Some header files contain source which is not directly translatable into Pascal, but the header file's impact on compilation must be incorporated into the translated code. For example, PSHPACK?.H contains compiler directives that are not directly translatable to Delphi; PSHPACK4.H directs the compiler to use 4-byte packing. More about this later.
Back to contents
2.3. #defines as constants
Back to contents
C and C++ use #defines in several ways. In a C header file #define can be used
  • for declaring a constant
  • for declaring a symbol for conditional compilation
  • for macros
This chapter describes the translation of #define into Delphi constants.
The format for declaring constants in C is:
#define NameOfConstant Value
For example:
#define TIME_ZONE_ID_UNKNOWN 0
#define TIME_ZONE_ID_STANDARD 1
#define TIME_ZONE_ID_DAYLIGHT 2
The translation into Delphi is:

CONST
TIME_ZONE_ID_UNKNOWN = 0;
TIME_ZONE_ID_STANDARD = 1;
TIME_ZONE_ID_DAYLIGHT = 2;
Back to contents
2.3.1. Hexadecimal values
Back to contents
C uses the prefix 0x to specify a hexadecimal value. For example, the C declaration
#define MY_CONSTANT 0xFF
translates to Delphi as

CONST
MY_CONSTANT = $FF;
Back to contents
3. Data Types
3.1. Basic Data Types
Back to contents
Here is a list of C data types and the equivalents in Delphi:
C
Delphi
UNSIGNED LONG
DWORD (Longint)
UNSIGNED SHORT
WORD
INT
Integer
UNSIGNED CHAR
Byte, Char (Depending on usage)
Back to contents
3.2. Windows API Common Types
Back to contents
The Windows API defines some common types for API usage. It is recommended that the same names be used in translations as far as possible. Windows.pas declares most of these types, some of which are listed below:
API Type declaration
Type used in Delphi Translation
Type Specification
ULONG
ULong
DWord
PULONG
PULong
^DWord
USHORT
UShort
SmallInt
PUSHORT
PUShort
^SmallInt
UCHAR
UChar
Byte
PUCHAR
PUChar
^Byte
DWORD
DWord
DWord
PDWORD, LPDWORD
PDWord
^DWord



BOOL
Bool
Bool
PBOOL, LPBOOL
PBool
^Bool
BYTE
Byte
Byte
PBYTE, LPBYTE
PByte
^Byte
WORD
Word
Word
PWORD, LPWORD
Pword
^Word
INT
Integer
Integer
PINT, LPINT
PInteger
^Integer
LPVOID
Pointer
Untyped Pointer
UINT
UInt
Integer
PUINT, LPUINT
PUInt
^Integer



WCHAR
WChar
WideChar
PWCHAR, LPWCHAR, PCWCH, LPCWCH, NWPSTR,
PWChar
^WideChar
PWSTR, LPWSTR
LPWStr
^WideChar
PCWSTR, LPCWSTR
LPCWStr
^WideChar
PCH, LPCH
PChar
^Char
PSTR, LPSTR
LPStr
^Char
PCSTR, LPCSTR
LPCStr
^Char
HANDLE
THandle
DWord
PHANDLE, LPHANDLE
PHandle
^DWord
Back to contents
3.3. Arrays - Two Methods
Back to contents
Method 1:
In C the first index of an array (or vector) is 0 and the declaration specifies the number of elements (not the last index), e.g.
DWORD MyType[4]
Delphi declares arrays as the range of elements, so the same declaration would be
MyType = Array [0..3] of DWORD;


Method 2:
Alternatively, the number of elements may be specified by a constant, e.g
#define NumberOfElements 5
//...
typedef struct _MyRec {
DWORD MyArray[NumberOfElements]
} MyRec, *PMyRec
The same declaration in Delphi is

Const
NumberOfElements = 5
//...

Type
TmyArray = Record
MyArray : Array [0..NumberOfElements-1] of DWORD;
end;

Remember, the range of the array is 0..NumberOfElements-1, but not 0..NumberOfElements.
Back to contents
3.4. Strings
Back to contents
In C, as in Delphi, a string is an array of char types. Often, a string declaration is used in combination with a constant declaration specifying the maximum string length, as the following example shows:
#define RAS_MaxEntryName 256
#define RASENTRYNAMEA struct tagRASENTRYNAMEA
RASENTRYNAMEA
{
DWORD dwSize;
CHAR szEntryName[ RAS_MaxEntryName + 1 ];
};
The first line declares a constant RAS_MaxEntryName with the value 256 specifying the maximum length of the string. The lines after it declare a struct (record) which contains a null-terminated string:
CHAR szEntryName[ RAS_MaxEntryName + 1 ];
The Delphi translation:
szEntryName : Array [0..RAS_MaxEntryName] of Char
Why not Array [0..RAS_MaxEntryName+1] of Char? Recall that a C array starts with 0 and the declaration specifies the number of elements. Thus, Array [0..RAS_MaxEntryName+1] in Delphi is equivalent to [RAS_MaxEntryName+2] in C.


Back to contents
3.5. Enumerations - Two Methods
Back to contents
Enumerated types can be translated in two ways.
Method 1:
typedef enum _FINDEX_INFO_LEVELS {
FindExInfoStandard,
FindExInfoMaxInfoLevel
} FINDEX_INFO_LEVELS;
This part of a C-header file translates easily to Delphi:


Type
TFindExInfoLevels = (FindExInfoStandard,
FindExInfoMaxInfoLevel);

The ordinal Value of the item FindExInfoStandard is 0. In Delphi each enumeration starts with 0.
Method 2:
The following C-declaration is more problematic:
typedef enum _ACL_INFORMATION_CLASS {
AclRevisionInformation = 1,
AclSizeInformation
} ACL_INFORMATION_CLASS;
The "= 1" in the declaration forces C to start with the ordinal value 1. This is not possible in Delphi.
There are two ways to solve the problem.
Solution a): Declare the enumeration as

TACLInformationClass = (_Fill0,
AclRevisionInformation,
AclSizeInformation);

Solution b): Translate the enumeration as

CONST
AclRevisionInformation = 1;
AclSizeInformation = 2;
TYPE
TACLInformationClass = DWORD;

That's no problem for this example. But in C it's possible to specify the ordinal value of each item of the enumeration, for example,
Typedef enum _ENUMEXAMPLE {
Item1 = 5,
Item2 = 10,
} ENUMEXAMPLE;
Using enumeration in Delphi the declaration would have to be done like this:

TEnumExample = (_Fill0,
_Fill1,
_Fill2,
_Fill3,
_Fill4,
Item1,
_Fill6,
_Fill7,
_Fill8,
_Fill9,
Item2);

This is hard to read and clumsy to maintain.
This works better:

CONST
Item1 = 5;
Item2 = 10;
TYPE
TEnumExample = DWORD;

Back to contents
Here is a real-world example from the winnt.h, which is not possible to translate to a native enumerated type:

//
// Start Type
//

#define SERVICE_BOOT_START 0x00000000
#define SERVICE_SYSTEM_START 0x00000001
#define SERVICE_AUTO_START 0x00000002
#define SERVICE_DEMAND_START 0x00000003
#define SERVICE_DISABLED 0x00000004
//
//
//
typedef enum _CM_SERVICE_LOAD_TYPE {
BootLoad = SERVICE_BOOT_START,
SystemLoad = SERVICE_SYSTEM_START,
AutoLoad = SERVICE_AUTO_START,
DemandLoad = SERVICE_DEMAND_START,
DisableLoad = SERVICE_DISABLED
} SERVICE_LOAD_TYPE;


The ordinal values of the items in the enumeration SERVICE_LOAD_TYPE depend on the constants declared above it. This is not possible in Delphi. The only way to translate it is:

//
// Start Type
//
CONST
SERVICE_BOOT_START = $00000000;
SERVICE_SYSTEM_START = $00000001;
SERVICE_AUTO_START = $00000002;
SERVICE_DEMAND_START = $00000003;
SERVICE_DISABLED = $00000004;
//
//
//
CONST
BootLoad = SERVICE_BOOT_START;
SystemLoad = SERVICE_SYSTEM_START;
AutoLoad = SERVICE_AUTO_START;
DemandLoad = SERVICE_DEMAND_START;
DisableLoad = SERVICE_DISABLED;
TYPE
TServiceLoadType = DWORD;

Back to contents
3.6. Structures, Records
3.6.1. Simple Structures
Back to contents
C structures are similar to records in Delphi. Structures are usually defined with the typedef keyword, but it's also possible to do it with #define.
The format of a structure declaration is
*** Andreas, Alan says the structure name is missing from this example ***
{Struct|Union} [OptIdentifier] [TagName]
{ FieldDefinitions [; ...]}
[Name [...]]
You can ignore the TagName. It's used in C for subsequent references to the structure.
This is how the fields within a structure are defined:

#define RASENTRYNAMEA struct tagRASENTRYNAMEA
RASENTRYNAMEA
{
DWORD dwSize;
CHAR szEntryName[ RAS_MaxEntryName + 1 ];
};

This C declaration defines a record (structure) named RASENTRYNAMEA. The Delphi-style name would be TRASENTRYNAMEA. This structure contains two fields: the first is named dwSize and has the type DWORD. The second field is an array of char with RAS_MaxEntryName + 1 elements.
The Delphi translation:

TYPE
PRASEntryName = ^TRASEntryName
TRASEntryName = Record
dwSize : DWORD;
szEntryName : Array [0..RAS_MaxEntryName] of Char
end

Remember, you may not declare the array of char with a range from 0 to RAS_MaxEntryName+1. The reason is, that in C the number of elements is specified, but in Delphi the range of elements. Read more in the chapters about Arrays and Strings.
3.6.2 Unions in Structures
Back to contents
C unions in structures are comparable to variant parts of records in Delphi. Blocks declared in a union structure are not consecutive but overlaid.

typedef struct _PROCESS_HEAP_ENTRY {
PVOID lpData;
DWORD cbData;
BYTE cbOverhead;
BYTE iRegionIndex;
WORD wFlags;
union {
struct {
HANDLE hMem;
DWORD dwReserved[ 3 ];
} Block;
struct {
DWORD dwCommittedSize;
DWORD dwUnCommittedSize;
LPVOID lpFirstBlock;
LPVOID lpLastBlock;
} Region;
};
} PROCESS_HEAP_ENTRY, *LPPROCESS_HEAP_ENTRY, *PPROCESS_HEAP_ENTRY;

This is the translation into Delphi:

type
PProcessHeapEntry = ^TProcessHeapEntry;
TProcessHeapEntry = Record
lpData: Pointer;
cbData: DWORD;
cbOverhead: Byte;
iRegionIndex: Byte;
wFlags: Word;
case Integer of
0: (Block: Record
hMem: Thandle
Reserved : Array [0..2] of DWORD;
end);
1: (Region: Record
dwCommittedSize: DWORD;
dwUnCommittedSize: DWORD;
lpFirstBlock: Pointer;
lpLastBlock: Pointer
end);
end;

Case Integer of starts the variant part of the record. Each variant is identified by an ordinal value. This value has no meaning when the type is being used, but is required for declaration.
Please note the difference between a variant (case-) record and a record without case-declaration.
The following translation is wrong:

type
PProcessHeapEntry = ^TProcessHeapEntry;
TProcessHeapEntry = Record
lpData: Pointer;
cbData: DWORD;
cbOverhead: Byte;
iRegionIndex: Byte;
wFlags: Word;
Block: Record
hMem: Thandle
Reserved : Array [0..2] of DWORD;
end;
Region: Record
dwCommittedSize: DWORD;
dwUnCommittedSize: DWORD;
lpFirstBlock: Pointer;
lpLastBlock: Pointer
end);
end;

This wrong translation would cause Block and Region to be consecutive in memory, and not overlaid, as follows:
LpData, cbData, cbOverhead, iRegionIndex, wFlags
HMem, dwReserved
DwCommittedSize, dwUnCommittedSize, lpFirstBlock, lplastBlock


3.6.3. Packed Records (Alignment)
Back to contents
If a record is packed (aligned) then the starting byte of a field (and consequently also of the record) is aligned with the first byte of a word (16-bit or 2-byte align) or of a DWord (32-bit or 4-byte alignment). This significantly speeds CPU access to the fields, but consumes more memory.
The older APIs (from the 16-bit world) usually use packed structures (one or two byte alignment). It is the default in 16-bit Delphi: there is no difference under Delphi 1 if you write record or packed record.
Four-byte alignment is the default under Delphi32, although some APIs do use packed records under Win32.
It's important to know which type of alignment is used in an API, because the size of a record depends on the alignment.
The following example explains the difference between the coding of 2-byte and 4-byte alignment:

TmyRecNotPacked = Record
FieldA : Word;
FieldB: LongInt;
FieldC: Byte;
End;
TmyRecPacked = packed Record
FieldA : Word;
FieldB: LongInt;
FieldC: Byte;
End;

Offset
0
1
2
3
4
5
6
7
8
9
0
10
11
TMyRecUnPacked
A
A
.
.
B
B
B
B
C
.
.
.
.
TMyRecPacked
A
A
B
B
B
B
C






Now, how to determine whether packing is required or not. In the Microsoft APIs, two methods are used. One is clear and simple:
#pragma pack(n)
Use the packed keyword if n is 1 or 2, but don't use packed if n is 4.
But Microsoft uses #include to control packing by putting the packing pragma in the include file. The following include files are used to control the packing method:
#include
Means
Packed required
pshpack1.h
#pragma pack (1)
Yes
pshpack2.h
#pragma pack (2)
Yes
pshpack3.h
#pragma pack (3)
Yes
pshpack4.h
#pragma pack (4)
No
Poppack.h
Back to previous packing
?
If a header file contains the following line
#include
you can interpret it as #pragma pack (1) and use the packed keywords in records from now until you come to another include of pshpack?.h or to an include of poppack.h.
Four-byte alignment (non-packed records) is the default under Delphi32. But what if the user changes the compiler options and switches off 4-byte alignment? If you need to ensure that 4-byte alignment is enabled you must insert the following line at the top of the unit:
{$ALIGN ON}
A unit header must look at least like this:

Unit MyUnit;
{$ALIGN ON}
You can use the{$ALIGN} directive instead of explicitly using the packed keyword, too, but I prefer explicit packing because it overrides {$ALIGN ON} and makes things quite specific.
NOTE: A field whose length is an odd number of bytes presents a problem if you encounter #pragma pack(2) and use the packed keyword for translating it. The field will be close packed on a byte boundary when it should be packed on a word boundary. A solution is being sought for handling this situation.
Back to contents
4. Macros
Back to contents
In C it's possible to define macros. Macros are not available in Delphi, so functions must be used to translate C-macros. In most cases it's easier to translate a macro based on the information about it from the documentation than trying to translate the code directly.
One example of a macro:
#define PRIMARYLANGID(lgid) ((WORD )(lgid) & 0x3ff)
Here, quite clearly, the macro accepts integers. A valid translation of this macro is

Function PRIMARYLANGID (lgid: DWORD): DWORD;
Begin
Result := lgid and $3FF
End;

The following macro accepts any datatype as parameter:

#define max(a,b) (((a) > (b)) ? (a) : (b))
It compares the values of parameter a with parameter b and returns the higher value. So that our function can pass any datatype, we can use variants.

Function Max (A, B: Variant): Variant;
Begin
If A > B then
Result := A
else
Result := B;
End;

I do recommend implementing the Delphi translation on the basis of the macro's documentation.
5. Conditionals
Back to contents
###ToDo
6. Functions
6.1. Basics
Back to contents
Let's use the following C-declaration as an example of declaring a function:


WINADVAPI
BOOL
WINAPI
ControlService(
SC_HANDLE hService,
DWORD dwControl,
LPSERVICE_STATUS lpServiceStatus
);

[Options] ReturnValueType [Options] FunctionName ( [ParameterList] )
ReturnValueType:
Specifies the type of the return value of the function. In the example above the type of the return value is BOOL. If the function does not return a value the keyword VOID is used and the translation in Delphi is a Procedure. Please note, that the type identifier can also be "hidden" in an identifier defined using #define.
Options:
Options can be a token specifying the calling convention and/or other keywords telling the compiler how to handle the function in question. The most important item is the calling convention. In the Windows-API header files, the calling convention is usually "hidden" in an identifier declared using #define, so you have to look what is defined. The example above uses the WINAPI symbol which is declared as __stdcall. This is necessary for the translation, too.
Every Options item must be assessed for impact and discarded or implemented.

FunctionName:
Specifies the name of the function
ParameterList:
List of parameters passed to the function separated with ",". Parameters are declared via a type identifier or via a type identifier + parameter name combination.
6.2. Calling Conventions
Back to contents
Referring to the example:

WINADVAPI
BOOL
WINAPI
ControlService(
SC_HANDLE hService,
DWORD dwControl,
LPSERVICE_STATUS lpServiceStatus
);

The type of the return value is BOOL. Now let's have a look at the definition of WINAPI. WINAPI is defined in windef.h the following way:
#define WINAPI __stdcall
The __stdcall keyword tells the C-compiler to use the standard-call calling convention for the function, so we have to declare the function using stdcall, too, since the default calling convention is Register in Delphi.
The function accepts three parameters. The first is a parameter with type SC_HANDLE, the second is a DWORD and the third is a pointer to a PServiceStatus structure.
Here is a Delphi translation of the example above:

Function ControlService (hService: SC_Handle;
dwControl: DWORD;
lpServiceStatus: PServiceStatus): Bool; stdcall;

Notes:
1. SC_Handle is defined in WinSvc.pas.
2. The C variable hService appears to conflict somewhat with the Delphi convention that employs H as the initial letter for a handle type. Whilst it seems desirable to use an alternative (hndService:HControlService, for example), Delphi will accept duplicate names in a parameter list (e.g. hwnd: HWND). Since Borland does it and it works, I would recommend staying with that convention.
Let's have a look at another function declaration:

ULONG (FAR PASCAL MAPISENDDOCUMENTS)(
ULONG ulUIParam,
LPSTR lpszDelimChar,
LPSTR lpszFilePaths,
LPSTR lpszFileNames,
ULONG ulReserved
);

This declaration contains a trap. The keyword PASCAL has been used to specify the Pascal-calling convention, which was usually used in the 16bit-Windows. But not under Win32. Look at the following line in the windef.h file:
#define PASCAL __stdcall
The windef.h header declares PASCAL as __stdcall, which specifes the stdcall calling convention, so you have to use stdcall calling convention in this case, too.
Notes
  1. It is very important to trace through all the #include files for any #defines which may affect the translation.
  2. The FAR keyword can be ignored in Delphi.


7. Linking
Back to contents
There are two ways to link a DLL and import a function. Static linking is very easy and the recommended way if the DLL is certain to be available on the client's machine. If the DLL is optional it is better to use dynamic linking (runtime-linking) to make sure that the application does not fail just because of a missing (possibly unimportant) DLL.
Static linking encodes the calling of the library DLL into the code as a direct call with no checking for inability to connect to the library. Dynamic linking calls the library DLL during the application's run-time and has the capability to handle problems with connection to the library. Dynamic linking can occur at startup, or during the program run-time as the library is needed by the user.
Borland usually uses static linking, but a few API translations use dynamic linking. One example is the translation of SMAPI (MAPI.pas).
Let's look at the two ways of linking a DLL and importing a function.
7.1. Static Linking
If using static linking, simply declare the function prototype in the interface section and the implementation section the following way:
INTERFACE
{Function|Procedure} FunctionName [FunctionDeclaration;]
IMPLEMENTATION
{Function|Procedure} FunctionName external DLLName [name 'FunctionExportName'];
The translation of a function prototype was in the previous section. What about importing the function? It's not obligatory to include the parameter part of the function in the implementation section, but you can if you want. As an example to explain how to import a function, let's use the OpenEvent function from the kernel32.dll.
There are two implementations of the function, one with unicode-support (16bit-WideChar) and one with ansi-character (8bit char) support.
The C-declaration is

WINBASEAPI
HANDLE
WINAPI
OpenEventA(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCSTR lpName
);
WINBASEAPI
HANDLE
WINAPI
OpenEventW(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCWSTR lpName
);
#ifdef UNICODE
#define OpenEvent OpenEventW
#else
#define OpenEvent OpenEventA
#endif // !UNICODE

Three function names are declared: OpenEventA for the ansi-character version of the function, OpenEventW for the wide-character (unicode) version, and OpenEvent. OpenEvent uses the widechar version of the function (OpenEventW ) if the UNICODE symbol is declared or the ansichar-version OpenEventA if the UNICODE symbol is not declared.

INTERFACE

{...}
function OpenEventA (dwDesiredAccess: DWORD;

bInheritHandle: BOOL;

lpName: PAnsiChar): THandle; stdcall;

function OpenEventW (dwDesiredAccess: DWORD;

bInheritHandle: BOOL;

lpName: PWideChar): THandle; stdcall;

{$IFDEF UNICODE}

function OpenEvent (dwDesiredAccess: DWORD;

bInheritHandle: BOOL;

lpName: PWideChar): THandle; stdcall;

{$ELSE}

function OpenEvent (dwDesiredAccess: DWORD;

bInheritHandle: BOOL;

lpName: PChar): THandle; stdcall;

{$ENDIF}

{...}

IMPLEMENTATION

Const

kernel32 = 'kernel32.dll';

{...}

function OpenEventA; external kernel32 name 'OpenEventA';

function OpenEventW; external kernel32 name 'OpenEventW';

{$IFDEF UNICODE}

function OpenEvent; external kernel32 name 'OpenEventW';

{$ELSE}

function OpenEvent; external kernel32 name 'OpenEventA';

{$ENDIF}
{...}

Back to contents
7.2. Dynamic Linking
Back to contents
Dynamic Linking is used to link the DLL at runtime.
Handling Static and Dynamic Linking in the Jedi Environment
Because both static and dynamic linking have their strengths, we must be prepared to support both techniques in the Jedi units. However, static linking is the default.
To support multiple methods compiler symbols are used. The two compiler symbols that symbolize dynamic linking are as follows:
If Xxxx_DYNLINK is defined the DLL must be linked dynamically at startup (in the initialization section).
If Xxxx_LINKONREQUEST is defined the DLL is linked dynamically, not at startup, but as needed by the user. Xxxx is the name of the API, e.g. MAPI_DYNLINK or MAPI_LINKONREQUEST.
If neither symbol is defined, static linking is used.
Each import unit should implement the following functions:
Function XxxxInitAPI: Boolean;
Procedure XxxxFreeAPI;
Function XxxxCheckAPI: Boolean;
The functions XxxxInitAPI and XxxxFreeAPI are available for the user if the symbol Xxxx_LINKONREQUEST is defined. The User can call this function to load or free the DLL. XxxxInitAPI returns TRUE, if the DLL has been loaded successfully.
If Xxxx_DYNLINK is defined, but not Xxxx_LINKONREQUEST, these functions are used internally to load or unload the DLL in the initialization section, but they are not available for the user.
The function XxxxCheckAPI returnes TRUE if the API is available, so the return value is TRUE if the DLL has previously been loaded successfully using XxxxInitAPI. When static linking is used, the return value is always TRUE.
Here is an example of how to handle the symbols in a translation:

unit apisample;

interface

USES Windows,
JediUtil;

// There are two conditional defines. One to use dynamic
// run time linking with linking on startup, the other
// to link upon request. In fact the only difference is
// that the library is not loaded in the initialization
// section if the upon-request symbol is defined.
// Both symbols start with the name of the api, e.g. TAPI.
// In this example it is APISAMPLE.
// APISAMPLE_DYNLINK signals that the API should be linked
// via LoadLibrary and GetProcAddress at startup.
// APISAMPLE_LINKONREQUEST signals that the libary should
// not be linked at startup, but via an initialization procedure.

// Since most of the stuff is the same for both we use
// APISAMPLE_DYNLINK as common symbol.

{$IFDEF APISAMPLE_LINKONREQUEST}
{$DEFINE APISAMPLE_DYNLINK}
{$ENDIF}

{$IFDEF APISAMPLE_DYNLINK}

// Define function types and variables for dynamic linking

TYPE
TApiSampleFunc1 = Function (lParam: LongInt): LongInt; stdcall;
TApiSampleFunc2 = Function (wParam: Word): LongInt; stdcall;
VAR
ApiSampleFunc1 : TApiSampleFunc1 = NIL;
ApiSampleFunc2 : TApiSampleFunc2 = NIL;

{$ELSE}

// We don't use dynamic linking so we implement static linking

Function ApiSampleFunc1 (lParam: LongInt): LongInt; stdcall;
Function ApiSampleFunc2 (wParam: Word): LongInt; stdcall;

{$ENDIF}

//
// Linking Control functions
//

// The XxxxInitAPI function follows InitAPI
// naming convention and is only visible if
// Xxxxx_LINKONREQUEST is defined. The same is true
// for the XxxxxFreeAPI function which frees the
// library

{$IFDEF APISAMPLE_LINKONREQUEST}

Function ApiSampleInitAPI: Boolean;
Procedure ApiSampleFreeAPI;

{$ENDIF}

// The XxxxxCheckAPI function returns true if
// the API is available. With static linking
// the function always returns TRUE

Function ApiSampleCheckAPI: Boolean;

implementation

CONST APISampleDLL = 'APISAMPLE.DLL'; // Name of the DLL

{$IFDEF APISAMPLE_DYNLINK}

VAR hDLL : THandle = 0; // Handle to the lib. Only req. for
dyn.link.

Function ApiSampleInitAPI: Boolean;
begin
Result := FALSE;

// Load library if necessary

If hDLL = 0 then hDLL := LoadLibrary (APISampleDLL);

If JediCheckInstanceHandle (hDLL) then
begin

// Set pointers to functions

@ApiSampleFunc1 := GetProcAddress (hDLL, 'ApiSampleFunc1');
@ApiSampleFunc2 := GetProcAddress (hDLL, 'ApiSampleFunc2');

// Everything ok, return true

Result := TRUE;
end
end;

Procedure ApiSampleFreeAPI;
begin
If hDLL <> 0 then
FreeLibrary (hDLL);
hDLL := 0;
end;

{$ELSE}

Function ApiSampleFunc1; external APISampleDLL name 'ApiSampleFunc1';
Function ApiSampleFunc2; external APISampleDLL name 'ApiSampleFunc1';

{$ENDIF}

Function ApiSampleCheckAPI: Boolean;
begin
{$IFDEF APISAMPLE_DYNLINK}
Result := hDLL <> 0;
{$ELSE}
Result := TRUE;
{$ENDIF}
end;


initialization
begin
{$IFDEF APISAMPLE_DYNLINK}
{$IFNDEF APISAMPLE_LINKONREQUEST}
// Call Init if dynamic linking and not link on request
ApiSampleInitAPI
{$ENDIF}
{$ENDIF}
end;
finalization
begin
{$IFDEF APISAMPLE_DYNLINK}
ApiSampleInitAPI; // Call free if dynamic linking
{$ENDIF}
end;
end.

You can use the the JediCheckInstanceHandle function from the common Jedi-support unit to check an instance handle if necessary.

Back to contents
8. The Jedi Common Support Unit

unit JediUtil;

{==========================================================}
{ Jedi Common Support Unit }
{==========================================================}

interface

USES WinTypes,
WinProcs;

{----------------------------------------------------------}
{ Function JediCheckInstanceHandle }
{ }
{ Parameter: }
{ }
{ hInst: Instance Handle }
{ }
{ Returns-Value: }
{ TRUE if the handle is valid, FALSE if not }
{----------------------------------------------------------}

Function JediCheckInstanceHandle (hInst: THandle): Boolean;

implementation

Function JediCheckInstanceHandle (hInst: THandle): Boolean;
begin
{$IFNDEF WIN32}
Result := hInst > HINSTANCE_ERROR;
{$ELSE}
Result := hInst <> 0;
{$ENDIF}
end;

end.

指定目录的图片2值化

```python # -*- coding: utf-8 -*- """指定目录的图片,自适应2值化 """ import os from PIL import Image import numpy as np imp...