一种API的缓冲区使用方法
很多API函数都需要传递一个记录结构或者指针作为参数,对于固定的纪录结构比较好办,声明一个相应的结构就可以了。但是对于QueryServiceConfig这个API函数来说,需要一个PQueryServiceConfig指针作为参数,但是记录结构里面有N个Pchar参数,那么怎么来传递参数和使用呢?
_QUERY_SERVICE_CONFIGA = record
dwServiceType: DWORD;
dwStartType: DWORD;
dwErrorControl: DWORD;
lpBinaryPathName: PAnsiChar;
lpLoadOrderGroup: PAnsiChar;
dwTagId: DWORD;
lpDependencies: PAnsiChar;
lpServiceStartName: PAnsiChar;
lpDisplayName: PAnsiChar;
end;
直接传递一个TQueryServiceConfig的地址是不行的,例如下面的代码不行:
var
Buffer:TQueryServiceConfig;
R:DWORD;
begin
....
QueryServiceConfig(ServiceHandle, Buffer, SizeOf(Buffer), R);
......
end;
明眼人一看就知道因为其中的PAnsiChar没有分配内存造成的,那么我们必须为他们分配内存了,看样子下面的代码应该没有问题了吧?
var
Buffer:TQueryServiceConfig;
R:DWORD;
begin
....
GetMem(Buffer.lpBinaryPathName,1024);
GetMem(Buffer.lpLoadOrderGroup,1024);
......
QueryServiceConfig(ServiceHandle, Buffer, SizeOf(Buffer) + 1024 * 5, R);
......
FreeMem(Buffer.lpBinaryPathName);
FreeMem(Buffer.lpLoadOrderGroup);
.......
end;
实际上,这个代码是错误的,无法正常运行的!那么到底如何做呢?很简单,我们传递参数的时候,根本考虑什么记录结构,只要传递一个整块的内存就可以了!API函数会自动填充相应的参数指针为你申请的内存块内的某个区域,里面的数据按照某个顺序排列的。正确代码如下:
var
R: DWORD;
ServiceConfig: PQueryServiceConfig;
begin
....
QueryServiceConfig(ServiceHandle, ServiceConfig, 0, R); // Get Buffer Length
GetMem(ServiceConfig, R + 1);
QueryServiceConfig(ServiceHandle, ServiceConfig, R + 1, R);
// ServiceName := ServiceConfig.lpBinaryPathName;
.....
FreeMem(ServiceConfig);
end;
警告:很多API函数都需要一个指针和一个长度作为参数,但是很多人传递参数的时候,直接申请固定长度内存作为参数,这样是很危险的,例如上面的代码,固然你可以直接使用:
var
R: DWORD;
ServiceConfig: PQueryServiceConfig;
begin
....
GetMem(ServiceConfig, 4096);
QueryServiceConfig(ServiceHandle, ServiceConfig, 4096, R);
// ServiceName := ServiceConfig.lpBinaryPathName;
.....
FreeMem(ServiceConfig);
end;
这样是非常危险的,如果返回的数据超过4096,那么你的程序还能正常运行吗?因此一般的在调用这种API之前,先用一次0长度的Call,这样会返回必须的长度,然后申请内存,再正常调用即可,以前我给过一个帖子讨论这个问题,在这里再强调一次。