让你的代码在 main 或者 WinMain 之前执行!

本文介绍如何在C/C++程序中,在main函数执行前后分别进行系统框架的初始化和释放操作。通过利用特定的宏定义和内存段,可以在程序启动之初及退出前自动调用指定的函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目标环境: WindowXP SP3 + VS2008.NET

开发语言: C/C++

目标生成: Console Application and Window Application.

有的时候,需要会遇到这样的一个情况,“想要在main或者WinMain函数执行前初始化我们的系统框架,和在之后释放我们的系统框架”, 如果这样,我们该怎么做呢?笔者今天放假,来公司继续解决昨天剩余的问题,然后调试exit函数的时候,会发现它会调用

static void __cdecl doexit (int code,int quick,int retcaller)这样的一个内部函数,那么继续单步调试,那么你就会发现

/* cache the function to call. */ function_to_call = (_PVFV)_decode_pointer(*onexitend); /* mark the function pointer as visited. */ *onexitend = (_PVFV)_encoded_null(); /* call the function, which can eventually change __onexitbegin and __onexitend */ (*function_to_call)(); onexitbegin_new = (_PVFV *)_decode_pointer(__onexitbegin); onexitend_new = (_PVFV *)_decode_pointer(__onexitend);

这段代码,OK,我们移上去(function_to_call)看看,你会发现是那些全局类变量的析构调用,那么我们继续往下面走,_initterm(__xp_a, __xp_z); 这个函数, 我第一次看到这个函数的时候,莫名其妙想在里面做文章了。。。OK,我们去看看这个函数内部到底是干什么的

/* from the CRT */ typedef void (*_PVFV)(void); void _initterm(_PVFV *begin, _PVFV *end) { _PVFV *cur; for (cur = begin; cur < end; cur++) { /* skip NULL pointers */ if (*cur) { (**cur)(); } } }

很容易就可以看到,他就是一个循环遍历,执行每个元素,而仔细看看,那每个元素不就是一个函数指针吗? OK,我们想实现前面所说的功能,该怎么做呢?怎么能够自动往里面添加我们的函数指针呢? 如果能够自由添加我们的函数指针的话,就可以在main和WinMain之前,之后分别调用我们自己的 “初始化”“退出”函数了。那么我们继续在这个源文件中“crt0dat.c”中看看其他代码,或许你会幸运的发现如下代码:

extern _CRTALLOC(".CRT$XIA") _PIFV __xi_a[]; extern _CRTALLOC(".CRT$XIZ") _PIFV __xi_z[]; /* C initializers */ extern _CRTALLOC(".CRT$XCA") _PVFV __xc_a[]; extern _CRTALLOC(".CRT$XCZ") _PVFV __xc_z[]; /* C++ initializers */ extern _CRTALLOC(".CRT$XPA") _PVFV __xp_a[]; extern _CRTALLOC(".CRT$XPZ") _PVFV __xp_z[]; /* C pre-terminators */ extern _CRTALLOC(".CRT$XTA") _PVFV __xt_a[]; extern _CRTALLOC(".CRT$XTZ") _PVFV __xt_z[]; /* C terminators */

MS也给我们写了一点注释, __xi_a[] 到 __xi_z[] 是用于 C语言初始化用的, __xc_a[] 到 __xc_z[] 适用于C++初始化用的,而

__xp_a[] 到 __xp_z[] 适用于C基本库预结束用的, __xt_a[] 到 __xt_z[] 适用于C基本库结束用的。通过这里,我们可以看到那个字符串中的英文字母".CRT$XIA" - ".CRT$XIZ", 我们可以分析出一些简单的规律,

I --> C initialize;

C --> C++ Initialize

P --> C PreUninitialize

T --> CUninitialize

这时候,我们需要查找 _CRTALLOC 宏定义, 通过查找它所引用的头文件,可以看到其中的sect_attribs.h:

...................................... #pragma section(".CRTMP$XCA",long,read) #pragma section(".CRTMP$XCZ",long,read) #pragma section(".CRTMP$XIA",long,read) #pragma section(".CRTMP$XIZ",long,read) ...................................... #define _CRTALLOC(x) __declspec(allocate(x))

OK,得到上述的那些信息,我们可以开始尝试了,输入以下代码:

#include <stdio.h> #include <stdlib.h> #include <malloc.h> class A { public: A() { printf("A()\n"); } ~A() { printf("~A()\n"); } }; A a; bool b = false; int foo() { if (!b) { printf("init now!\n"); b = true; } else { printf("uninit now\n"); } return 0; } typedef int (__cdecl *_PVFV)(); #pragma section(".CRT$XIU", long, read) #pragma section(".CRT$XPU", long, read) __declspec(allocate(".CRT$XIU")) _PVFV mgt_startup[] = { foo }; __declspec(allocate(".CRT$XPU")) _PVFV mgt_exit[] = { foo }; int main() { printf("main.!\n"); //exit(1); return 0; }

那么,你可以Ctrl+F5测试一下, 你可以看到 init A() main() ~A() uninit 这样的顺序,呵呵,总之目标实现啦,其实原理也很简单的,我就不讲啦, 杭州天气太热了。。。

参考文章:

1. http://www.codeguru.com/cpp/misc/misc/threadsprocesses/article.php/c6945__1/Running-Code-Before-and-After-Main.htm

2. http://book.51cto.com/art/200904/121077.htm

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值