Error handling using SEH (transfer)

xiaoxiao2021-03-06  70

The error handles the translation of the idle, today is about the error handling. The following content is not my original, I just collected them together.  错 处理 处理 处理 处理 处理 处理A comparative underlying thing. It has a stable error handling system. It is a good system basis. From the perspective of development, the error handling is generally the following ways. Compare the basic, use the return value to indicate the error or Correct, such as using INT as a return value, 0 means normal 1 means an error, this is an approach to the C language, such as the WINDOWS API is transmitted using this way. It is obvious that the light is a The 32-bit value is not too complicated. At this time, each implementation method, such as the getLastError function used by Windows. COM also uses this way, most of the functions return a HRESULT, he is currently a TypedEf, In fact, it is a 32-bit long. This long is divided into several parts, each part represents a different meaning, this can be found in the MSDN or Windows's header file. This method is more obvious error transfer method. But the disadvantage is also very obvious. Because the return value is used to pass the error message, the function itself is returned to use other ways, and the C language can only use the video address, this is often seen in the Windows API. In addition, the error message is passed by the return value, so the error check must be caller to complete, you have to write a test statement like if-else, and if you reveal such a statement, you can think of it. Things, and must be a layer returned to the error message, which is not only on the program, but also has a lot loss on the reading of the readability of the entire code. But in the C language, this is also a way .windows also provides a solution for this problem, SEH - structural communication, saying that he can completely handle errors, and he is not the error in semantics, but the BUG of the program, for example, I have to open a file, but this file is damaged for some reason, this item belongs to the programs should find a corrected error, not the task that is completed by Windows .Windows just captures the illegal, except 0 type error (of course, you can call your own RaiseException to achieve the same purpose) .seh looks like the following way: __try {int * p = 0;  * p = 0;                                                          The mechanism of the operating system level, and the exception of C is the mechanism of the language level. Although it is possible to see C exceptions and some relationships of Windows SEH (refer to the C exception Implemented by MSVC). SEH implementation The above details can refer to the article of Under the hood of MSJ, a Crash Course On The DePths of Win32 Structured Exception Handling. The above article has a more detailed explanation of Windows SEH, simple saying is Windows in each thread. A linked list is saved inside the Tib (Thread Information Block), which puts some callbacks that occur when Windows will call the callback function to be invoked. When the exception occurs, the Windows starts from the beginning of the chain list those callback functions, callback The function returns the appropriate value indicates whether this exception is handled. If not, the next chain table node that Windows moves, if there is no one in the end of the linked list, Windows will go to a default function, this function Will display a basket that everyone should have seen on the screen - the app has an error, will be turned off, and there is a button for more information. There may be no use in C may not use this return value. The way I personally think that C programmers should give exception. Although many people have excluded this new thing, C exception mechanism will save programmers to check the return value, you don't have to check The return value of the function (in the previous is required, no matter where you don't care, you must check it, you must check it, because you have a responsibility to return this return value to call you), in C

With the help of an abnormal mechanism, you can write code without the repeated value of the depends function, so the error should be processed by people, those who don't want to handle errors, can not be handled as errors. The same is true. You must take the following code segment   void afunctionmaymeetsomeerror ()                                                                                                                                 }                                                                                                                                                                                                                                                                                                     Catch (Exception & E)                                                           The first function, but he didn't want to deal with this error (maybe it is the intention of the program itself, maybe he doesn't know how to deal with this error), and the third function is the real error handler, he creates a try -catch structure to capture this error. This can save a lot of code, and the code is more good above the readability. It is a personal task that uses the try-catch structure to make a big simplified error. It is very useful, but using try-catch will bring additional overhead. This overhead is mainly reflected in the length of the code, and there is no much impact on the speed of operation (this can be used from the compiler. It is seen that many people who oppose unusual people will reduce running speed. Ha ha) As a programmer of C , now we have a powerful error handling tool, now the problem is expected, for the programs Abnormal, it is more able to handle, for exceptions expected in those programs, we want to get more detailed information, such as the call stack of functions, the number of files located in the source code, etc., more even, we want to know when an exception When you happen, the specific information of our program, local variable, global variable value, and then we may generate a crash.log file, you want to return this log file. We can analyze bugs, etc. At this time C exception It is very little thing that can be made. We can also use __file __, __ line __file __, __ line __, __ functioni ON__ This kind of compiled macro is acquired, but other things are unlikely to rely on the C language itself, this time you may ask for SEH, because Windows will prepare enough information when the exception occurs, then Call our registered exception handler, in this information, you can find what you want.   here is the key to the content I want to introduce, above ... 嘿嘿 ...   first look Why don't we rely on other features to achieve the function of obtaining the source code file function. We define the following exception class  Class CEXCEPTION {STD :: string m_strfile ;std :: String M_StrFunction ;st :: string m_strdes;                                             # String strdes): m_strfile (strfile), m_nline (nline), m_strdes (strdes) {}lpctstr what () const #                                                                                                                    Error information}};

  then we define the following Hong # define throwexception (x) throw Cexception (__ file __, __ function __, __ line __, x)                                                    Role # i in _     这样 类 类 类 类 类 类 类 类 类 类 类 类 信息 类 类 类 信息 信息 信息 信息 信息 信息 信息 信息 信息 信息You can also use a little skill. If you output this exception information to the VC's Debug Output window, you can double-click the place where you have an exception, just like you have a mistake when compiling, you can position To the wrong location, the method is very simple, you use the "file name (line number)" format output is OK. However, we don't satisfy such a small tips, we need more information. At this time, We have to use Windows SEH. Windows calls to your setup function (this function is not set by the compiler when the exception occurs, but the function of the compiler is completed, the VC compiles the C TRY "function to set the function The name is __cxxframehandler, when the __try structure is set, _exception_handle3), and C

The exception has been exhausted. We look at the __try structure, what is the compiler, the compiler will perform the contents of us written in the __except back parentheses, in this parentheses we can call 2 functions GetExceptioninformation () and getExceptioncode (), this two functions can return the information we want, note that these two functions can only be called inside the brackets behind __except. We can call our own functions in this parentheses. The return value of a function can be used as a parameter. It is obvious that we can make a lot of things in this nature. Must pay attention to our function must return a few fixed values, telling Windows This exception should be handled or Do not process, we build the following structure                                                           The Crashfilter function is the Crashfilter function. We can do whatever you want. First, getExceptioninformation () returns a pointer for Exception_Pointers, and he contains two members, one is pEXception_record, he is a pointer, record the basic basics The situation, PCONTEXT is also a pointer, which records the thread when the abnormality occurs, the value of the register (we want DUMP all registers), there is two things, we can do a lot The things are got from the Context to locate an exception module (using VirtualQuery to get the memory block located in this memory address (EIP), then use the start address of this memory block to call getModuleFileName to get GetModuleFileName To the name of the module, this area can refer to how many other examples, to how to get the list of modules in the go, you can find a detailed method). With EIP, we can also read the content of the exception instruction, use directly The value of EIP is OK (because Windows uses FLAT address mode), then uses exception code (can be obtained from exception_pointers can be obtained) to get exception information, this information is mostly from MSDN Find, others can get tuning in ntdll.dll (call the formatMessage function, specify the module handle of NTDLL.DLL), then maybe you want to collect the CPU type of the target computer, memory status State, operating system information, etc. (These can be obtained by getSystemInfo, GlobalMemoryStatus, getVersionex function, which can search for detailed methods on Google). Then you may dump stack, this time there is a tip, Win32 below The thread of the thread is always placed in the specified section of the FS: [4] This place is put on this place is the TOP address of the stack, and the current stack is recorded in the Context, you have to do the Context's ESP pointer to FS:

The memory between [4] All DUMP is OK. Then maybe you want to list all the DLL names in the current process, and the information of the DLL, this is also in the VirtualQuery function, the basic method is to traverse 4G virtual address space , Repeated calls the VirtualQuery function, once it is found to be a legal memory address space, call the getModuleFileName function If it is successful, it is a DLL. At this time you can get the DLL DOS file header, get further to the NT file header, then get it All of the DLL ... You have to know that a DLL and EXE Module Handle are actually the start of the DLL and the EXE file in the memory, starting from this address DLL and EXE file DOS file header. With these things, it is also very boring. Your dump is either a lot of things, or there is really no way to read the information, and those 16-based Stack content is not used. The next thing is a bit exciting. We want DUMP out of an abnormality to call the stack, the local variable of DUMP out of the function, the value of global variables. This arduous task fell to the Windows's Debug API, Windows released after NT. A file called dbghelp.dll, this file can complete our requirements. Specific information can be viewed by MSDN, I will find the under the hood in MSJ below: IMPROVED ERROR Reporting With DBGHELP 5.1 Note To complete the following content you must have a PDB file with an exe file or a DLL file. VC will help you generate this file, he is the symbol file for debugging. The key part is a few new functions in 5.1. We can get these things you want. This article is not explaining how to use DBGHELP articles, so I have skipped his way of use. You can search for Google, or view msdn.   want DUMP Call Stack, inevitably want to view Stack, this task is handed over to the StackWalk function of DBGHELP lib, you have to prepare a StackFrame structure, pass to the Stackwalk function, the other parameters are easy to get, machine type, process handle, Thread handle, context (just the context can be used), two callback functions (do not have your own implementation, using DBGHELP LIB yourself to implement it), then StackWalk fills the StackFrame structure you pass, then you will Use this structure to call SYMFROM The addr function can get the function name of the current stack position, and there is also the offset of the current PC position relative to the function start code PC. Call the SymgetLineFromAddr function to obtain the source code file name and row information. This will complete the Call Stack Handling process, stackframe sf; MEMSET (& sf, 0, sizeof (sf)); // Initialization StackFrame structure   sf.addrpc.offset = pContext-> EIP;   sf.addrpc.mode = AddrModeFlat; sf.AddrStack.Offset = pContext-> Esp; sf.AddrStack.Mode = AddrModeFlat; sf.AddrFrame.Offset = pContext-> Ebp; sf.AddrFrame.Mode = AddrModeFlat;  dwMachineType = IMAGE_FILE_MACHINE_I386; while (1)  { // Get the next stack frame if (StackWalk (dwMachineType, hProcess, GetCurrentThread (), & sf, pContext, 0, SymFunctionTableAccess, SymGetModuleBase! , 0)) break;  // check the correctness of the frame if (0 == sf.AddrFrame.Offset) break;

 // function name BYTE symbolBuffer [sizeof (SYMBOL_INFO) 1024] being called; PSYMBOL_INFO pSymbol = (PSYMBOL_INFO) symbolBuffer; pSymbol-> SizeOfStruct = sizeof (symbolBuffer Ppsymbol-> maxnamelen = 1024; // offset   DWord64 symdisplacement = 0; // Get symbol   IF (Symfromaddr (HProcess, sf .Addrpc.offset, & symdisplacement, psymbol) Writelog (HFile, Text ("% 4D Function:% HS"), I, Psymbol-> Name;}                                                                                                                             This is very important, because the local variables have their own living environment, they all have their own context, you must set this context when you are dump, this is also easy to complete. Imagehlp_stack_frame imagelpstackframe;   imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset; SymSetContext (hProcess, & imagehlpStackFrame, 0);  unique is that the address you want to set, simply pass the pc just offset the stack frame is ok remember this value is. Different, the information you get can be different. Next to call the Symenumsymbols function enumerate all the variables. He needs you to provide a callback function, it is clear that all work is completed in a function. In enumerating global variables Use this function, the only difference, the global function does not need to specify the context.   When the DBGHELP enumerates a variable, he will prepare the basic information of this variable, then call your callback function Your function. A like this like this, Bool Callback EnumerateSymbols Callback (psymbol_info psyminfo, ulong symbolsize, pvoid userContext)   first is the symbolic information, you use this information to get the structure you want, the second is the size, basically ignore, the last one is the symbolic context, In keeporizing the partial variables is the connection of [EBP- ??], which is accessible to us, what we want to do is to use info and context to generate the appropriate output   first judge the type of this symbol (Info->

转载请注明原文地址:https://www.9cbs.com/read-91412.html

New Post(0)