모든 쓰레드은 처리되지 않은 예외를위해 UnhandledExceptionFilter 를 가지고 있다.
아래는 쓰레드 시작 원형이다.

1
2
3
4
5
6
7
8
9
VOID RtlUserThreadStart(PTHREAD_START_ROUTE pfnStartAddr, PVOID pvParam) {
    __try {
        ExitThread( (pfnStartAddr)(pvParam) );
    }
    __except(UnhandledExceptionFilter(GetExceptionInformation())) {
        ExitProcess(GetExceptionCode());
    }
    // 주의:  절대 수행되지 않음
}

또한 SetUnhandledExceptionFilter() 함수를 사용하여 처리되지 않은 예외가 발생하였을때 사용자 코드가 처리되도록 할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
LONG WINAPI ExceptionFilter(EXCEPTION_POINTERS* excptPtr)
{
    // ...
 
    return EXCEPTION_EXECUTE_HANDLER;
}
int _tmain(int argc, _TCHAR* argv[])
{
    ::SetUnhandledExceptionFilter(ExceptionFilter);
    // ...
 
    return 0;
}

하지만 이 함수는 다른 쓰레드에 의해 선점 당할수 있다는 점을 명심해야한다.
만약 사용중인 써드파티 라이브러리에서 위의 함수를 사용하였다면, 유저가 등록한 예외처리부는 덮혀 씌어질 것이고, 이는 사용자에게 엄청난 혼란을 가져다 줄것이다. (그것도 쥐도새도 모르게)

이러한 경우를 겪은 많은 선배 프로그래머분들께서 우리에게 올바른 해법을 남겨주셨다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI MyDummySetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
{
    return NULL;
};
BOOL PreventSetUnhandledExceptionFilter()
{
    HMODULE hKernel32 = LoadLibrary(_T("kernel32.dll"));
    if (hKernel32 == NULL) return FALSE;
    void *pOrgEntry = GetProcAddress(hKernel32,
        "SetUnhandledExceptionFilter");
    if(pOrgEntry == NULL) return FALSE;
 
    DWORD dwOldProtect = 0;
    SIZE_T jmpSize = 5;
#ifdef _M_X64
    jmpSize = 13;
#endif
    BOOL bProt = VirtualProtect(pOrgEntry, jmpSize,
        PAGE_EXECUTE_READWRITE, &dwOldProtect);
    BYTE newJump[20];
    void *pNewFunc = &MyDummySetUnhandledExceptionFilter;
#ifdef _M_IX86
    DWORD dwOrgEntryAddr = (DWORD) pOrgEntry;
    dwOrgEntryAddr += jmpSize; // add 5 for 5 op-codes for jmp rel32
    DWORD dwNewEntryAddr = (DWORD) pNewFunc;
    DWORD dwRelativeAddr = dwNewEntryAddr - dwOrgEntryAddr;
    // JMP rel32: Jump near, relative, displacement relative to next instruction.
    newJump[0] = 0xE9;  // JMP rel32
    memcpy(&newJump[1], &dwRelativeAddr, sizeof(pNewFunc));
#elif _M_X64
    // We must use R10 or R11, because these are "scratch" registers
    // which need not to be preserved access function calls
    // For more info see: Register Usage for x64 64-Bit
    // Thanks to Matthew Smith!!!
    newJump[0] = 0x49;  // MOV R11, ...
    newJump[1] = 0xBB;  // ...
    memcpy(&newJump[2], &pNewFunc, sizeof (pNewFunc));
    //pCur += sizeof (ULONG_PTR);
    newJump[10] = 0x41;  // JMP R11, ...
    newJump[11] = 0xFF;  // ...
    newJump[12] = 0xE3;  // ...
#endif
    SIZE_T bytesWritten;
    BOOL bRet = WriteProcessMemory(GetCurrentProcess(),
        pOrgEntry, newJump, jmpSize, &bytesWritten);
 
    if (bProt != FALSE)
    {
        DWORD dwBuf;
        VirtualProtect(pOrgEntry, jmpSize, dwOldProtect, &dwBuf);
    }
    return bRet;
}

'Dev.Write' 카테고리의 다른 글

(GLSL) cook-torrence sample  (0) 2011.04.18
Tutorial 10 - Shader  (0) 2011.04.16
Custom RTTI  (0) 2011.04.04
Deferred Shading  (0) 2011.03.28
Tutorial04  (0) 2011.03.27