474 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
		
		
			
		
	
	
			474 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
|  | //////////////////////////////////////////////////////////////////////////////
 | ||
|  | //
 | ||
|  | //  Detours Test Program (trcapi.cpp of trcapi.dll)
 | ||
|  | //
 | ||
|  | //  Microsoft Research Detours Package
 | ||
|  | //
 | ||
|  | //  Copyright (c) Microsoft Corporation.  All rights reserved.
 | ||
|  | //
 | ||
|  | #undef WIN32_LEAN_AND_MEAN
 | ||
|  | #define _WIN32_WINNT        0x400
 | ||
|  | #define WIN32
 | ||
|  | #define NT
 | ||
|  | #define _WINSOCK_DEPRECATED_NO_WARNINGS
 | ||
|  | 
 | ||
|  | #define DBG_TRACE   0
 | ||
|  | 
 | ||
|  | #if _MSC_VER >= 1300
 | ||
|  | #include <winsock2.h>
 | ||
|  | #endif
 | ||
|  | #include <windows.h>
 | ||
|  | #include <stdio.h>
 | ||
|  | #pragma warning(push)
 | ||
|  | #if _MSC_VER > 1400
 | ||
|  | #pragma warning(disable:6102 6103) // /analyze warnings
 | ||
|  | #endif
 | ||
|  | #include <strsafe.h>
 | ||
|  | #pragma warning(pop)
 | ||
|  | #include "detours.h"
 | ||
|  | #include "syelog.h"
 | ||
|  | 
 | ||
|  | #if (_MSC_VER < 1299)
 | ||
|  | #define LONG_PTR    LONG
 | ||
|  | #define ULONG_PTR   ULONG
 | ||
|  | #define PLONG_PTR   PLONG
 | ||
|  | #define PULONG_PTR  PULONG
 | ||
|  | #define INT_PTR     INT
 | ||
|  | #define UINT_PTR    UINT
 | ||
|  | #define PINT_PTR    PINT
 | ||
|  | #define PUINT_PTR   PUINT
 | ||
|  | #define DWORD_PTR   DWORD
 | ||
|  | #define PDWORD_PTR  PDWORD
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | #pragma warning(disable:4996)   // We don't care about deprecated APIs.
 | ||
|  | 
 | ||
|  | //////////////////////////////////////////////////////////////////////////////
 | ||
|  | #pragma warning(disable:4127)   // Many of our asserts are constants.
 | ||
|  | 
 | ||
|  | #define ASSERT_ALWAYS(x)   \
 | ||
|  |     do {                                                        \ | ||
|  |     if (!(x)) {                                                 \ | ||
|  |             AssertMessage(#x, __FILE__, __LINE__);              \ | ||
|  |             DebugBreak();                                       \ | ||
|  |     }                                                           \ | ||
|  |     } while (0) | ||
|  | 
 | ||
|  | #ifndef NDEBUG
 | ||
|  | #define ASSERT(x)           ASSERT_ALWAYS(x)
 | ||
|  | #else
 | ||
|  | #define ASSERT(x)
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | #define UNUSED(c)    (c) = (c)
 | ||
|  | 
 | ||
|  | //////////////////////////////////////////////////////////////////////////////
 | ||
|  | static HMODULE s_hInst = NULL; | ||
|  | static WCHAR s_wzDllPath[MAX_PATH]; | ||
|  | static CHAR s_szDllPath[MAX_PATH]; | ||
|  | 
 | ||
|  | BOOL ProcessEnumerate(); | ||
|  | BOOL InstanceEnumerate(HINSTANCE hInst); | ||
|  | 
 | ||
|  | VOID _PrintEnter(const CHAR *psz, ...); | ||
|  | VOID _PrintExit(const CHAR *psz, ...); | ||
|  | VOID _Print(const CHAR *psz, ...); | ||
|  | VOID _VPrint(PCSTR msg, va_list args, PCHAR pszBuf, LONG cbBuf); | ||
|  | 
 | ||
|  | VOID AssertMessage(CONST PCHAR pszMsg, CONST PCHAR pszFile, ULONG nLine); | ||
|  | 
 | ||
|  | //////////////////////////////////////////////////////////////////////////////
 | ||
|  | //
 | ||
|  | // Trampolines
 | ||
|  | //
 | ||
|  | extern "C" { | ||
|  |     //  Trampolines for SYELOG library.
 | ||
|  |     //
 | ||
|  |     extern HANDLE (WINAPI *Real_CreateFileW)(LPCWSTR a0, DWORD a1, DWORD a2, | ||
|  |                                              LPSECURITY_ATTRIBUTES a3, DWORD a4, DWORD a5, | ||
|  |                                              HANDLE a6); | ||
|  |     extern BOOL (WINAPI *Real_WriteFile)(HANDLE hFile, | ||
|  |                                          LPCVOID lpBuffer, | ||
|  |                                          DWORD nNumberOfBytesToWrite, | ||
|  |                                          LPDWORD lpNumberOfBytesWritten, | ||
|  |                                          LPOVERLAPPED lpOverlapped); | ||
|  |     extern BOOL (WINAPI *Real_FlushFileBuffers)(HANDLE hFile); | ||
|  |     extern BOOL (WINAPI *Real_CloseHandle)(HANDLE hObject); | ||
|  |     extern BOOL (WINAPI *Real_WaitNamedPipeW)(LPCWSTR lpNamedPipeName, DWORD nTimeOut); | ||
|  |     extern BOOL (WINAPI *Real_SetNamedPipeHandleState)(HANDLE hNamedPipe, | ||
|  |                                                        LPDWORD lpMode, | ||
|  |                                                        LPDWORD lpMaxCollectionCount, | ||
|  |                                                        LPDWORD lpCollectDataTimeout); | ||
|  |     extern DWORD (WINAPI *Real_GetCurrentProcessId)(VOID); | ||
|  |     extern VOID (WINAPI *Real_GetSystemTimeAsFileTime)(LPFILETIME lpSystemTimeAsFileTime); | ||
|  | 
 | ||
|  |     VOID ( WINAPI * Real_InitializeCriticalSection)(LPCRITICAL_SECTION lpSection) | ||
|  |         = InitializeCriticalSection; | ||
|  |     VOID ( WINAPI * Real_EnterCriticalSection)(LPCRITICAL_SECTION lpSection) | ||
|  |         = EnterCriticalSection; | ||
|  |     VOID ( WINAPI * Real_LeaveCriticalSection)(LPCRITICAL_SECTION lpSection) | ||
|  |         = LeaveCriticalSection; | ||
|  | } | ||
|  | 
 | ||
|  | #include "_win32.cpp"
 | ||
|  | 
 | ||
|  | ////////////////////////////////////////////////////////////// Logging System.
 | ||
|  | //
 | ||
|  | static BOOL s_bLog = FALSE; | ||
|  | static LONG s_nTlsIndent = -1; | ||
|  | static LONG s_nTlsThread = -1; | ||
|  | static LONG s_nThreadCnt = 0; | ||
|  | 
 | ||
|  | VOID _PrintEnter(const CHAR *psz, ...) | ||
|  | { | ||
|  |     DWORD dwErr = GetLastError(); | ||
|  | 
 | ||
|  |     LONG nIndent = 0; | ||
|  |     LONG nThread = 0; | ||
|  |     if (s_nTlsIndent >= 0) { | ||
|  |         nIndent = (LONG)(LONG_PTR)TlsGetValue(s_nTlsIndent); | ||
|  |         TlsSetValue(s_nTlsIndent, (PVOID)(LONG_PTR)(nIndent + 1)); | ||
|  |     } | ||
|  |     if (s_nTlsThread >= 0) { | ||
|  |         nThread = (LONG)(LONG_PTR)TlsGetValue(s_nTlsThread); | ||
|  |     } | ||
|  | 
 | ||
|  |     if (s_bLog && psz) { | ||
|  |         CHAR szBuf[1024]; | ||
|  |         PCHAR pszBuf = szBuf; | ||
|  |         PCHAR pszEnd = szBuf + ARRAYSIZE(szBuf) - 1; | ||
|  |         LONG nLen = (nIndent > 0) ? (nIndent < 35 ? nIndent * 2 : 70) : 0; | ||
|  |         *pszBuf++ = (CHAR)('0' + ((nThread / 100) % 10)); | ||
|  |         *pszBuf++ = (CHAR)('0' + ((nThread / 10) % 10)); | ||
|  |         *pszBuf++ = (CHAR)('0' + ((nThread / 1) % 10)); | ||
|  |         *pszBuf++ = ' '; | ||
|  |         while (nLen-- > 0) { | ||
|  |             *pszBuf++ = ' '; | ||
|  |         } | ||
|  |         *pszBuf++ = '+'; | ||
|  |         *pszBuf = '\0'; | ||
|  | 
 | ||
|  |         va_list  args; | ||
|  |         va_start(args, psz); | ||
|  | 
 | ||
|  |         while ((*pszBuf++ = *psz++) != 0 && pszBuf < pszEnd) { | ||
|  |             // Copy characters.
 | ||
|  |         } | ||
|  |         *pszEnd = '\0'; | ||
|  |         SyelogV(SYELOG_SEVERITY_INFORMATION, szBuf, args); | ||
|  | 
 | ||
|  |         va_end(args); | ||
|  |     } | ||
|  |     SetLastError(dwErr); | ||
|  | } | ||
|  | 
 | ||
|  | VOID _PrintExit(const CHAR *psz, ...) | ||
|  | { | ||
|  |     DWORD dwErr = GetLastError(); | ||
|  | 
 | ||
|  |     LONG nIndent = 0; | ||
|  |     LONG nThread = 0; | ||
|  |     if (s_nTlsIndent >= 0) { | ||
|  |         nIndent = (LONG)(LONG_PTR)TlsGetValue(s_nTlsIndent) - 1; | ||
|  |         ASSERT_ALWAYS(nIndent >= 0); | ||
|  |         TlsSetValue(s_nTlsIndent, (PVOID)(LONG_PTR)nIndent); | ||
|  |     } | ||
|  |     if (s_nTlsThread >= 0) { | ||
|  |         nThread = (LONG)(LONG_PTR)TlsGetValue(s_nTlsThread); | ||
|  |     } | ||
|  | 
 | ||
|  |     if (s_bLog && psz) { | ||
|  |         CHAR szBuf[1024]; | ||
|  |         PCHAR pszEnd = szBuf + ARRAYSIZE(szBuf) - 1; | ||
|  |         PCHAR pszBuf = szBuf; | ||
|  |         LONG nLen = (nIndent > 0) ? (nIndent < 35 ? nIndent * 2 : 70) : 0; | ||
|  |         *pszBuf++ = (CHAR)('0' + ((nThread / 100) % 10)); | ||
|  |         *pszBuf++ = (CHAR)('0' + ((nThread / 10) % 10)); | ||
|  |         *pszBuf++ = (CHAR)('0' + ((nThread / 1) % 10)); | ||
|  |         *pszBuf++ = ' '; | ||
|  |         while (nLen-- > 0) { | ||
|  |             *pszBuf++ = ' '; | ||
|  |         } | ||
|  |         *pszBuf++ = '-'; | ||
|  |         *pszBuf = '\0'; | ||
|  | 
 | ||
|  |         va_list  args; | ||
|  |         va_start(args, psz); | ||
|  | 
 | ||
|  |         while ((*pszBuf++ = *psz++) != 0 && pszBuf < pszEnd) { | ||
|  |             // Copy characters.
 | ||
|  |         } | ||
|  |         *pszEnd = '\0'; | ||
|  |         SyelogV(SYELOG_SEVERITY_INFORMATION, szBuf, args); | ||
|  | 
 | ||
|  |         va_end(args); | ||
|  |     } | ||
|  |     SetLastError(dwErr); | ||
|  | } | ||
|  | 
 | ||
|  | VOID _Print(const CHAR *psz, ...) | ||
|  | { | ||
|  |     DWORD dwErr = GetLastError(); | ||
|  | 
 | ||
|  |     LONG nIndent = 0; | ||
|  |     LONG nThread = 0; | ||
|  |     if (s_nTlsIndent >= 0) { | ||
|  |         nIndent = (LONG)(LONG_PTR)TlsGetValue(s_nTlsIndent); | ||
|  |     } | ||
|  |     if (s_nTlsThread >= 0) { | ||
|  |         nThread = (LONG)(LONG_PTR)TlsGetValue(s_nTlsThread); | ||
|  |     } | ||
|  | 
 | ||
|  |     if (s_bLog && psz) { | ||
|  |         CHAR szBuf[1024]; | ||
|  |         PCHAR pszEnd = szBuf + ARRAYSIZE(szBuf) - 1; | ||
|  |         PCHAR pszBuf = szBuf; | ||
|  |         LONG nLen = (nIndent > 0) ? (nIndent < 35 ? nIndent * 2 : 70) : 0; | ||
|  |         *pszBuf++ = (CHAR)('0' + ((nThread / 100) % 10)); | ||
|  |         *pszBuf++ = (CHAR)('0' + ((nThread / 10) % 10)); | ||
|  |         *pszBuf++ = (CHAR)('0' + ((nThread / 1) % 10)); | ||
|  |         *pszBuf++ = ' '; | ||
|  |         while (nLen-- > 0) { | ||
|  |             *pszBuf++ = ' '; | ||
|  |         } | ||
|  |         *pszBuf = '\0'; | ||
|  | 
 | ||
|  |         va_list  args; | ||
|  |         va_start(args, psz); | ||
|  | 
 | ||
|  |         while ((*pszBuf++ = *psz++) != 0 && pszBuf < pszEnd) { | ||
|  |             // Copy characters.
 | ||
|  |         } | ||
|  |         *pszEnd = '\0'; | ||
|  |         SyelogV(SYELOG_SEVERITY_INFORMATION, szBuf, args); | ||
|  | 
 | ||
|  |         va_end(args); | ||
|  |     } | ||
|  |     SetLastError(dwErr); | ||
|  | } | ||
|  | 
 | ||
|  | VOID AssertMessage(CONST PCHAR pszMsg, CONST PCHAR pszFile, ULONG nLine) | ||
|  | { | ||
|  |     Syelog(SYELOG_SEVERITY_FATAL, | ||
|  |            "ASSERT(%s) failed in %s, line %d.\n", pszMsg, pszFile, nLine); | ||
|  | } | ||
|  | 
 | ||
|  | //////////////////////////////////////////////////////////////////////////////
 | ||
|  | //
 | ||
|  | PIMAGE_NT_HEADERS NtHeadersForInstance(HINSTANCE hInst) | ||
|  | { | ||
|  |     PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hInst; | ||
|  |     __try { | ||
|  |         if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { | ||
|  |             SetLastError(ERROR_BAD_EXE_FORMAT); | ||
|  |             return NULL; | ||
|  |         } | ||
|  | 
 | ||
|  |         PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader + | ||
|  |                                                           pDosHeader->e_lfanew); | ||
|  |         if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) { | ||
|  |             SetLastError(ERROR_INVALID_EXE_SIGNATURE); | ||
|  |             return NULL; | ||
|  |         } | ||
|  |         if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) { | ||
|  |             SetLastError(ERROR_EXE_MARKED_INVALID); | ||
|  |             return NULL; | ||
|  |         } | ||
|  |         return pNtHeader; | ||
|  |     } __except(EXCEPTION_EXECUTE_HANDLER) { | ||
|  |     } | ||
|  |     SetLastError(ERROR_EXE_MARKED_INVALID); | ||
|  | 
 | ||
|  |     return NULL; | ||
|  | } | ||
|  | 
 | ||
|  | BOOL InstanceEnumerate(HINSTANCE hInst) | ||
|  | { | ||
|  |     WCHAR wzDllName[MAX_PATH]; | ||
|  | 
 | ||
|  |     PIMAGE_NT_HEADERS pinh = NtHeadersForInstance(hInst); | ||
|  |     if (pinh && Real_GetModuleFileNameW(hInst, wzDllName, ARRAYSIZE(wzDllName))) { | ||
|  |         Syelog(SYELOG_SEVERITY_INFORMATION, "### %p: %ls\n", hInst, wzDllName); | ||
|  |         return TRUE; | ||
|  |     } | ||
|  |     return FALSE; | ||
|  | } | ||
|  | 
 | ||
|  | BOOL ProcessEnumerate() | ||
|  | { | ||
|  |     Syelog(SYELOG_SEVERITY_INFORMATION, | ||
|  |            "######################################################### Binaries\n"); | ||
|  | 
 | ||
|  |     PBYTE pbNext; | ||
|  |     for (PBYTE pbRegion = (PBYTE)0x10000;; pbRegion = pbNext) { | ||
|  |         MEMORY_BASIC_INFORMATION mbi; | ||
|  |         ZeroMemory(&mbi, sizeof(mbi)); | ||
|  | 
 | ||
|  |         if (VirtualQuery((PVOID)pbRegion, &mbi, sizeof(mbi)) <= 0) { | ||
|  |             break; | ||
|  |         } | ||
|  |         pbNext = (PBYTE)mbi.BaseAddress + mbi.RegionSize; | ||
|  | 
 | ||
|  |         // Skip free regions, reserver regions, and guard pages.
 | ||
|  |         //
 | ||
|  |         if (mbi.State == MEM_FREE || mbi.State == MEM_RESERVE) { | ||
|  |             continue; | ||
|  |         } | ||
|  |         if (mbi.Protect & PAGE_GUARD || mbi.Protect & PAGE_NOCACHE) { | ||
|  |             continue; | ||
|  |         } | ||
|  |         if (mbi.Protect == PAGE_NOACCESS) { | ||
|  |             continue; | ||
|  |         } | ||
|  | 
 | ||
|  |         // Skip over regions from the same allocation...
 | ||
|  |         { | ||
|  |             MEMORY_BASIC_INFORMATION mbiStep; | ||
|  | 
 | ||
|  |             while (VirtualQuery((PVOID)pbNext, &mbiStep, sizeof(mbiStep)) > 0) { | ||
|  |                 if ((PBYTE)mbiStep.AllocationBase != pbRegion) { | ||
|  |                     break; | ||
|  |                 } | ||
|  |                 pbNext = (PBYTE)mbiStep.BaseAddress + mbiStep.RegionSize; | ||
|  |                 mbi.Protect |= mbiStep.Protect; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         WCHAR wzDllName[MAX_PATH]; | ||
|  |         PIMAGE_NT_HEADERS pinh = NtHeadersForInstance((HINSTANCE)pbRegion); | ||
|  | 
 | ||
|  |         if (pinh && | ||
|  |             Real_GetModuleFileNameW((HINSTANCE)pbRegion,wzDllName,ARRAYSIZE(wzDllName))) { | ||
|  | 
 | ||
|  |             Syelog(SYELOG_SEVERITY_INFORMATION, | ||
|  |                    "### %p..%p: %ls\n", pbRegion, pbNext, wzDllName); | ||
|  |         } | ||
|  |         else { | ||
|  |             Syelog(SYELOG_SEVERITY_INFORMATION, | ||
|  |                    "### %p..%p: State=%04x, Protect=%08x\n", | ||
|  |                    pbRegion, pbNext, mbi.State, mbi.Protect); | ||
|  |         } | ||
|  |     } | ||
|  |     Syelog(SYELOG_SEVERITY_INFORMATION, "###\n"); | ||
|  | 
 | ||
|  |     LPVOID lpvEnv = Real_GetEnvironmentStrings(); | ||
|  |     Syelog(SYELOG_SEVERITY_INFORMATION, "### Env= %08x [%08x %08x]\n", | ||
|  |            lpvEnv, ((PVOID*)lpvEnv)[0], ((PVOID*)lpvEnv)[1]); | ||
|  | 
 | ||
|  |     return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | //////////////////////////////////////////////////////////////////////////////
 | ||
|  | //
 | ||
|  | // DLL module information
 | ||
|  | //
 | ||
|  | BOOL ThreadAttach(HMODULE hDll) | ||
|  | { | ||
|  |     (void)hDll; | ||
|  | 
 | ||
|  |     if (s_nTlsIndent >= 0) { | ||
|  |         TlsSetValue(s_nTlsIndent, (PVOID)0); | ||
|  |     } | ||
|  |     if (s_nTlsThread >= 0) { | ||
|  |         LONG nThread = InterlockedIncrement(&s_nThreadCnt); | ||
|  |         TlsSetValue(s_nTlsThread, (PVOID)(LONG_PTR)nThread); | ||
|  |     } | ||
|  |     return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | BOOL ThreadDetach(HMODULE hDll) | ||
|  | { | ||
|  |     (void)hDll; | ||
|  | 
 | ||
|  |     if (s_nTlsIndent >= 0) { | ||
|  |         TlsSetValue(s_nTlsIndent, (PVOID)0); | ||
|  |     } | ||
|  |     if (s_nTlsThread >= 0) { | ||
|  |         TlsSetValue(s_nTlsThread, (PVOID)0); | ||
|  |     } | ||
|  |     return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | BOOL ProcessAttach(HMODULE hDll) | ||
|  | { | ||
|  |     s_bLog = FALSE; | ||
|  |     s_nTlsIndent = TlsAlloc(); | ||
|  |     s_nTlsThread = TlsAlloc(); | ||
|  |     ThreadAttach(hDll); | ||
|  | 
 | ||
|  |     WCHAR wzExeName[MAX_PATH]; | ||
|  | 
 | ||
|  |     s_hInst = hDll; | ||
|  |     Real_GetModuleFileNameW(hDll, s_wzDllPath, ARRAYSIZE(s_wzDllPath)); | ||
|  |     Real_GetModuleFileNameW(NULL, wzExeName, ARRAYSIZE(wzExeName)); | ||
|  |     StringCchPrintfA(s_szDllPath, ARRAYSIZE(s_szDllPath), "%ls", s_wzDllPath); | ||
|  | 
 | ||
|  |     SyelogOpen("trcapi" DETOURS_STRINGIFY(DETOURS_BITS), SYELOG_FACILITY_APPLICATION); | ||
|  |     ProcessEnumerate(); | ||
|  | 
 | ||
|  |     LONG error = AttachDetours(); | ||
|  |     if (error != NO_ERROR) { | ||
|  |         Syelog(SYELOG_SEVERITY_FATAL, "### Error attaching detours: %d\n", error); | ||
|  |     } | ||
|  | 
 | ||
|  |     s_bLog = TRUE; | ||
|  |     return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | BOOL ProcessDetach(HMODULE hDll) | ||
|  | { | ||
|  |     ThreadDetach(hDll); | ||
|  |     s_bLog = FALSE; | ||
|  | 
 | ||
|  |     LONG error = DetachDetours(); | ||
|  |     if (error != NO_ERROR) { | ||
|  |         Syelog(SYELOG_SEVERITY_FATAL, "### Error detaching detours: %d\n", error); | ||
|  |     } | ||
|  | 
 | ||
|  |     Syelog(SYELOG_SEVERITY_NOTICE, "### Closing.\n"); | ||
|  |     SyelogClose(FALSE); | ||
|  | 
 | ||
|  |     if (s_nTlsIndent >= 0) { | ||
|  |         TlsFree(s_nTlsIndent); | ||
|  |     } | ||
|  |     if (s_nTlsThread >= 0) { | ||
|  |         TlsFree(s_nTlsThread); | ||
|  |     } | ||
|  |     return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD dwReason, PVOID lpReserved) | ||
|  | { | ||
|  |     (void)hModule; | ||
|  |     (void)lpReserved; | ||
|  |     BOOL ret; | ||
|  | 
 | ||
|  |     if (DetourIsHelperProcess()) { | ||
|  |         return TRUE; | ||
|  |     } | ||
|  | 
 | ||
|  |     switch (dwReason) { | ||
|  |       case DLL_PROCESS_ATTACH: | ||
|  |         DetourRestoreAfterWith(); | ||
|  |         OutputDebugStringA("trcapi" DETOURS_STRINGIFY(DETOURS_BITS) ".dll:" | ||
|  |                            " DllMain DLL_PROCESS_ATTACH\n"); | ||
|  |         return ProcessAttach(hModule); | ||
|  |       case DLL_PROCESS_DETACH: | ||
|  |         ret = ProcessDetach(hModule); | ||
|  |         OutputDebugStringA("trcapi" DETOURS_STRINGIFY(DETOURS_BITS) ".dll:" | ||
|  |                            " DllMain DLL_PROCESS_DETACH\n"); | ||
|  |         return ret; | ||
|  |       case DLL_THREAD_ATTACH: | ||
|  |         OutputDebugStringA("trcapi" DETOURS_STRINGIFY(DETOURS_BITS) ".dll:" | ||
|  |                            " DllMain DLL_THREAD_ATTACH\n"); | ||
|  |         return ThreadAttach(hModule); | ||
|  |       case DLL_THREAD_DETACH: | ||
|  |         OutputDebugStringA("trcapi" DETOURS_STRINGIFY(DETOURS_BITS) ".dll:" | ||
|  |                            " DllMain DLL_THREAD_DETACH\n"); | ||
|  |         return ThreadDetach(hModule); | ||
|  |     } | ||
|  |     return TRUE; | ||
|  | } | ||
|  | //
 | ||
|  | ///////////////////////////////////////////////////////////////// End of File.
 |