从其它平台迁移而来
由于长期使用Delphi开发,又与硬件打交道比较多,不可避免地要与标准C动态库进行对接,而往往厂家提供的SDK又偏偏没有Delphi的,无奈也就只好自己改写.h头文件了。写得多了,也就有了一点点心得,在这里就分享出来,也好与大家互相交流、学习。
知识点
-
标准C动态库使用的都是单字节字符。
-
Delphi 2007以前默认使用的是单字节字符,即Ansi编码,也就是说Char = AnsiChar、PChar = PAnsiChar、string = AnsiString;Delphi 2009以后使用的是双字节字符,即Unicode编码,也就是说Char = WideChar、PChar = PWideChar、string = WideString。为了保证改写后的.pas文件适用于Dephi的各个版本,应避免使用Char、PChar、string这种类型不明确的数据类型(通常情况下使用AnsiChar、PAnsiChar、AnsiString即可,但特殊情况要特殊处理)。 -
@string[1]才是字符串首地址。 -
Delphi中可以把
AnsiString当作缓冲区来使用,某些情况下比array of Byte要方便得多。 -
对字符串变量第一次使用
SetLengh时会重新分配内存,第二次使用时,若设定的长度比第一次小,则只会进行截断而并不改变已写入的内容,该特性在使用API返回字符串时非常好用。 -
Delphi中的
record是进行过字节对齐的,执行效率高,但占用空间比看到的会略大;packed record是未进行过字节对齐的,执行效率略低,但占用空间与看到的保持一致。也就是说,Delphi中packed record才是与C中的struct等同。 -
在Delphi中
packed record配合case可以实现C中的union,具体是否等同还要看实际定义的字节是否一致(需要对每种数据类型占用的空间十分熟悉)。 -
标准C动态库的API函数或回调函数,在Delphi中均要使用
stdcall;来修饰,以确保传参顺序一致。
数据类型对应关系
| C/C++ 类型 | Delphi 基本类型 | Delphi Window 单元类型 | 说明 |
|---|---|---|---|
| char | ShortInt / Int8 | 8位有符号整型 | |
| char* | PShortInt | ||
| unsigned char / BYTE | Byte / UInt8 | UCHAR | 8位无符号整型,字节型 |
| unsigned char* | PByte | LPBYTE / PUCHAR | |
| short | SmallInt / Int16 | SHORT | 16位有符号整型 |
| short* | PSmallInt | PSHORT | |
| unsigned short | Word / UInt16 | WORD | 16位无符号整型 |
| unsigned short* | PWord | PUSHORT | |
| int / long | Integer / Longint / Int32 | LONG | 32位有符号整型 |
| int* / long* | PInteger / PLongInt | PLONG | |
| unsigned / unsigned int / unsigned long | Cardinal / LongWord / UInt32 | DWORD / UINT / ULONG / ULONG32 | 32位无符号整型 |
| unsigned int* / unsigned long* | PCardinal / PLongWord / PUint32 | PDWORD / PUINT / PULONG | |
| long long / __int64 | Int64 | LONG64 / LONGLONG | 64位有符号整型 |
| long long* / __int64* | PInt64 | PLONG64 | |
| unsigned long long / unsigned __int64 | UInt64 | ULONG64 / ULONGLONG / DWORD64 | 64位无符号整型 |
| unsigned long long* / unsigned __int64* | PUInt64 | PULONG64 / PULONGLONG / PDWORD64 | |
| float | Single / Float32 | 32位单精度浮点型 | |
| float* | PSingle | ||
| double | Double / Float64 | 64位双精度浮点型 | |
| double* | PDouble | ||
| long double | Extended | 10字节浮点型 | |
| char | AnsiChar | 单字节字符 | |
| char* | PAnsiChar | LPSTR / LPCSTR | |
| char** | PPAnsiChar | ||
| wchar_t / WCHAR | WideChar | WCHAR | 双字节字符 |
| wchar_t* | PWideChar | PWChar / LPWSTR / LPCWSTR | |
| wchar_t** | PPWideChar | ||
| 任意1字节类型 | Boolean / ByteBool | 1字节布尔型 | |
| 任意1字节类型指针 | PBoolean / PByteBool | ||
| 任意2字节类型 | WordBool | 2字节布尔型 | |
| 任意2字节类型指针 | PWordBool | ||
| BOOL | LongBool | BOOL | 4字节布尔型 |
| BOOL* | PLongBool | PBOOL | |
| void* | Pointer | PVOID / LPVOID / LPCVOID | 无类型指针 |
| void** | PPointer | PPVOID |
升华
有了以上知识,把.h头文件改为.pas单元是不成问题了,但改写后的API是否好用,还要打个问号。
比如有以下API:
|
|
按前面提到的改为.pas单元:
|
|
改写没有任何问题,但是不如以下这种直观、方便,毕竟有不少人对指针和地址还是相当恐惧的。
|
|
假如以上API为:
|
|
那么,生硬的改成
|
|
就不如改为
|
|
改写不仅仅是为了改写,更是为了调用。当然,若是再加一层封装来给其它单元调用,甚至直接封装成类,那是再好不过了!