//3456789012345678901234567890123456789012345678901234567890123456789012345678 /*++ Copyright (c) 1997 Microsoft Corporation. Module Name: logutils.c Abstract: Contains functions that deal with ntlog.dll Author: Jason Allor (JasonAll) 5/27/97 Revision History: --*/ #include "logutils.h" static HANDLE g_hSemaphore; static ULONG g_ulPass; static ULONG g_ulFail; static ULONG g_ulInfo; /*++ Routine Description: LoadDLLs Tries to dynamically load ntlog.dll functions Arguments: DLLName: the name of the DLL to load, ntlog.dll in this case Return Value: void --*/ void LoadDLLs(IN PTCHAR DLLName) { HINSTANCE Library; // // Set global handle to logfile to NULL // gPnPTestLog = NULL; // // Load the engine DLL // Library = LoadLibrary(DLLName); if ((UINT) Library > 32) { #ifdef UNICODE _tlLog = (Dll_tlLog) \ GetProcAddress(Library, "tlLog_W"); _tlCreateLog = (Dll_tlCreateLog) \ GetProcAddress(Library, "tlCreateLog_W"); #else _tlLog = (Dll_tlLog) \ GetProcAddress(Library, "tlLog_A"); _tlCreateLog = (Dll_tlCreateLog) \ GetProcAddress(Library, "tlCreateLog_A"); #endif _tlAddParticipant = (Dll_tlAddParticipant) \ GetProcAddress(Library, "tlAddParticipant"); _tlDestroyLog = (Dll_tlDestroyLog) \ GetProcAddress(Library, "tlDestoryLog"); _tlEndVariation = (Dll_tlEndVariation) \ GetProcAddress(Library, "tlEndVariation"); _tlRemoveParticipant = (Dll_tlRemoveParticipant) \ GetProcAddress(Library, "tlRemoveParticipant"); _tlStartVariation = (Dll_tlStartVariation) \ GetProcAddress(Library, "tlStartVariation"); _tlReportStats = (Dll_tlReportStats) \ GetProcAddress(Library, "tlReportStats"); gNtLogLoaded = TRUE; } else { gNtLogLoaded = FALSE; } return; } // LoadDLLs // /*++ Routine Description: InitLog This routine will import the NtLog DLL functions and initialize them Arguments: none Return Value: HANDLE: handle to the log file --*/ HANDLE InitLog(IN PTCHAR tszLogName, IN PTCHAR tszTitle, IN BOOL bConsole) { gPnPTestLog = NULL; gPnPTestLogFile = NULL; g_ulPass = 0; g_ulFail = 0; g_ulInfo = 0; // // Initialize Semaphore // g_hSemaphore = CreateSemaphore(NULL, 1, 9999, NULL); if (g_hSemaphore == NULL) { _ftprintf(stderr, TEXT("WARNING! Could not create semaphore!\n")); } CreateConsoleWindow(bConsole, tszTitle); // // Set up console window for log output // g_hConsole = CreateConsoleScreenBuffer(GENERIC_WRITE, 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL); if (g_hConsole == INVALID_HANDLE_VALUE) { return INVALID_HANDLE_VALUE; } // // Load ntlog.dll // LoadDLLs(TEXT("ntlog.dll")); if (gNtLogLoaded) { g_LogLineLen = NTLOG_LINE_LEN; gPnPTestLog = _tlCreateLog((LPCWSTR)(tszLogName), LOG_OPTIONS); if (!gPnPTestLog) { _ftprintf(stderr, TEXT("WARNING! Log file could not be created!\n")); } else { _tlStartVariation(gPnPTestLog); _tlAddParticipant(gPnPTestLog, 0, 0); } } else { SetConsoleActiveScreenBuffer(g_hConsole); g_LogLineLen = NO_NTLOG_LINE_LEN; gPnPTestLogFile = _tfopen(tszLogName, TEXT("w")); if (!gPnPTestLogFile) { _ftprintf(stderr, TEXT("WARNING! Log file could not be created!\n")); } } return gPnPTestLog; } // InitLog // /*++ Routine Description: ExitLog Processes clean-up work before exiting the program Arguments: none Return Value: void --*/ void ExitLog() { double dTotal; double dPass; double dFail; USHORT usPassPerc; USHORT usFailPerc; CloseHandle(g_hConsole); CloseHandle(g_hSemaphore); if (gNtLogLoaded) { if (gPnPTestLog) { _tlReportStats(gPnPTestLog); _tlRemoveParticipant(gPnPTestLog); _tlEndVariation(gPnPTestLog); gPnPTestLog = NULL; } } else { // // Print out statistics // dTotal = g_ulPass + g_ulFail; dPass = (double)g_ulPass / dTotal; dFail = (double)g_ulFail / dTotal; usPassPerc = (USHORT)(dPass * 100); usFailPerc = (USHORT)(dFail * 100); LogBlankLine(); LogBlankLine(); Log(0, INFO, TEXT("Log Statistics:")); LogBlankLine(); Log(0, INFO, TEXT("Pass: %lu\t%lu%%%%%%%%%%%%%%%"), g_ulPass, usPassPerc); Log(0, INFO, TEXT("Fail: %lu\t%lu%%%%%%%%%%%%%%%"), g_ulFail, usFailPerc); Log(0, INFO, TEXT("Total: %lu"), dTotal); if (gPnPTestLog) { fclose(gPnPTestLogFile); gPnPTestLogFile = NULL; } } } // ExitLog // /*++ Routine Description: WriteLog Wrapper function to write to the log Arguments: dwLoglevel: specifies the log level such as TLS_INF, TLS_WARN, or TLS_SEV2 tszBuffer: the string to write to the log Return Value: void --*/ void WriteLog(IN DWORD dwLogLevel, IN PTCHAR tszBuffer) { USHORT i; HANDLE hConsole; DWORD dwDummy; TCHAR tszLogLine[2000]; CHAR cszAnsi[2000]; CHAR cszLogLine[2000]; if (gNtLogLoaded) { if (gPnPTestLog) { _tlLog(gPnPTestLog, dwLogLevel | TL_VARIATION, tszBuffer); } } else { // // Convert tszBuffer to an ANSI string // #ifdef UNICODE _tcscpy(tszLogLine, tszBuffer); for (i = 0; i < _tcslen(tszBuffer) && i < 1999; i++) { cszAnsi[i] = (CHAR)tszLogLine[0]; _tcscpy(tszLogLine, _tcsinc(tszLogLine)); } cszAnsi[i] = '\0'; #else strcpy(cszAnsi, tszBuffer); #endif switch (dwLogLevel) { case INFO: sprintf(cszLogLine, "INFO "); g_ulInfo++; break; case SEV1: sprintf(cszLogLine, "SEV1 "); g_ulFail++; break; case SEV2: sprintf(cszLogLine, "SEV2 "); g_ulFail++; break; case SEV3: sprintf(cszLogLine, "SEV3 "); g_ulFail++; break; case PASS: sprintf(cszLogLine, "PASS "); g_ulPass++; break; default: sprintf(cszLogLine, " "); } if (gPnPTestLogFile) { sprintf (cszLogLine, "%s%s\n", cszLogLine, cszAnsi); WaitForSingleObject(g_hSemaphore, INFINITE); // // Print to log file // fprintf(gPnPTestLogFile, cszLogLine); fflush(gPnPTestLogFile); // // Print to screen // WriteFile(g_hConsole, cszLogLine, strlen(cszLogLine), &dwDummy, NULL); ReleaseSemaphore(g_hSemaphore, 1, NULL); } } return; } // WriteLog // /*++ Routine Description: Log Wrapper to the Log function. It divides a long string into shorter strings and puts each one on a separate line to avoid running over the end of a console window Arguments: dFunctionNumber: shows which function this log output is coming from. Used to track function progress dwLoglevel: specifies the log level such as TLS_INF, TLS_WARN, or TLS_SEV2 tszLogString: printf() style format string Return Value: void --*/ void Log(IN double dFunctionNumber, IN DWORD dwLogLevel, IN PTCHAR tszLogString, IN ...) { va_list va; TCHAR tszBuffer[LOG_STRING_LEN]; TCHAR tszBufferToPrint[LOG_STRING_LEN]; TCHAR tszTmpString[LOG_STRING_LEN + LOG_SPACE_LEN]; ULONG ulIndex, i, j; BOOL boolFirstTime = TRUE; int iInteger, iFunctionNumber; double dDecimal; // // Prints the list to a buffer // va_start(va, tszLogString); if (!_vsntprintf (tszBuffer, LOG_STRING_LEN, tszLogString, va)) { _ftprintf(stderr, TEXT("Log: Failed\n")); ExitLog(); exit(1); } va_end(va); switch (dwLogLevel) { case INFO: SetConsoleTextAttribute(g_hConsole, GREY); break; case SEV1: SetConsoleTextAttribute(g_hConsole, RED); break; case SEV2: SetConsoleTextAttribute(g_hConsole, RED); break; case SEV3: SetConsoleTextAttribute(g_hConsole, RED); break; case PASS: SetConsoleTextAttribute(g_hConsole, GREEN); break; default: SetConsoleTextAttribute(g_hConsole, GREY); } while (_tcslen(tszBuffer)) { ZeroMemory(tszBufferToPrint, LOG_STRING_LEN); if (_tcslen(tszBuffer) > g_LogLineLen) { // // If the LogString is longer than the console length, start at the // maximum console length and work backwards until we hit a space // to find out where to cut off the string // for (ulIndex = g_LogLineLen; ulIndex > 0; ulIndex--) { if (tszBuffer[ulIndex] == ' ') { break; } } // // index now sits on the last char that we want to print. Create // two strings - one to print now and one with the leftovers. // for (i = 0; i < ulIndex; i++) { tszBufferToPrint[i] = tszBuffer[i]; } ulIndex++; for (i = 0; i < LOG_STRING_LEN; i++) { // // Shift the remaining string up to the front // if (i < LOG_STRING_LEN - ulIndex) { tszBuffer[i] = tszBuffer[i + ulIndex]; } else { tszBuffer[i] = '\0'; } } } else { // // Just print out the entire string since it contains no spaces // _stprintf(tszBufferToPrint, tszBuffer); ZeroMemory(tszBuffer, LOG_STRING_LEN); } if (boolFirstTime) { { _stprintf(tszTmpString, TEXT("(%.2f) "), dFunctionNumber); } _tcscat(tszTmpString, tszBufferToPrint); _tcscpy(tszBufferToPrint, tszTmpString); WriteLog(dwLogLevel, tszBufferToPrint); } else { _stprintf(tszTmpString, TEXT(" ")); _tcscat(tszTmpString, tszBufferToPrint); _tcscpy(tszBufferToPrint, tszTmpString); WriteLog(INFO, tszBufferToPrint); } boolFirstTime = FALSE; } return; } // Log // /*++ Routine Description: LogBlankLine Print a blank line to the log Arguments: none Return Value: void --*/ VOID LogBlankLine() { SetConsoleTextAttribute(g_hConsole, GREY); WriteLog(INFO, TEXT(" ")); } // LogBlankLine // /*++ Routine Description: CreateConsoleWindow Creates a console window for this process to dump the log output into. Gives the console window a title and uses this title to get a handle to the console window. Then disables the cancel button on the window. Arguments: bConsole: TRUE if a new console window needs to be created. FALSE if there is already a console that can be used tszTitle: title to give the console window Return Value: none --*/ VOID CreateConsoleWindow(IN BOOL bConsole, IN PTCHAR tszTitle) { HWND gConsole; if (bConsole) { // // Create a console window to dump the log output in // if (!AllocConsole()) { goto RETURN; } } if (!SetConsoleTitle(tszTitle)) { goto RETURN; } RETURN: return; } // CreateConsoleWindow // VOID AddLogParticipant(IN HANDLE hLog) { if (gNtLogLoaded) { _tlAddParticipant(hLog, 0, 0); } } // AddLogParticipant // VOID RemoveLogParticipant(IN HANDLE hLog) { if (gNtLogLoaded) { _tlRemoveParticipant(hLog); } } // RemoveLogParticipant //