130 likes | 241 Vues
Learn about SEH and how to handle errors in Windows using structured exception handling. Examples and best practices included.
E N D
Win32 Programming Lesson 23: SEH That’s right… you’ll never generate an exception, will you?
Where are we? • Looked at DLLs and their many facets • But we’re not very good at handling errors yet… how does Windows do it?
Imagine • Your code will never generate an error • (Try really hard )
SEH • “Structured Exception Handling” • Basically, lets you assume the code is “good” and deal with the errors later • Very similar to the Java/C++ exception handling mechanism but different – don’t confuse the two • Mostly handled by the Compiler not the OS – special code is written to the application
So… • Introducing the Termination Handler • Guarantees that a particular block of code will be executed regardless of what happens above it • __try { // Guarded Body …} __finally { // Termination Handler …}
Example • DWORD Funcenstein1() { DWORD dwTemp; // 1. Do any processing here. __try { // 2. Request permission to access // protected data, and then use it. WaitForSingleObject(g_hSem, INFINITE); g_dwProtectedData = 5; dwTemp = g_dwProtectedData; } __finally { // 3. Allow others to use protected data. ReleaseSemaphore(g_hSem, 1, NULL); } // 4. Continue processing. return(dwTemp);}
But… • DWORD Funcenstein1() { DWORD dwTemp; // 1. Do any processing here. __try { // 2. Request permission to access // protected data, and then use it. WaitForSingleObject(g_hSem, INFINITE); g_dwProtectedData = 5; dwTemp = g_dwProtectedData;// Return the new value. return(dwTemp); } __finally { // 3. Allow others to use protected data. ReleaseSemaphore(g_hSem, 1, NULL); } // 4. Continue processing.dwTemp = 9; return(dwTemp);}
The Problem • Jumping from the __try to the __finally can be expensive in terms of CPU time • Called a local unwind
A Better Example • DWORD Funcfurter1() { DWORD dwTemp; // 1. Do any processing here. __try { // 2. Request permission to access // protected data, and then use it. WaitForSingleObject(g_hSem, INFINITE); dwTemp = Funcinator(g_dwProtectedData); } __finally { // 3. Allow others to use protected data. ReleaseSemaphore(g_hSem, 1, NULL); } // 4. Continue processing. return(dwTemp); } • What if Funcinator throws an access violation?
Quiz: What’s the output? • DWORD FuncaDoodleDoo() { DWORD dwTemp = 0; while (dwTemp < 10) { _ _try { if (dwTemp == 2) continue; if (dwTemp == 3) break; } _ _finally { dwTemp++; } dwTemp++; } dwTemp += 10; return(dwTemp); }
And Another… • DWORD Funcenstein4() { DWORD dwTemp; // 1. Do any processing here. _ _try { // 2. Request permission to access // protected data, and then use it. WaitForSingleObject(g_hSem, INFINITE); g_dwProtectedData = 5; dwTemp = g_dwProtectedData; // Return the new value. return(dwTemp); } _ _finally { // 3. Allow others to use protected data. ReleaseSemaphore(g_hSem, 1, NULL); return(103); } dwTemp = 9; return(dwTemp); }
Better Approach: __leave • Think of __leave as a normal exit that runs normally into __finally • This dramatically saves time and simplifies coding • However, it does complicate your code a little bit
But we’re still not done • Can see in the __finally block why we got there… • BOOL AbnormalTermination();