In general, in order to end our program, we will use return or call exit () in the main function (). There are also exitprocess () and terminateprocess () functions in windows.
The purpose of this article is to compare the differences between the above methods of program termination and analyze its principles.
First, we will use an example to illustrate the differences between several ending methods.
The test environment is Windows XP Home SP2 and the compiler is visual studio.net 2003.
The test code is as follows:
# Include
# Include
# Include
Class Test
{
Public:
Test (int I) {m_ I = I; printf ('construct % dn', m_ I );};
~ Test () {printf ('struct % dn', m_ I );};
PRIVATE:
Int m_ I;
};
Test T_1 (1 );
Int main (INT argc, char * argv [])
{
Test T_2 (2 );
Printf ('Hello world! N ');
// Return 0;
// Exit (0 );
// Exitprocess (0 );
}
Our goal is to see how different the two ending methods are.
The running result of the program is:
End with return 0:
Construct 1
Construct 2
Hello world!
Destruct 2
Destruct 1
End with exit (0:
Construct 1
Construct 2
Hello world!
Destruct 1
End with exitprocess (0:
Construct 1
Construct 2
Hello world!
From the results, we can see that using return to terminate a process can correctly analyze global and local objects. When exit () is used to end the process, the Global object can be correctly parsed, but the local object is not correctly parsed. When exitprocess (0) is used, the global and local objects are not correctly parsed.
Why is this happening?
In Windows core programming, we can get the following explanation:
'When the entry point function (winmain, wwinmain, main, or wmain) of the main thread returns, it returns the startup code in the C/C ++ runtime, it can correctly understand all the C-runtime resources used by the process. After resources are released in the C runtime, the C Runtime startup Code explicitly calls exitprocess and passes the value returned by the entry function to it. '
By tracking the code, we can find that:
Return 0 actually performs the following operations:
Return 0;
00401035 mov dword ptr [ebp-0D4h], 0
0040103f Lea ECx, [T_2]
00401042 call test ::~ Test (4010f0h)
00401047 mov eax, dword ptr [ebp-0D4h]
}
0040104d push edX
0040104e mov ECx, EBP
00401050 push eax
00401051 Lea edX, DS: [401072 H]
00401057 call _ rtc_checkstackvars (4011e0h)
0040105c pop eax
0040105d pop edX
0040105e pop EDI
0040105f pop ESI
00401060 pop EBX
00401061 add ESP, 0d8h
00401067 cmp ebp, ESP
00401069 call _ rtc_checkesp (4011b0h)
0040da-e mov ESP, EBP
00401070 pop EBP
00401071 RET
After ret, the program returns the code to start the main function and performs the following operations:
If (! Managedapp)
Exit (mainret );
_ Cexit ();
It can be seen that the destructor of the local object T_2 is called on return 0.
While
Void _ cdecl exit (
Int code
)
{
Doexit (Code, 0, 0);/* full term, kill process */
}
Void _ cdecl _ cexit (
Void
)
{
Doexit (0, 0, 1);/* full term, return to caller */
}
In fact, the program calls the doexit function.
Static void _ cdecl doexit (
Int code,
Int quick,
Int retcaller
)
{
# Ifdef _ debug
Static int fexit = 0;
# Endif/* _ debug */
# Ifdef _ Mt
_ Lockexit ();/* assure only 1 thread in exit path */
_ Try
# Endif/* _ Mt */
If (_ c_exit_done = true)/* If doexit () is being called recursively */
Terminateprocess (getcurrentprocess (), Code);/* terminate with extreme prejudice */
_ C_termination_done = true;
/* Save callable exit flag (for use by Terminators )*/
_ Exitflag = (char) retcaller;/* 0 = term ,! 0 = callable exit */
If (! Quick ){
/*
* Do _ onexit/atexit () Terminators
* (If there are any)
*
* These Terminators must be executed in reverse order (LIFO )!
*
* Note:
* This Code assumes that _ onexitbegin points
* To the first valid onexit () entry and that
* _ Onexitend points past the last valid entry.
* If _ onexitbegin = _ onexitend, the table
* Is empty and there are no routines to call.
*/
If (_ onexitbegin ){
While (-- _ onexitend> = _ onexitbegin)
/*
* If current table entry is non-null,
* Call thru it.
*/
If (* _ onexitend! = NULL)
(** _ Onexitend )();
}
/*
* Do pre-Terminators
*/
_ Initterm (_ xp_a, _ xp_z );
}
/*
* Do Terminators
*/
_ Initterm (_ xt_a, _ xt_z );
# Ifndef crtdll
# Ifdef _ debug
/* Dump all memory leaks */
If (! Fexit & _ crtsetdbgflag (_ crtdbg_report_flag) & _ crtdbg_leak_check_df)
{
Fexit = 1;
_ Crtdumpmemoryleaks ();
}
# Endif/* _ debug */
# Endif/* crtdll */
/* Return to OS or to caller */
# Ifdef _ Mt
_ Finally
If (retcaller)
_ Unlockexit ();/* unlock the exit code path */
_ End_try_finally
# Endif/* _ Mt */
If (retcaller)
Return;
_ C_exit_done = true;
_ Crtexitprocess (CODE );
}
Some of the source code is as follows:
If (_ onexitbegin ){
00406056 cmp dword ptr [___ onexitbegin (412da8h)], 0
0040605d je doexit + 70h (406090 H)
While (-- _ onexitend> = _ onexitbegin)
0040605f mov edX, dword ptr [___ onexitend (412da4h)]
00406065 sub edX, 4
00406068 mov dword ptr [___ onexitend (412da4h)], EDX
0040606e mov eax, dword ptr [___ onexitend (412da4h)]
00406073 CMP eax, dword ptr [___ onexitbegin (412da8h)]
00406079 JB doexit + 70 h (406090 H)
/*
* If current table entry is non-null,
* Call thru it.
*/
If (* _ onexitend! = NULL)
0040607b mov ECx, dword ptr [___ onexitend (412da4h)]
00406081 cmp dword ptr [ECx], 0
00406084 je doexit + 6eh (40608eh)
(** _ Onexitend )();
00406086 mov edX, dword ptr [___ onexitend (412da4h)]
0040608c call dword ptr [edX]
}
0040608e JMP doexit + 3fh (40605fh)
The program jumps to the following code at 0040608c:
0040ec10 push EBP
0040ec11 mov EBP, ESP
0040ec13 sub ESP, 0c0h
0040ec19 push EBX
0040ec1a push ESI
0040ec1b push EDI
0040ec1c Lea EDI, [ebp-0C0h]
0040ec22 mov ECx, 30 h
0040ec27 mov eax, 0 cccccccch
0040ec2c rep STOs dword ptr [EDI]
0040ec2e mov ECx, offset T_1 (412760 H)
0040ec33 call test ::~ Test (4010f0h)
0040ec38 pop EDI
0040ec39 pop ESI
0040ec3a pop EBX
0040ec3b add ESP, 0c0h
0040ec41 cmp ebp, ESP
0040ec43 call _ rtc_checkesp (4011b0h)
0040ec48 mov ESP, EBP
0040ec4a pop EBP
0040ec4b RET
The global variable T_1 is destructed here.
At the end of doexit, the program calls
_ Crtexitprocess (CODE );
Void _ cdecl _ crtexitprocess (
Int status
)
{
Hmodule hmod;
Pfn_exit_process PFN;
Hmod = getmodulehandle ('mscoree. dll ');
If (hmod! = NULL ){
PFN = (pfn_exit_process) getprocaddress (hmod, 'corexitprocess ');
If (PFN! = NULL ){
PFN (Status );
}
}
/*
* Either mscoree. dll isn' t loaded,
* Or corexitprocess isn' t exported from mscoree. dll,
* Or corexitprocess returned (shocould never happen ).
* Just call exitprocess.
*/
Exitprocess (Status );
}
Here, exitprocess is finally called. At this point, the Global Object T_1 and the local object T_2 have completed the destructor.
From the analysis process, we can draw the following conclusions.
In Windows, the actual execution process of return 0 is:
- First, analyze the local objects in the main function.
- Return to the function that calls main.
- Call the exit function. The exit function calls the doexit function to analyze global objects.
- Finally, call exitprocess to end the process.
Therefore, exitprocess is not responsible for the analysis of any object. Exit is only responsible for the analysis of global objects. Return 0 can parse local objects and call exit. Therefore, it can parse all objects.
ZL = http://chriszz.bokee.com/896602.html Author: chriszz