首页  编辑  

一种API的缓冲区使用方法

Tags: /超级猛料/API.Windows应用程序接口/其他相关/   Date Created:

一种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,这样会返回必须的长度,然后申请内存,再正常调用即可,以前我给过一个帖子讨论这个问题,在这里再强调一次。