在函数退出时,实现某些子函数(例如:释放内存,关闭句柄)调用的自动化有两个好处:
1、 减少程序员劳动强度,使得代码变得更加简洁顺畅。增强代码的可读性。
2、 增强代码的健壮性。
在C语言中如何释放资源?我们必需把return 语句替换成如下这样:
#define vreturn returnfunc();
return
#define LIST_NAME
g_unique_exit_head_list
#define LOCAL_EXIT_INIT
PCALL_LISTLIST_NAME=NULL
#define LSET_EXIT_FUNC
setexitfunc
typedef
struct
_M_MM
{
struct
_M_MM
*
pNext
; //
指向下一个要释放的资源节点
DWORD
ParamSpace
; //
资源释放函数需要的参数字节数
LPVOID
pFunc
; //
释放资源的函数
DWORD
param
[1]; //
可变长数组,存放参数
}
CALL_LIST
,*
PCALL_LIST
;
为了说明问题,下面请看一段代码
….
If((FileHandle=CreateFile(…)==0)
{
….
return STATUS_ERROR;
}
….
If((pmem=VirtuallAlloc(…)==0)
{
…
CloseHandle(FileHandle);
return STATUS_ERROR;
}
….
以上代码替换成如下这个样子
….
If((FileHandle=CreateFile(…)==0)
{
….
vreturn STATUS_ERROR;
}
LSET_EXIT_FUNC(LIST_HEAD,1
,CloseHandle, FileHandle
);
….
If((pmem=VirtuallAlloc(…)==0)
{
//CloseHandle(FileHandle); 本应在退出之前加入这句,把return换成vreturn之后就不用了。
vreturn STATUS_ERROR;
}
LSET_EXIT_FUNC(LIST_HEAD,1
,VirtualFree, pmem
);
….
这种释放资源的方法,将在下一个产品作为一个标准的编码方法引入,以便减少退出之前忘了调用相关函数而产生的错误。这种错误一旦发生,往往出错位置比较隐蔽,在实验室当中肯定无法重现,而且需要客户配合才能解决。
说了这么多,不知道细心的朋友是否会注意到,因为退出的时候,需要调用不同的函数释放资源。那么肯定会面临这样一个问题:被用来释放资源的函数,可能是_stdcall函数,可能是_cdecl类型,可能有的函数需要三个参数,有的却不需要参数。
对于_stdcall类型的函数,我的实现方法如下:
returnfunc()
{
PCALL_LIST
pList
,
pListNext
;
for
(
pList
=
m_pList
;
pList
;) //
m_pList
链表头。
{
callapi
(
pList
); //
调用释放资源函数
pListNext
=
pList
->
pNext
;
delete
pList
; //
释放链表本身
pList
=
pListNext
;
}
}
void
callapi
(
PCALL_LIST
pList
) //
调用释放资源函数
{
BYTE
*
pesp
,*
pnewesp
;
DWORD
ParamSpace
;
ParamSpace
=
pList
->
ParamSpace
; //
释放
API
函数用到的参数字节数。参数个数
*SIZEOF(DWORD)
_asm
mov
pesp
,esp; //
pesp
-=
ParamSpace
; //
把参数拷贝到当前
ESP
所指的位置
pnewesp
=
pesp
; //
为了下一步调用的函数,设置参数
memcpy
(
pesp
,
pList
->
param
,
ParamSpace
); //
pesp
= (
BYTE
*)
pList
->
pFunc
; //
_asm
{
mov eax,
pesp
mov esp,
pnewesp
call eax //
这里调用释放资源函数,因为是
_STDCALL
函数,
//
所以不需要自己填平堆栈,如果是
_cdecl
函数,
//
应该如何填平堆栈呢?因为
esp
已经变化了,
//
应该如何知道
esp
的正确位置呢??
}
}
对于_cdecl类型的函数如何调用,我还没有很好的方法,如何恢复堆栈。各位高人一起想想看:)。