570 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
		
		
			
		
	
	
			570 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
|  | //////////////////////////////////////////////////////////////////////////////
 | ||
|  | //
 | ||
|  | //  Detours Test Program (tracebld.cpp of tracebld.exe)
 | ||
|  | //
 | ||
|  | //  Microsoft Research Detours Package
 | ||
|  | //
 | ||
|  | //  Copyright (c) Microsoft Corporation.  All rights reserved.
 | ||
|  | //
 | ||
|  | #include <windows.h>
 | ||
|  | #include <stdio.h>
 | ||
|  | #include <stdlib.h>
 | ||
|  | #include <stddef.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 "tracebld.h"
 | ||
|  | 
 | ||
|  | #if (_MSC_VER < 1299)
 | ||
|  | typedef ULONG * PULONG_PTR; | ||
|  | typedef ULONG ULONG_PTR; | ||
|  | typedef LONG * PLONG_PTR; | ||
|  | typedef LONG LONG_PTR; | ||
|  | #endif
 | ||
|  | 
 | ||
|  | //////////////////////////////////////////////////////////////////////////////
 | ||
|  | #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)
 | ||
|  | 
 | ||
|  | //////////////////////////////////////////////////////////////////////////////
 | ||
|  | 
 | ||
|  | enum { | ||
|  |     CLIENT_AWAITING_PIPE_ACCEPT = 0x21, | ||
|  |     CLIENT_AWAITING_PIPE_DATA   = 0x22, | ||
|  | }; | ||
|  | 
 | ||
|  | typedef struct _CLIENT : OVERLAPPED | ||
|  | { | ||
|  |     HANDLE          hPipe; | ||
|  |     LONG            nClient; | ||
|  |     HANDLE          hFile; | ||
|  |     BOOL            fAwaitingAccept; | ||
|  |     PVOID           Zero; | ||
|  |     TBLOG_MESSAGE   Message; | ||
|  | 
 | ||
|  |     BOOL LogMessage(PTBLOG_MESSAGE pMessage, DWORD nBytes); | ||
|  |     BOOL LogMessageV(PCHAR pszMsg, ...); | ||
|  | } CLIENT, *PCLIENT; | ||
|  | 
 | ||
|  | //////////////////////////////////////////////////////////////////////////////
 | ||
|  | //
 | ||
|  | CHAR        s_szLogFile[MAX_PATH]; | ||
|  | CHAR        s_szPipe[MAX_PATH]; | ||
|  | LONG        s_nActiveClients = 0; | ||
|  | LONG        s_nTotalClients = 0; | ||
|  | LONGLONG    s_llStartTime; | ||
|  | BOOL        s_fVerbose = FALSE; | ||
|  | TBLOG_PAYLOAD s_Payload; | ||
|  | 
 | ||
|  | //////////////////////////////////////////////////////////////////////////////
 | ||
|  | //
 | ||
|  | VOID MyErrExit(PCSTR pszMsg) | ||
|  | { | ||
|  |     DWORD error = GetLastError(); | ||
|  | 
 | ||
|  |     fprintf(stderr, "TRACEBLD: Error %ld in %s.\n", error, pszMsg); | ||
|  |     fflush(stderr); | ||
|  |     exit(1); | ||
|  | } | ||
|  | 
 | ||
|  | //////////////////////////////////////////////////////////////////////////////
 | ||
|  | //
 | ||
|  | BOOL CLIENT::LogMessageV(PCHAR pszMsg, ...) | ||
|  | { | ||
|  |     DWORD cbWritten = 0; | ||
|  |     CHAR szBuf[1024]; | ||
|  |     PCHAR pcchEnd = szBuf + ARRAYSIZE(szBuf) - 2; | ||
|  |     PCHAR pcchCur = szBuf; | ||
|  |     HRESULT hr; | ||
|  | 
 | ||
|  |     va_list args; | ||
|  |     va_start(args, pszMsg); | ||
|  |     hr = StringCchVPrintfExA(pcchCur, pcchEnd - pcchCur, | ||
|  |                              &pcchCur, NULL, STRSAFE_NULL_ON_FAILURE, | ||
|  |                              pszMsg, args); | ||
|  |     va_end(args); | ||
|  |     if (FAILED(hr)) { | ||
|  |         goto cleanup; | ||
|  |     } | ||
|  | 
 | ||
|  |     hr = StringCchPrintfExA(pcchCur, szBuf + (ARRAYSIZE(szBuf)) - pcchCur, | ||
|  |                             &pcchCur, NULL, STRSAFE_NULL_ON_FAILURE, | ||
|  |                             "\n"); | ||
|  | 
 | ||
|  |   cleanup: | ||
|  |     WriteFile(hFile, szBuf, (DWORD)(pcchCur - szBuf), &cbWritten, NULL); | ||
|  |     return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | BOOL CLIENT::LogMessage(PTBLOG_MESSAGE pMessage, DWORD nBytes) | ||
|  | { | ||
|  |     // Sanity check the size of the message.
 | ||
|  |     //
 | ||
|  |     if (nBytes > pMessage->nBytes) { | ||
|  |         nBytes = pMessage->nBytes; | ||
|  |     } | ||
|  |     if (nBytes >= sizeof(*pMessage)) { | ||
|  |         nBytes = sizeof(*pMessage) - 1; | ||
|  |     } | ||
|  | 
 | ||
|  |     // Don't log message if there isn't and message text.
 | ||
|  |     //
 | ||
|  |     DWORD cbWrite = nBytes - offsetof(TBLOG_MESSAGE, szMessage); | ||
|  |     if (cbWrite <= 0 ) { | ||
|  |         return TRUE; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (s_fVerbose) { | ||
|  |         printf("[%s]", pMessage->szMessage); | ||
|  |     } | ||
|  | 
 | ||
|  |     DWORD cbWritten = 0; | ||
|  |     WriteFile(hFile, pMessage->szMessage, cbWrite, &cbWritten, NULL); | ||
|  |     return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | BOOL CloseConnection(PCLIENT pClient) | ||
|  | { | ||
|  |     InterlockedDecrement(&s_nActiveClients); | ||
|  |     if (pClient != NULL) { | ||
|  |         if (pClient->hPipe != INVALID_HANDLE_VALUE) { | ||
|  |             //FlushFileBuffers(pClient->hPipe);
 | ||
|  |             if (!DisconnectNamedPipe(pClient->hPipe)) { | ||
|  |                 DWORD error = GetLastError(); | ||
|  |                 pClient->LogMessageV("<!-- Error %d in DisconnectNamedPipe. -->\n", error); | ||
|  |             } | ||
|  |             CloseHandle(pClient->hPipe); | ||
|  |             pClient->hPipe = INVALID_HANDLE_VALUE; | ||
|  |         } | ||
|  |         if (pClient->hFile != INVALID_HANDLE_VALUE) { | ||
|  |             CloseHandle(pClient->hFile); | ||
|  |             pClient->hFile = INVALID_HANDLE_VALUE; | ||
|  |         } | ||
|  |         GlobalFree(pClient); | ||
|  |         pClient = NULL; | ||
|  |     } | ||
|  |     return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | // Creates a pipe instance and initiate an accept request.
 | ||
|  | //
 | ||
|  | PCLIENT CreatePipeConnection(HANDLE hCompletionPort, LONG nClient) | ||
|  | { | ||
|  |     HANDLE hPipe = CreateNamedPipeA(s_szPipe,                   // pipe name
 | ||
|  |                                     PIPE_ACCESS_INBOUND |       // read-only access
 | ||
|  |                                     FILE_FLAG_OVERLAPPED,       // overlapped mode
 | ||
|  |                                     PIPE_TYPE_MESSAGE |         // message-type pipe
 | ||
|  |                                     PIPE_READMODE_MESSAGE |     // message read mode
 | ||
|  |                                     PIPE_WAIT,                  // blocking mode
 | ||
|  |                                     PIPE_UNLIMITED_INSTANCES,   // unlimited instances
 | ||
|  |                                     0,                          // output buffer size
 | ||
|  |                                     0,                          // input buffer size
 | ||
|  |                                     20000,                      // client time-out
 | ||
|  |                                     NULL);                      // no security attributes
 | ||
|  |     if (hPipe == INVALID_HANDLE_VALUE) { | ||
|  |         MyErrExit("CreateNamedPipe"); | ||
|  |     } | ||
|  | 
 | ||
|  |     // Allocate the client data structure.
 | ||
|  |     //
 | ||
|  |     PCLIENT pClient = (PCLIENT) GlobalAlloc(GPTR, sizeof(CLIENT)); | ||
|  |     if (pClient == NULL) { | ||
|  |         MyErrExit("GlobalAlloc pClient"); | ||
|  |     } | ||
|  | 
 | ||
|  |     CHAR szLogFile[MAX_PATH]; | ||
|  |     StringCchPrintfA(szLogFile, ARRAYSIZE(szLogFile), "%s.%08d.xml", s_szLogFile, nClient); | ||
|  | 
 | ||
|  |     ZeroMemory(pClient, sizeof(*pClient)); | ||
|  |     pClient->hPipe = hPipe; | ||
|  |     pClient->nClient = nClient; | ||
|  |     pClient->fAwaitingAccept = TRUE; | ||
|  |     pClient->hFile = CreateFileA(szLogFile, | ||
|  |                                  GENERIC_WRITE, | ||
|  |                                  FILE_SHARE_READ, | ||
|  |                                  NULL, | ||
|  |                                  CREATE_ALWAYS, | ||
|  |                                  FILE_ATTRIBUTE_NORMAL | | ||
|  |                                  FILE_FLAG_SEQUENTIAL_SCAN, | ||
|  |                                  NULL); | ||
|  |     if (pClient->hFile == INVALID_HANDLE_VALUE) { | ||
|  |         fprintf(stderr, "TRACEBLD: Error opening output file: %s: %ld\n\n", | ||
|  |                 szLogFile, GetLastError()); | ||
|  |         fflush(stderr); | ||
|  |         MyErrExit("CreateFile"); | ||
|  |     } | ||
|  | 
 | ||
|  |     // Associate file with our complietion port.
 | ||
|  |     //
 | ||
|  |     if (!CreateIoCompletionPort(pClient->hPipe, hCompletionPort, (ULONG_PTR)pClient, 0)) { | ||
|  |         MyErrExit("CreateIoComplietionPort pClient"); | ||
|  |     } | ||
|  | 
 | ||
|  |     if (!ConnectNamedPipe(hPipe, pClient)) { | ||
|  |         DWORD error = GetLastError(); | ||
|  | 
 | ||
|  |         if (error == ERROR_IO_PENDING) { | ||
|  |             return NULL; | ||
|  |         } | ||
|  |         if (error == ERROR_PIPE_CONNECTED) { | ||
|  | #if 0
 | ||
|  |             pClient->LogMessageV("<!-- ConnectNamedPipe client already connected. -->"); | ||
|  | #endif
 | ||
|  |             pClient->fAwaitingAccept = FALSE; | ||
|  |         } | ||
|  |         else if (error != ERROR_IO_PENDING && | ||
|  |                  error != ERROR_PIPE_LISTENING) { | ||
|  | 
 | ||
|  |             MyErrExit("ConnectNamedPipe"); | ||
|  |         } | ||
|  |     } | ||
|  |     else { | ||
|  |         fprintf(stderr, "*** ConnectNamedPipe accepted immediately.\n"); | ||
|  | #if 0
 | ||
|  |         pClient->LogMessageV("<!-- ConnectNamedPipe accepted immediately. -->"); | ||
|  | #endif
 | ||
|  |         pClient->fAwaitingAccept = FALSE; | ||
|  |     } | ||
|  |     return pClient; | ||
|  | } | ||
|  | 
 | ||
|  | BOOL DoRead(PCLIENT pClient) | ||
|  | { | ||
|  |     SetLastError(NO_ERROR); | ||
|  |     DWORD nBytes = 0; | ||
|  |     BOOL b = ReadFile(pClient->hPipe, &pClient->Message, sizeof(pClient->Message), | ||
|  |                       &nBytes, pClient); | ||
|  | 
 | ||
|  |     DWORD error = GetLastError(); | ||
|  | 
 | ||
|  |     if (b && error == NO_ERROR) { | ||
|  |         return TRUE; | ||
|  |     } | ||
|  |     if (error == ERROR_BROKEN_PIPE) { | ||
|  |         pClient->LogMessageV("<!-- **** ReadFile 002 *** ERROR_BROKEN_PIPE [%d] -->\n", nBytes); | ||
|  |         CloseConnection(pClient); | ||
|  |         return TRUE; | ||
|  |     } | ||
|  |     else if (error == ERROR_INVALID_HANDLE) { | ||
|  |         // ?
 | ||
|  |         pClient->LogMessageV("<!-- **** ReadFile 002 *** ERROR_INVALID_HANDLE -->\n"); | ||
|  |         // I have no idea why this happens.  Our remedy is to drop the connection.
 | ||
|  |         return TRUE; | ||
|  |     } | ||
|  |     else if (error != ERROR_IO_PENDING) { | ||
|  |         if (b) { | ||
|  |             pClient->LogMessageV("<!-- **** ReadFile 002 succeeded: %d -->\n", error); | ||
|  |         } | ||
|  |         else { | ||
|  |             pClient->LogMessageV("<!-- **** ReadFile 002 failed: %d -->\n", error); | ||
|  |         } | ||
|  |         CloseConnection(pClient); | ||
|  |     } | ||
|  |     return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | DWORD WINAPI WorkerThread(LPVOID pvVoid) | ||
|  | { | ||
|  |     PCLIENT pClient; | ||
|  |     BOOL b; | ||
|  |     LPOVERLAPPED lpo; | ||
|  |     DWORD nBytes; | ||
|  |     HANDLE hCompletionPort = (HANDLE)pvVoid; | ||
|  | 
 | ||
|  |     for (BOOL fKeepLooping = TRUE; fKeepLooping;) { | ||
|  |         pClient = NULL; | ||
|  |         lpo = NULL; | ||
|  |         nBytes = 0; | ||
|  |         b = GetQueuedCompletionStatus(hCompletionPort, | ||
|  |                                       &nBytes, (PULONG_PTR)&pClient, &lpo, INFINITE); | ||
|  | 
 | ||
|  |         if (!b) { | ||
|  |             if (pClient) { | ||
|  |                 if (GetLastError() == ERROR_BROKEN_PIPE) { | ||
|  |                     pClient->LogMessageV("<!-- Client closed pipe. -->"); | ||
|  |                 } | ||
|  |                 else { | ||
|  |                     pClient->LogMessageV("<!-- *** GetQueuedCompletionStatus failed %d -->", | ||
|  |                                          GetLastError()); | ||
|  |                 } | ||
|  |                 CloseConnection(pClient); | ||
|  |             } | ||
|  |             continue; | ||
|  |         } | ||
|  | 
 | ||
|  |         if (pClient->fAwaitingAccept) { | ||
|  |             BOOL fAgain = TRUE; | ||
|  |             while (fAgain) { | ||
|  |                 LONG nClient = InterlockedIncrement(&s_nTotalClients); | ||
|  |                 InterlockedIncrement(&s_nActiveClients); | ||
|  |                 pClient->fAwaitingAccept = FALSE; | ||
|  | 
 | ||
|  |                 PCLIENT pNew = CreatePipeConnection(hCompletionPort, nClient); | ||
|  | 
 | ||
|  |                 fAgain = FALSE; | ||
|  |                 if (pNew != NULL) { | ||
|  |                     fAgain = !pNew->fAwaitingAccept; | ||
|  |                     DoRead(pNew); | ||
|  |                 } | ||
|  |             } | ||
|  |         } | ||
|  |         else { | ||
|  |             if (nBytes <= offsetof(TBLOG_MESSAGE, szMessage)) { | ||
|  |                 pClient->LogMessageV("</t:Process>\n"); | ||
|  |                 CloseConnection(pClient); | ||
|  |                 continue; | ||
|  |             } | ||
|  |             pClient->LogMessage(&pClient->Message, nBytes); | ||
|  |         } | ||
|  | 
 | ||
|  |         DoRead(pClient); | ||
|  |     } | ||
|  |     return 0; | ||
|  | } | ||
|  | 
 | ||
|  | BOOL CreateWorkers(HANDLE hCompletionPort) | ||
|  | { | ||
|  |     DWORD dwThread; | ||
|  |     HANDLE hThread; | ||
|  |     DWORD i; | ||
|  |     SYSTEM_INFO SystemInfo; | ||
|  | 
 | ||
|  |     GetSystemInfo(&SystemInfo); | ||
|  | 
 | ||
|  |     for (i = 0; i < 1; i++) { | ||
|  |         hThread = CreateThread(NULL, 0, WorkerThread, hCompletionPort, 0, &dwThread); | ||
|  |         if (!hThread) { | ||
|  |             MyErrExit("CreateThread WorkerThread"); | ||
|  |             // Unreachable: return FALSE;
 | ||
|  |         } | ||
|  |         CloseHandle(hThread); | ||
|  |     } | ||
|  |     return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | DWORD CopyEnvironment(PWCHAR pwzzOut, PCWSTR pwzzIn) | ||
|  | { | ||
|  |     PCWSTR pwzzBeg = pwzzOut; | ||
|  |     while (*pwzzIn) { | ||
|  |         while (*pwzzIn) { | ||
|  |             *pwzzOut++ = *pwzzIn++; | ||
|  |         } | ||
|  |         *pwzzOut++ = *pwzzIn++;   // Copy zero.
 | ||
|  |     } | ||
|  |     *pwzzOut++ = '\0';    // Add last zero.
 | ||
|  | 
 | ||
|  |     return (DWORD)(pwzzOut - pwzzBeg); | ||
|  | } | ||
|  | 
 | ||
|  | //////////////////////////////////////////////////////////////////////////////
 | ||
|  | //
 | ||
|  | DWORD main(int argc, char **argv) | ||
|  | { | ||
|  |     HANDLE hCompletionPort; | ||
|  |     BOOL fNeedHelp = FALSE; | ||
|  |     WCHAR wzzDrop[1024] = L"build\0nmake\0"; | ||
|  | 
 | ||
|  |     GetSystemTimeAsFileTime((FILETIME *)&s_llStartTime); | ||
|  |     StringCchPrintfA(s_szPipe, ARRAYSIZE(s_szPipe), "%s.%d", TBLOG_PIPE_NAME, GetCurrentProcessId()); | ||
|  | 
 | ||
|  |     int arg = 1; | ||
|  |     for (; arg < argc && (argv[arg][0] == '-' || argv[arg][0] == '/'); arg++) { | ||
|  |         CHAR *argn = argv[arg] + 1; | ||
|  |         CHAR *argp = argn; | ||
|  |         while (*argp && *argp != ':' && *argp != '=') { | ||
|  |             argp++; | ||
|  |         } | ||
|  |         if (*argp == ':' || *argp == '=') { | ||
|  |             *argp++ = '\0'; | ||
|  |         } | ||
|  | 
 | ||
|  |         switch (argn[0]) { | ||
|  | 
 | ||
|  |           case 'd':                                     // Drop Processes
 | ||
|  |           case 'D': | ||
|  |             if (*argp) { | ||
|  |                 PWCHAR pwz = wzzDrop; | ||
|  |                 while (*argp) { | ||
|  |                     if (*argp == ';') { | ||
|  |                         *pwz++ = '\0'; | ||
|  |                     } | ||
|  |                     else { | ||
|  |                         *pwz++ = *argp++; | ||
|  |                     } | ||
|  |                 } | ||
|  |                 *pwz++ = '\0'; | ||
|  |                 *pwz = '\0'; | ||
|  |             } | ||
|  |           case 'o':                                 // Output file.
 | ||
|  |           case 'O': | ||
|  |             StringCchCopyA(s_szLogFile, ARRAYSIZE(s_szLogFile), argp); | ||
|  |             break; | ||
|  | 
 | ||
|  |           case 'v':                                     // Verbose
 | ||
|  |           case 'V': | ||
|  |             s_fVerbose = TRUE; | ||
|  |             break; | ||
|  | 
 | ||
|  |           case '?':                                 // Help.
 | ||
|  |             fNeedHelp = TRUE; | ||
|  |             break; | ||
|  | 
 | ||
|  |           default: | ||
|  |             fNeedHelp = TRUE; | ||
|  |             printf("TRACEBLD: Bad argument: %s:%s\n", argn, argp); | ||
|  |             break; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     if (arg >= argc) { | ||
|  |         fNeedHelp = TRUE; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (fNeedHelp) { | ||
|  |         printf("Usage:\n" | ||
|  |                "    tracebld [options] command {command arguments}\n" | ||
|  |                "Options:\n" | ||
|  |                "    /o:file    Log all events to the output files.\n" | ||
|  |                "    /?         Display this help message.\n" | ||
|  |                "Summary:\n" | ||
|  |                "    Runs the build commands and figures out which files have dependencies..\n" | ||
|  |                "\n"); | ||
|  |         exit(9001); | ||
|  |     } | ||
|  | 
 | ||
|  |     // Create the completion port.
 | ||
|  |     hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 0); | ||
|  |     if (hCompletionPort == NULL) { | ||
|  |         MyErrExit("CreateIoCompletionPort"); | ||
|  |     } | ||
|  | 
 | ||
|  |     // Create completion port worker threads.
 | ||
|  |     //
 | ||
|  |     CreateWorkers(hCompletionPort); | ||
|  |     CreatePipeConnection(hCompletionPort, 0); | ||
|  | 
 | ||
|  |     printf("TRACEBLD: Ready for clients.  Press Ctrl-C to stop.\n"); | ||
|  | 
 | ||
|  |     /////////////////////////////////////////////////////////// Validate DLLs.
 | ||
|  |     //
 | ||
|  |     CHAR szTmpPath[MAX_PATH]; | ||
|  |     CHAR szExePath[MAX_PATH]; | ||
|  |     CHAR szDllPath[MAX_PATH]; | ||
|  |     PCHAR pszFilePart = NULL; | ||
|  | 
 | ||
|  |     if (!GetModuleFileNameA(NULL, szTmpPath, ARRAYSIZE(szTmpPath))) { | ||
|  |         printf("TRACEBLD: Couldn't retreive exe name.\n"); | ||
|  |         return 9002; | ||
|  |     } | ||
|  |     if (!GetFullPathNameA(szTmpPath, ARRAYSIZE(szExePath), szExePath, &pszFilePart) || | ||
|  |         pszFilePart == NULL) { | ||
|  |         printf("TRACEBLD: Error: %s is not a valid path name..\n", szTmpPath); | ||
|  |         return 9002; | ||
|  |     } | ||
|  | 
 | ||
|  |     StringCchCopyA(pszFilePart, szExePath + ARRAYSIZE(szExePath) - pszFilePart, | ||
|  |              "trcbld" DETOURS_STRINGIFY(DETOURS_BITS) ".dll"); | ||
|  |     StringCchCopyA(szDllPath, ARRAYSIZE(szDllPath), szExePath); | ||
|  | 
 | ||
|  |     //////////////////////////////////////////////////////////////////////////
 | ||
|  |     STARTUPINFOA si; | ||
|  |     PROCESS_INFORMATION pi; | ||
|  |     CHAR szCommand[2048]; | ||
|  |     CHAR szExe[MAX_PATH]; | ||
|  |     CHAR szFullExe[MAX_PATH] = "\0"; | ||
|  |     PCHAR pszFileExe = NULL; | ||
|  | 
 | ||
|  |     ZeroMemory(&si, sizeof(si)); | ||
|  |     ZeroMemory(&pi, sizeof(pi)); | ||
|  |     si.cb = sizeof(si); | ||
|  | 
 | ||
|  |     szCommand[0] = L'\0'; | ||
|  | 
 | ||
|  |     StringCchCopyA(szExe, sizeof(szExe), argv[arg]); | ||
|  |     for (; arg < argc; arg++) { | ||
|  |         if (strchr(argv[arg], ' ') != NULL || strchr(argv[arg], '\t') != NULL) { | ||
|  |             StringCchCatA(szCommand, sizeof(szCommand), "\""); | ||
|  |             StringCchCatA(szCommand, sizeof(szCommand), argv[arg]); | ||
|  |             StringCchCatA(szCommand, sizeof(szCommand), "\""); | ||
|  |         } | ||
|  |         else { | ||
|  |             StringCchCatA(szCommand, sizeof(szCommand), argv[arg]); | ||
|  |         } | ||
|  | 
 | ||
|  |         if (arg + 1 < argc) { | ||
|  |             StringCchCatA(szCommand, sizeof(szCommand), " "); | ||
|  |         } | ||
|  |     } | ||
|  |     printf("TRACEBLD: Starting: `%s'\n", szCommand); | ||
|  |     printf("TRACEBLD:   with `%s'\n", szDllPath); | ||
|  |     fflush(stdout); | ||
|  | 
 | ||
|  |     DWORD dwFlags = CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED; | ||
|  | 
 | ||
|  |     SetLastError(0); | ||
|  |     SearchPathA(NULL, szExe, ".exe", ARRAYSIZE(szFullExe), szFullExe, &pszFileExe); | ||
|  | 
 | ||
|  | 
 | ||
|  |     if (!DetourCreateProcessWithDllExA(szFullExe[0] ? szFullExe : NULL, szCommand, | ||
|  |                                        NULL, NULL, TRUE, dwFlags, NULL, NULL, | ||
|  |                                        &si, &pi, szDllPath, NULL)) { | ||
|  |         printf("TRACEBLD: DetourCreateProcessWithDllEx failed: %ld\n", GetLastError()); | ||
|  |         ExitProcess(9007); | ||
|  |     } | ||
|  | 
 | ||
|  |     ZeroMemory(&s_Payload, sizeof(s_Payload)); | ||
|  |     s_Payload.nParentProcessId = GetCurrentProcessId(); | ||
|  |     s_Payload.nTraceProcessId = GetCurrentProcessId(); | ||
|  |     s_Payload.nGeneology = 1; | ||
|  |     s_Payload.rGeneology[0] = 0; | ||
|  |     StringCchCopyW(s_Payload.wzStdin, ARRAYSIZE(s_Payload.wzStdin), L"\\\\.\\CONIN$"); | ||
|  |     StringCchCopyW(s_Payload.wzStdout, ARRAYSIZE(s_Payload.wzStdout), L"\\\\.\\CONOUT$"); | ||
|  |     StringCchCopyW(s_Payload.wzStderr, ARRAYSIZE(s_Payload.wzStderr), L"\\\\.\\CONOUT$"); | ||
|  |     StringCchCopyW(s_Payload.wzParents, ARRAYSIZE(s_Payload.wzParents), L""); | ||
|  |     CopyEnvironment(s_Payload.wzzDrop, wzzDrop); | ||
|  |     LPWCH pwStrings = GetEnvironmentStringsW(); | ||
|  |     CopyEnvironment(s_Payload.wzzEnvironment, pwStrings); | ||
|  |     FreeEnvironmentStringsW(pwStrings); | ||
|  | 
 | ||
|  |     if (!DetourCopyPayloadToProcess(pi.hProcess, s_guidTrace, | ||
|  |                                     &s_Payload, sizeof(s_Payload))) { | ||
|  |         printf("TRACEBLD: DetourCopyPayloadToProcess failed: %ld\n", GetLastError()); | ||
|  |         ExitProcess(9008); | ||
|  |     } | ||
|  | 
 | ||
|  |     ResumeThread(pi.hThread); | ||
|  | 
 | ||
|  |     WaitForSingleObject(pi.hProcess, INFINITE); | ||
|  | 
 | ||
|  |     DWORD dwResult = 0; | ||
|  |     if (!GetExitCodeProcess(pi.hProcess, &dwResult)) { | ||
|  |         printf("TRACEBLD: GetExitCodeProcess failed: %ld\n", GetLastError()); | ||
|  |         return 9008; | ||
|  |     } | ||
|  | 
 | ||
|  |     printf("TRACEBLD: %ld processes.\n", s_nTotalClients); | ||
|  | 
 | ||
|  |     return dwResult; | ||
|  | } | ||
|  | //
 | ||
|  | //////////////////////////////////////////////////////////////////////////////
 |