diff --git a/ANSI.c b/ANSI.c index 87401d2..d278877 100644 --- a/ANSI.c +++ b/ANSI.c @@ -68,42 +68,31 @@ implement SO & SI to use the DEC Special Graphics Character Set (enables line drawing via ASCII); ignore \e(X & \e)X (where X is any character); add \e[?25h & \e[?25l to show/hide the cursor (DECTCEM). + + v1.50, 7 to 14 December, 2011: + added dynamic environment variable ANSICON_VER to return version; + read ANSICON_EXC environment variable to exclude selected modules; + read ANSICON_GUI environment variable to hook selected GUI programs; + read ANSICON_DEF environment variable to set the default GR; + transfer current GR to child, read it on exit. */ #include "ansicon.h" +#include "version.h" #include #define isdigit(c) ('0' <= (c) && (c) <= '9') +#ifdef __GNUC__ +#define SHARED __attribute__((shared, section(".share"))) +#else +#pragma section(".shared", read,write,shared) +#define SHARED __declspec(allocate(".shared")) +#endif + // ========== Global variables and constants -// Macro for adding pointers/DWORDs together without C arithmetic interfering -#define MakePtr( cast, ptr, addValue ) (cast)((DWORD_PTR)(ptr)+(DWORD)(addValue)) - - -const char APIKernel[] = "kernel32.dll"; -const char APIKernelBase[] = "kernelbase.dll"; -const char APIConsole[] = "API-MS-Win-Core-Console-L1-1-0.dll"; -const char APIProcessThreads[] = "API-MS-Win-Core-ProcessThreads-L1-1-0.dll"; -const char APIProcessEnvironment[] = "API-MS-Win-Core-ProcessEnvironment-L1-1-0.dll"; -const char APILibraryLoader[] = "API-MS-Win-Core-LibraryLoader-L1-1-0.dll"; -const char APIFile[] = "API-MS-Win-Core-File-L1-1-0.dll"; - -PCSTR APIs[] = -{ - APIKernel, - APIConsole, - APIProcessThreads, - APIProcessEnvironment, - APILibraryLoader, - APIFile, - NULL -}; - - -HMODULE hKernel; // Kernel32 module handle -HINSTANCE hDllInstance; // Dll instance handle HANDLE hConOut; // handle to CONOUT$ #define ESC '\x1B' // ESCape character @@ -175,7 +164,7 @@ const WCHAR G1[] = #define BACKGROUND_BLACK 0 #define BACKGROUND_WHITE BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE -WORD foregroundcolor[8] = +const BYTE foregroundcolor[8] = { FOREGROUND_BLACK, // black foreground FOREGROUND_RED, // red foreground @@ -187,7 +176,7 @@ WORD foregroundcolor[8] = FOREGROUND_WHITE // white foreground }; -WORD backgroundcolor[8] = +const BYTE backgroundcolor[8] = { BACKGROUND_BLACK, // black background BACKGROUND_RED, // red background @@ -199,210 +188,84 @@ WORD backgroundcolor[8] = BACKGROUND_WHITE, // white background }; +const BYTE attr2ansi[8] = // map console attribute to ANSI number +{ + 0, // black + 4, // blue + 2, // green + 6, // cyan + 1, // red + 5, // magenta + 3, // yellow + 7 // white +}; -// screen attributes -WORD org_fg, org_bg, org_bold, org_ul; // original attributes -WORD foreground; -WORD background; -WORD bold; -WORD underline; -WORD rvideo; -WORD concealed; +GRM grm; // saved cursor position COORD SavePos; +// Variables to enable copying attributes between processes. +SHARED DWORD s_pid; +SHARED GRM s_grm; +SHARED DWORD s_flag; +#define GRM_INIT 1 +#define GRM_EXIT 2 -// ========== Hooking API functions -// -// References about API hooking (and dll injection): -// - Matt Pietrek ~ Windows 95 System Programming Secrets. -// - Jeffrey Richter ~ Programming Applications for Microsoft Windows 4th ed. +PROCESS_INFORMATION child_pi; -typedef struct + +// Wait for the child process to finish, then update our GRM to the child's. +DWORD WINAPI UpdateGRM( LPVOID lpParameter ) { - PCSTR lib; - PSTR name; - PROC newfunc; - PROC oldfunc; - PROC apifunc; -} HookFn, *PHookFn; - -HookFn Hooks[]; - -//----------------------------------------------------------------------------- -// HookAPIOneMod -// Substitute a new function in the Import Address Table (IAT) of the -// specified module. -// Return FALSE on error and TRUE on success. -//----------------------------------------------------------------------------- - -BOOL HookAPIOneMod( - HMODULE hFromModule, // Handle of the module to intercept calls from - PHookFn Hooks, // Functions to replace - BOOL restore // Restore the original functions - ) -{ - PIMAGE_DOS_HEADER pDosHeader; - PIMAGE_NT_HEADERS pNTHeader; - PIMAGE_IMPORT_DESCRIPTOR pImportDesc; - PIMAGE_THUNK_DATA pThunk; - PHookFn hook; - - // Tests to make sure we're looking at a module image (the 'MZ' header) - pDosHeader = (PIMAGE_DOS_HEADER)hFromModule; - if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) + WaitForSingleObject( child_pi.hProcess, INFINITE ); + if (s_flag == GRM_EXIT && s_pid == child_pi.dwProcessId) { - DEBUGSTR( L"error: %S(%d)", __FILE__, __LINE__ ); - return FALSE; + s_flag = 0; + grm = s_grm; } - // The MZ header has a pointer to the PE header - pNTHeader = MakePtr( PIMAGE_NT_HEADERS, pDosHeader, pDosHeader->e_lfanew ); + return 0; +} - // One more test to make sure we're looking at a "PE" image - if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) - { - DEBUGSTR( L"error: %S(%d)", __FILE__, __LINE__ ); + +// Search an environment variable for a string. +BOOL search_env( LPCTSTR var, LPCTSTR val ) +{ + static LPTSTR env; + static DWORD env_len; + DWORD len; + BOOL not; + + len = GetEnvironmentVariable( var, env, env_len ); + if (len == 0) return FALSE; + + if (len > env_len) + { + LPTSTR tmp = realloc( env, TSIZE(len) ); + if (tmp == NULL) + return FALSE; + env = tmp; + env_len = len; + GetEnvironmentVariable( var, env, env_len ); } - // We now have a valid pointer to the module's PE header. - // Get a pointer to its imports section. - pImportDesc = MakePtr( PIMAGE_IMPORT_DESCRIPTOR, - pDosHeader, - pNTHeader->OptionalHeader. - DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]. - VirtualAddress ); - - // Bail out if the RVA of the imports section is 0 (it doesn't exist) - if (pImportDesc == (PIMAGE_IMPORT_DESCRIPTOR)pDosHeader) - { + not = (*env == '!'); + if (not && env[1] == '\0') return TRUE; - } - // Iterate through the array of imported module descriptors, looking - // for the module whose name matches the pszFunctionModule parameter. - for (; pImportDesc->Name; pImportDesc++) - { - PCSTR* lib; - PSTR pszModName = MakePtr( PSTR, pDosHeader, pImportDesc->Name ); - for (lib = APIs; *lib; ++lib) - if (_stricmp( pszModName, *lib ) == 0) - break; - if (*lib == NULL) - continue; + for (var = wcstok( env + not, L";" ); var; var = wcstok( NULL, L";" )) + if (_wcsicmp( val, var ) == 0) + return !not; - // Get a pointer to the found module's import address table (IAT). - pThunk = MakePtr( PIMAGE_THUNK_DATA, pDosHeader, pImportDesc->FirstThunk ); - - // Blast through the table of import addresses, looking for the ones - // that match the original addresses. - while (pThunk->u1.Function) - { - for (hook = Hooks; hook->name; ++hook) - { - PROC patch = 0; - if (restore) - { - if ((PROC)pThunk->u1.Function == hook->newfunc) - patch = (lib == APIs) ? hook->oldfunc : hook->apifunc; - } - else if ((PROC)pThunk->u1.Function == hook->oldfunc || - (PROC)pThunk->u1.Function == hook->apifunc) - { - patch = hook->newfunc; - } - if (patch) - { - DWORD flOldProtect, flNewProtect, flDummy; - MEMORY_BASIC_INFORMATION mbi; - - DEBUGSTR( L" %S", hook->name ); - // Get the current protection attributes. - VirtualQuery( &pThunk->u1.Function, &mbi, sizeof(mbi) ); - // Take the access protection flags. - flNewProtect = mbi.Protect; - // Remove ReadOnly and ExecuteRead flags. - flNewProtect &= ~(PAGE_READONLY | PAGE_EXECUTE_READ); - // Add on ReadWrite flag - flNewProtect |= (PAGE_READWRITE); - // Change the access protection on the region of committed pages in the - // virtual address space of the current process. - VirtualProtect( &pThunk->u1.Function, sizeof(PVOID), - flNewProtect, &flOldProtect ); - - // Overwrite the original address with the address of the new function. - if (!WriteProcessMemory( GetCurrentProcess(), - &pThunk->u1.Function, - &patch, sizeof(patch), NULL )) - { - DEBUGSTR( L"error: %S(%d)", __FILE__, __LINE__ ); - return FALSE; - } - - // Put the page attributes back the way they were. - VirtualProtect( &pThunk->u1.Function, sizeof(PVOID), - flOldProtect, &flDummy ); - } - } - pThunk++; // Advance to next imported function address - } - } - - return TRUE; // Function not found + return not; } -//----------------------------------------------------------------------------- -// HookAPIAllMod -// Substitute a new function in the Import Address Table (IAT) of all -// the modules in the current process. -// Return FALSE on error and TRUE on success. -//----------------------------------------------------------------------------- - -BOOL HookAPIAllMod( PHookFn Hooks, BOOL restore ) -{ - HANDLE hModuleSnap; - MODULEENTRY32 me; - BOOL fOk; - - // Take a snapshot of all modules in the current process. - hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, - GetCurrentProcessId() ); - - if (hModuleSnap == INVALID_HANDLE_VALUE) - { - DEBUGSTR( L"error: %S(%d)", __FILE__, __LINE__ ); - return FALSE; - } - - // Fill the size of the structure before using it. - me.dwSize = sizeof(MODULEENTRY32); - - // Walk the module list of the modules. - for (fOk = Module32First( hModuleSnap, &me ); fOk; - fOk = Module32Next( hModuleSnap, &me )) - { - // We don't hook functions in our own module. - if (me.hModule != hDllInstance && me.hModule != hKernel) - { - DEBUGSTR( (restore) ? L"Unhooking from %s" : L"Hooking in %s", - me.szModule ); - // Hook this function in this module. - if (!HookAPIOneMod( me.hModule, Hooks, restore )) - { - CloseHandle( hModuleSnap ); - return FALSE; - } - } - } - CloseHandle( hModuleSnap ); - return TRUE; -} // ========== Print Buffer functions -#define BUFFER_SIZE (278+512) // fill out the section +#define BUFFER_SIZE 2048 int nCharInBuffer; WCHAR ChBuffer[BUFFER_SIZE]; @@ -508,58 +371,86 @@ void InterpretEscSeq( void ) if (es_argc == 0) es_argv[es_argc++] = 0; for (i = 0; i < es_argc; i++) { - switch (es_argv[i]) + if (30 <= es_argv[i] && es_argv[i] <= 37) + grm.foreground = es_argv[i] - 30; + else if (40 <= es_argv[i] && es_argv[i] <= 47) + grm.background = es_argv[i] - 40; + else switch (es_argv[i]) { case 0: - foreground = org_fg; - background = org_bg; - bold = (es_argc == 1) ? org_bold : 0; - underline = (es_argc == 1) ? org_ul : 0; - rvideo = 0; - concealed = 0; + { + TCHAR def[4]; + int a; + *def = '7'; def[1] = '\0'; + GetEnvironmentVariable( L"ANSICON_DEF", def, lenof(def) ); + a = wcstol( def, NULL, 16 ); + grm.reverse = FALSE; + if (a < 0) + { + grm.reverse = TRUE; + a = -a; + } + grm.foreground = attr2ansi[a & 7]; + grm.background = attr2ansi[(a >> 4) & 7]; + if (es_argc == 1) + { + grm.bold = a & FOREGROUND_INTENSITY; + grm.underline = a & BACKGROUND_INTENSITY; + } + else + { + grm.bold = 0; + grm.underline = 0; + } + grm.rvideo = 0; + grm.concealed = 0; + } break; - case 1: bold = FOREGROUND_INTENSITY; break; - case 5: /* blink */ - case 4: underline = BACKGROUND_INTENSITY; break; - case 7: rvideo = 1; break; - case 8: concealed = 1; break; - case 21: bold = 0; break; + + case 1: grm.bold = FOREGROUND_INTENSITY; break; + case 5: // blink + case 4: grm.underline = BACKGROUND_INTENSITY; break; + case 7: grm.rvideo = 1; break; + case 8: grm.concealed = 1; break; + case 21: // oops, this actually turns on double underline + case 22: grm.bold = 0; break; case 25: - case 24: underline = 0; break; - case 27: rvideo = 0; break; - case 28: concealed = 0; break; + case 24: grm.underline = 0; break; + case 27: grm.rvideo = 0; break; + case 28: grm.concealed = 0; break; } - if (30 <= es_argv[i] && es_argv[i] <= 37) foreground = es_argv[i]-30; - if (40 <= es_argv[i] && es_argv[i] <= 47) background = es_argv[i]-40; } - if (concealed) + if (grm.concealed) { - if (rvideo) + if (grm.rvideo) { - attribut = foregroundcolor[foreground] - | backgroundcolor[foreground]; - if (bold) + attribut = foregroundcolor[grm.foreground] + | backgroundcolor[grm.foreground]; + if (grm.bold) attribut |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY; } else { - attribut = foregroundcolor[background] - | backgroundcolor[background]; - if (underline) + attribut = foregroundcolor[grm.background] + | backgroundcolor[grm.background]; + if (grm.underline) attribut |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY; } } - else if (rvideo) + else if (grm.rvideo) { - attribut = foregroundcolor[background] | backgroundcolor[foreground]; - if (bold) + attribut = foregroundcolor[grm.background] + | backgroundcolor[grm.foreground]; + if (grm.bold) attribut |= BACKGROUND_INTENSITY; - if (underline) + if (grm.underline) attribut |= FOREGROUND_INTENSITY; } else - attribut = foregroundcolor[foreground] | backgroundcolor[background] - | bold | underline; + attribut = foregroundcolor[grm.foreground] | grm.bold + | backgroundcolor[grm.background] | grm.underline; + if (grm.reverse) + attribut = ((attribut >> 4) & 15) | ((attribut & 15) << 4); SetConsoleTextAttribute( hConOut, attribut ); return; @@ -974,50 +865,340 @@ ParseAndPrintString( HANDLE hDev, FlushBuffer(); if (lpNumberOfBytesWritten != NULL) *lpNumberOfBytesWritten = nNumberOfBytesToWrite - i; - return( i == 0 ); + return (i == 0); +} + + +// ========== Hooking API functions +// +// References about API hooking (and dll injection): +// - Matt Pietrek ~ Windows 95 System Programming Secrets. +// - Jeffrey Richter ~ Programming Applications for Microsoft Windows 4th ed. + +// Macro for adding pointers/DWORDs together without C arithmetic interfering +#define MakeVA( cast, offset ) (cast)((DWORD_PTR)(pDosHeader)+(DWORD)(offset)) + + +const char APIKernel[] = "kernel32.dll"; +const char APIConsole[] = "API-MS-Win-Core-Console-L1-1-0.dll"; +const char APIProcessThreads[] = "API-MS-Win-Core-ProcessThreads-L1-1-0.dll"; +const char APIProcessEnvironment[] = "API-MS-Win-Core-ProcessEnvironment-L1-1-0.dll"; +const char APILibraryLoader[] = "API-MS-Win-Core-LibraryLoader-L1-1-0.dll"; +const char APIFile[] = "API-MS-Win-Core-File-L1-1-0.dll"; + +PCSTR APIs[] = +{ + APIKernel, + APIConsole, + APIProcessThreads, + APIProcessEnvironment, + APILibraryLoader, + APIFile, + NULL +}; + + +HMODULE hKernel; // Kernel32 module handle +HINSTANCE hDllInstance; // Dll instance handle +TCHAR hDllName[MAX_PATH]; // Dll file name +#ifdef _WIN64 +LPTSTR hDllNameType; // pointer to process type within above +#endif + +typedef struct +{ + PCSTR lib; + PSTR name; + PROC newfunc; + PROC oldfunc; + PROC apifunc; +} HookFn, *PHookFn; + +HookFn Hooks[]; + +const WCHAR zIgnoring[] = L"Ignoring"; +const WCHAR zHooking[] = L"Hooking"; +const WCHAR zUnhooking[] = L"Unhooking"; + + +//----------------------------------------------------------------------------- +// HookAPIOneMod +// Substitute a new function in the Import Address Table (IAT) of the +// specified module. +// Return FALSE on error and TRUE on success. +//----------------------------------------------------------------------------- + +BOOL HookAPIOneMod( + HMODULE hFromModule, // Handle of the module to intercept calls from + PHookFn Hooks, // Functions to replace + BOOL restore // Restore the original functions + ) +{ + PIMAGE_DOS_HEADER pDosHeader; + PIMAGE_NT_HEADERS pNTHeader; + PIMAGE_IMPORT_DESCRIPTOR pImportDesc; + PIMAGE_THUNK_DATA pThunk; + PHookFn hook; + + // Tests to make sure we're looking at a module image (the 'MZ' header) + pDosHeader = (PIMAGE_DOS_HEADER)hFromModule; + if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) + { + DEBUGSTR( 1, L"Image has no DOS header!" ); + return FALSE; + } + + // The MZ header has a pointer to the PE header + pNTHeader = MakeVA( PIMAGE_NT_HEADERS, pDosHeader->e_lfanew ); + + // One more test to make sure we're looking at a "PE" image + if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) + { + DEBUGSTR( 1, L"Image has no NT header!" ); + return FALSE; + } + + // We now have a valid pointer to the module's PE header. + // Get a pointer to its imports section. + pImportDesc = MakeVA( PIMAGE_IMPORT_DESCRIPTOR, + pNTHeader->OptionalHeader. + DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]. + VirtualAddress ); + + // Bail out if the RVA of the imports section is 0 (it doesn't exist) + if (pImportDesc == (PIMAGE_IMPORT_DESCRIPTOR)pDosHeader) + return TRUE; + + // Iterate through the array of imported module descriptors, looking + // for the module whose name matches the pszFunctionModule parameter. + for (; pImportDesc->Name; pImportDesc++) + { + PCSTR* lib; + PSTR pszModName = MakeVA( PSTR, pImportDesc->Name ); + for (lib = APIs; *lib; ++lib) + if (_stricmp( pszModName, *lib ) == 0) + break; + if (*lib == NULL) + continue; + + // Get a pointer to the found module's import address table (IAT). + pThunk = MakeVA( PIMAGE_THUNK_DATA, pImportDesc->FirstThunk ); + + // Blast through the table of import addresses, looking for the ones + // that match the original addresses. + while (pThunk->u1.Function) + { + for (hook = Hooks; hook->name; ++hook) + { + PROC patch = 0; + if (restore) + { + if ((PROC)pThunk->u1.Function == hook->newfunc) + patch = (lib == APIs) ? hook->oldfunc : hook->apifunc; + } + else if ((PROC)pThunk->u1.Function == hook->oldfunc || + (PROC)pThunk->u1.Function == hook->apifunc) + { + patch = hook->newfunc; + } + if (patch) + { + DWORD flOldProtect, flNewProtect, flDummy; + MEMORY_BASIC_INFORMATION mbi; + + DEBUGSTR( 3, L" %S", hook->name ); + // Get the current protection attributes. + VirtualQuery( &pThunk->u1.Function, &mbi, sizeof(mbi) ); + // Take the access protection flags. + flNewProtect = mbi.Protect; + // Remove ReadOnly and ExecuteRead flags. + flNewProtect &= ~(PAGE_READONLY | PAGE_EXECUTE_READ); + // Add on ReadWrite flag + flNewProtect |= (PAGE_READWRITE); + // Change the access protection on the region of committed pages in the + // virtual address space of the current process. + VirtualProtect( &pThunk->u1.Function, sizeof(PVOID), + flNewProtect, &flOldProtect ); + + // Overwrite the original address with the address of the new function. + if (!WriteProcessMemory( GetCurrentProcess(), + &pThunk->u1.Function, + &patch, sizeof(patch), NULL )) + { + DEBUGSTR( 1, L"Could not patch!" ); + return FALSE; + } + + // Put the page attributes back the way they were. + VirtualProtect( &pThunk->u1.Function, sizeof(PVOID), + flOldProtect, &flDummy ); + } + } + pThunk++; // Advance to next imported function address + } + } + + return TRUE; // Function not found +} + +//----------------------------------------------------------------------------- +// HookAPIAllMod +// Substitute a new function in the Import Address Table (IAT) of all +// the modules in the current process. +// Return FALSE on error and TRUE on success. +//----------------------------------------------------------------------------- + +BOOL HookAPIAllMod( PHookFn Hooks, BOOL restore ) +{ + HANDLE hModuleSnap; + MODULEENTRY32 me; + BOOL fOk; + + // Take a snapshot of all modules in the current process. + hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, + GetCurrentProcessId() ); + + if (hModuleSnap == INVALID_HANDLE_VALUE) + { + DEBUGSTR( 1, L"Failed to create snapshot!" ); + return FALSE; + } + + // Fill the size of the structure before using it. + me.dwSize = sizeof(MODULEENTRY32); + + // Walk the module list of the modules. + for (fOk = Module32First( hModuleSnap, &me ); fOk; + fOk = Module32Next( hModuleSnap, &me )) + { + // We don't hook functions in our own module. + if (me.hModule != hDllInstance && me.hModule != hKernel) + { + if (search_env( L"ANSICON_EXC", me.szModule )) + { + DEBUGSTR( 2, L"%s %s", zIgnoring, me.szModule ); + continue; + } + DEBUGSTR( 2, L"%s %s", (restore) ? zUnhooking : zHooking, me.szModule ); + // Hook this function in this module. + if (!HookAPIOneMod( me.hModule, Hooks, restore )) + { + CloseHandle( hModuleSnap ); + return FALSE; + } + } + } + CloseHandle( hModuleSnap ); + return TRUE; } // ========== Child process injection // Inject code into the target process to load our DLL. -void Inject( LPPROCESS_INFORMATION pinfo, LPPROCESS_INFORMATION lpi, - DWORD dwCreationFlags ) +void Inject( DWORD dwCreationFlags, LPPROCESS_INFORMATION lpi, + BOOL wide, LPCVOID lpApp, LPCVOID lpCmd ) { - int type = ProcessType( pinfo ); - if (type != 0) + int type; + BOOL gui; + + type = ProcessType( &child_pi, &gui ); + if (gui) { - WCHAR dll[MAX_PATH]; -#ifdef _WIN64 - DWORD len = GetModuleFileName( GetModuleHandleA( "ANSI64.dll" ), - dll, lenof(dll) ); - if (type == 32) + TCHAR app[MAX_PATH]; + LPTSTR name; + LPCTSTR term = L" \t"; + + app[MAX_PATH-1] = '\0'; + if (lpApp == NULL) { - dll[len-6] = '3'; - dll[len-5] = '2'; - InjectDLL32( pinfo, dll ); + // Extract the program from the command line. I would use + // GetModuleFileNameEx, but it doesn't work when a process is created + // suspended and setting up a delay until it does work sometimes + // prevents the process running at all. GetProcessImageFileName works, + // but it's not supported in 2K. + if (wide) + { + LPCTSTR pos; + for (pos = lpCmd; *pos == ' ' || *pos == '\t'; ++pos) ; + if (*pos == '"') + { + term = L"\""; + ++pos; + } + wcsncpy( app, pos, MAX_PATH-1 ); + } + else + { + LPCSTR pos; + for (pos = lpCmd; *pos == ' ' || *pos == '\t'; ++pos) ; + if (*pos == '"') + { + term = L"\""; + ++pos; + } + MultiByteToWideChar( CP_ACP, 0, pos, -1, app, MAX_PATH ); + } + // CreateProcess only works with surrounding quotes ('"a name"' works, but + // 'a" "name' fails), so that's all I'll test, too. However, it also + // tests for a file at each separator ('a name' tries "a.exe" before + // "a name.exe") which I won't do. + name = wcspbrk( app, term ); + if (name) + *name = '\0'; } else { - InjectDLL64( pinfo, dll ); + if (wide) + wcsncpy( app, lpApp, MAX_PATH-1 ); + else + MultiByteToWideChar( CP_ACP, 0, lpApp, -1, app, MAX_PATH ); + } + name = get_program_name( app ); + if (!search_env( L"ANSICON_GUI", name )) + { + DEBUGSTR( 1, L" %s", zIgnoring ); + type = 0; + } + } + if (type != 0) + { +#ifdef _WIN64 + if (type == 32) + { + hDllNameType[0] = '3'; + hDllNameType[1] = '2'; + InjectDLL32( &child_pi, hDllName ); + } + else + { + hDllNameType[0] = '6'; + hDllNameType[1] = '4'; + InjectDLL64( &child_pi, hDllName ); } #else - GetModuleFileName( GetModuleHandleA( "ANSI32.dll" ), dll, lenof(dll) ); - InjectDLL32( pinfo, dll ); + InjectDLL32( &child_pi, hDllName ); #endif + if (!gui && !(dwCreationFlags & (CREATE_NEW_CONSOLE | DETACHED_PROCESS))) + { + s_pid = child_pi.dwProcessId; + s_grm = grm; + s_flag = GRM_INIT; + CloseHandle( CreateThread( NULL, 4096, UpdateGRM, NULL, 0, NULL ) ); + } } if (!(dwCreationFlags & CREATE_SUSPENDED)) - ResumeThread( pinfo->hThread ); + ResumeThread( child_pi.hThread ); if (lpi) { - memcpy( lpi, pinfo, sizeof(PROCESS_INFORMATION) ); + memcpy( lpi, &child_pi, sizeof(PROCESS_INFORMATION) ); } else { - CloseHandle( pinfo->hProcess ); - CloseHandle( pinfo->hThread ); + CloseHandle( child_pi.hThread ); + CloseHandle( child_pi.hProcess ); } } @@ -1033,8 +1214,6 @@ BOOL WINAPI MyCreateProcessA( LPCSTR lpApplicationName, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ) { - PROCESS_INFORMATION pi; - if (!CreateProcessA( lpApplicationName, lpCommandLine, lpThreadAttributes, @@ -1044,13 +1223,14 @@ BOOL WINAPI MyCreateProcessA( LPCSTR lpApplicationName, lpEnvironment, lpCurrentDirectory, lpStartupInfo, - &pi )) + &child_pi )) return FALSE; - DEBUGSTR( L"CreateProcessA: \"%S\", \"%S\"", + DEBUGSTR( 1, L"CreateProcessA: \"%S\", \"%S\"", (lpApplicationName == NULL) ? "" : lpApplicationName, (lpCommandLine == NULL) ? "" : lpCommandLine ); - Inject( &pi, lpProcessInformation, dwCreationFlags ); + Inject( dwCreationFlags, lpProcessInformation, + FALSE, lpApplicationName, lpCommandLine ); return TRUE; } @@ -1067,8 +1247,6 @@ BOOL WINAPI MyCreateProcessW( LPCWSTR lpApplicationName, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ) { - PROCESS_INFORMATION pi; - if (!CreateProcessW( lpApplicationName, lpCommandLine, lpThreadAttributes, @@ -1078,13 +1256,14 @@ BOOL WINAPI MyCreateProcessW( LPCWSTR lpApplicationName, lpEnvironment, lpCurrentDirectory, lpStartupInfo, - &pi )) + &child_pi )) return FALSE; - DEBUGSTR( L"CreateProcessW: \"%s\", \"%s\"", + DEBUGSTR( 1, L"CreateProcessW: \"%s\", \"%s\"", (lpApplicationName == NULL) ? L"" : lpApplicationName, (lpCommandLine == NULL) ? L"" : lpCommandLine ); - Inject( &pi, lpProcessInformation, dwCreationFlags ); + Inject( dwCreationFlags, lpProcessInformation, + TRUE, lpApplicationName, lpCommandLine ); return TRUE; } @@ -1104,13 +1283,15 @@ FARPROC WINAPI MyGetProcAddress( HMODULE hModule, LPCSTR lpProcName ) // Ignore LoadLibrary so other hooks continue to work (our version // might end up at a different address). if (proc == Hooks[0].oldfunc || proc == Hooks[1].oldfunc) + { + DEBUGSTR( 3, L"GetProcAddress: %S (ignoring)", lpProcName ); return proc; - + } for (hook = Hooks+2; hook->name; ++hook) { if (proc == hook->oldfunc) { - DEBUGSTR( L"GetProcAddress: %S", lpProcName ); + DEBUGSTR( 3, L"GetProcAddress: %S", lpProcName ); return hook->newfunc; } } @@ -1118,13 +1299,15 @@ FARPROC WINAPI MyGetProcAddress( HMODULE hModule, LPCSTR lpProcName ) else if (Hooks[0].apifunc) // assume if one is defined, all are { if (proc == Hooks[0].apifunc || proc == Hooks[1].apifunc) + { + DEBUGSTR( 3, L"GetProcAddress: %S (ignoring)", lpProcName ); return proc; - + } for (hook = Hooks+2; hook->name; ++hook) { if (proc == hook->apifunc) { - DEBUGSTR( L"GetProcAddress: %S", lpProcName ); + DEBUGSTR( 3, L"GetProcAddress: %S", lpProcName ); return hook->newfunc; } } @@ -1135,14 +1318,39 @@ FARPROC WINAPI MyGetProcAddress( HMODULE hModule, LPCSTR lpProcName ) } +void HookLibrary( HMODULE hMod, LPCVOID lpFileName, BOOL wide, LPCSTR funcName ) +{ + LPCWSTR name; + WCHAR wname[MAX_PATH]; + + if (hMod && hMod != hKernel) + { + if (!wide) + { + MultiByteToWideChar( AreFileApisANSI() ? CP_ACP : CP_OEMCP, 0, + lpFileName, -1, wname, MAX_PATH ); + lpFileName = wname; + } + name = wcsrchr( lpFileName, '\\' ); + if (name == NULL) + name = lpFileName; + else + ++name; + if (search_env( L"ANSICON_EXC", name )) + DEBUGSTR( 2, L"%s %s (%S)", zIgnoring, lpFileName, funcName ); + else + { + DEBUGSTR( 2, L"%s %s (%S)", zHooking, lpFileName, funcName ); + HookAPIOneMod( hMod, Hooks, FALSE ); + } + } +} + + HMODULE WINAPI MyLoadLibraryA( LPCSTR lpFileName ) { HMODULE hMod = LoadLibraryA( lpFileName ); - if (hMod && hMod != hKernel) - { - DEBUGSTR( L"Hooking in %S (LoadLibraryA)", lpFileName ); - HookAPIOneMod( hMod, Hooks, FALSE ); - } + HookLibrary( hMod, lpFileName, FALSE, "LoadLibraryA" ); return hMod; } @@ -1150,11 +1358,7 @@ HMODULE WINAPI MyLoadLibraryA( LPCSTR lpFileName ) HMODULE WINAPI MyLoadLibraryW( LPCWSTR lpFileName ) { HMODULE hMod = LoadLibraryW( lpFileName ); - if (hMod && hMod != hKernel) - { - DEBUGSTR( L"Hooking in %s (LoadLibraryW)", lpFileName ); - HookAPIOneMod( hMod, Hooks, FALSE ); - } + HookLibrary( hMod, lpFileName, TRUE, "LoadLibraryW" ); return hMod; } @@ -1163,11 +1367,8 @@ HMODULE WINAPI MyLoadLibraryExA( LPCSTR lpFileName, HANDLE hFile, DWORD dwFlags ) { HMODULE hMod = LoadLibraryExA( lpFileName, hFile, dwFlags ); - if (hMod && hMod != hKernel && !(dwFlags & LOAD_LIBRARY_AS_DATAFILE)) - { - DEBUGSTR( L"Hooking in %S (LoadLibraryExA)", lpFileName ); - HookAPIOneMod( hMod, Hooks, FALSE ); - } + if (!(dwFlags & LOAD_LIBRARY_AS_DATAFILE)) + HookLibrary( hMod, lpFileName, FALSE, "LoadLibraryExA" ); return hMod; } @@ -1176,11 +1377,8 @@ HMODULE WINAPI MyLoadLibraryExW( LPCWSTR lpFileName, HANDLE hFile, DWORD dwFlags ) { HMODULE hMod = LoadLibraryExW( lpFileName, hFile, dwFlags ); - if (hMod && hMod != hKernel && !(dwFlags & LOAD_LIBRARY_AS_DATAFILE)) - { - DEBUGSTR( L"Hooking in %s (LoadLibraryExW)", lpFileName ); - HookAPIOneMod( hMod, Hooks, FALSE ); - } + if (!(dwFlags & LOAD_LIBRARY_AS_DATAFILE)) + HookLibrary( hMod, lpFileName, TRUE, "LoadLibraryExW" ); return hMod; } @@ -1205,10 +1403,10 @@ WINAPI MyWriteConsoleA( HANDLE hCon, LPCVOID lpBuffer, if (GetConsoleMode( hCon, &Mode ) && (Mode & ENABLE_PROCESSED_OUTPUT)) { UINT cp = GetConsoleOutputCP(); - DEBUGSTR( L"\33WriteConsoleA: %lu \"%.*S\"", + DEBUGSTR( 4, L"\33WriteConsoleA: %lu \"%.*S\"", nNumberOfCharsToWrite, nNumberOfCharsToWrite, lpBuffer ); - len = MultiByteToWideChar( cp, 0, lpBuffer, nNumberOfCharsToWrite, NULL, 0 ); - buf = malloc( len * sizeof(WCHAR) ); + len = MultiByteToWideChar( cp, 0, lpBuffer, nNumberOfCharsToWrite, NULL,0 ); + buf = malloc( TSIZE(len) ); if (buf == NULL) { if (lpNumberOfCharsWritten != NULL) @@ -1225,39 +1423,8 @@ WINAPI MyWriteConsoleA( HANDLE hCon, LPCVOID lpBuffer, // "character" count. This causes some programs to think not everything // was written, so the difference is sent again. Fudge the (presumably) // correct count. - TCHAR env[2048]; - if (GetEnvironmentVariable( L"ANSICON_API", env, lenof(env) )) - { - BOOL not; - - not = (*env == '!'); - if (not && env[1] == '\0') - { - *lpNumberOfCharsWritten = nNumberOfCharsToWrite; - } - else - { - TCHAR path[MAX_PATH]; - LPTSTR name, exe; - - GetModuleFileName( NULL, path, lenof(path) ); - name = wcsrchr( path, '\\' ); - if (name == NULL) - name = path; - else - ++name; - exe = wcsrchr( name, '.' ); - if (exe != NULL && exe != name) - *exe = '\0'; - for (exe = wcstok( env + not, L";" ); exe; exe = wcstok( NULL, L";" )) - { - if (_wcsicmp( name, exe ) == 0) - break; - } - if ((exe && !not) || (!exe && not)) - *lpNumberOfCharsWritten = nNumberOfCharsToWrite; - } - } + if (search_env( L"ANSICON_API", prog )) + *lpNumberOfCharsWritten = nNumberOfCharsToWrite; } return rc; } @@ -1278,7 +1445,7 @@ WINAPI MyWriteConsoleW( HANDLE hCon, LPCVOID lpBuffer, DWORD Mode; if (GetConsoleMode( hCon, &Mode ) && (Mode & ENABLE_PROCESSED_OUTPUT)) { - DEBUGSTR( L"\33WriteConsoleW: %lu \"%.*s\"", + DEBUGSTR( 4, L"\33WriteConsoleW: %lu \"%.*s\"", nNumberOfCharsToWrite, nNumberOfCharsToWrite, lpBuffer ); return ParseAndPrintString( hCon, lpBuffer, nNumberOfCharsToWrite, @@ -1300,7 +1467,7 @@ WINAPI MyWriteFile( HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, DWORD Mode; if (GetConsoleMode( hFile, &Mode ) && (Mode & ENABLE_PROCESSED_OUTPUT)) { - DEBUGSTR( L"WriteFile->" ); + DEBUGSTR( 4, L"WriteFile->" ); return MyWriteConsoleA( hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, @@ -1344,6 +1511,13 @@ void set_ansicon( PCONSOLE_SCREEN_BUFFER_INFO pcsbi ) DWORD WINAPI MyGetEnvironmentVariableA( LPCSTR lpName, LPSTR lpBuffer, DWORD nSize ) { + if (lstrcmpiA( lpName, "ANSICON_VER" ) == 0) + { + if (nSize < sizeof(PVEREA)) + return sizeof(PVEREA); + memcpy( lpBuffer, PVEREA, sizeof(PVEREA) ); + return sizeof(PVEREA) - 1; + } if (lstrcmpiA( lpName, "ANSICON" ) == 0) set_ansicon( NULL ); return GetEnvironmentVariableA( lpName, lpBuffer, nSize ); @@ -1352,6 +1526,13 @@ WINAPI MyGetEnvironmentVariableA( LPCSTR lpName, LPSTR lpBuffer, DWORD nSize ) DWORD WINAPI MyGetEnvironmentVariableW( LPCWSTR lpName, LPWSTR lpBuffer, DWORD nSize ) { + if (lstrcmpi( lpName, L"ANSICON_VER" ) == 0) + { + if (nSize < lenof(PVERE)) + return lenof(PVERE); + memcpy( lpBuffer, PVERE, sizeof(PVERE) ); + return lenof(PVERE) - 1; + } if (lstrcmpi( lpName, L"ANSICON" ) == 0) set_ansicon( NULL ); return GetEnvironmentVariableW( lpName, lpBuffer, nSize ); @@ -1383,10 +1564,6 @@ HookFn Hooks[] = { //----------------------------------------------------------------------------- void OriginalAttr( void ) { - static const char attr2ansi[8] = // map console attribute to ANSI number - { - 0, 4, 2, 6, 1, 5, 3, 7 - }; HANDLE hConOut; CONSOLE_SCREEN_BUFFER_INFO csbi; @@ -1396,11 +1573,44 @@ void OriginalAttr( void ) if (!GetConsoleScreenBufferInfo( hConOut, &csbi )) csbi.wAttributes = 7; CloseHandle( hConOut ); - foreground = org_fg = attr2ansi[csbi.wAttributes & 7]; - background = org_bg = attr2ansi[(csbi.wAttributes >> 4) & 7]; - bold = org_bold = csbi.wAttributes & FOREGROUND_INTENSITY; - underline = org_ul = csbi.wAttributes & BACKGROUND_INTENSITY; + if (s_flag == GRM_INIT && s_pid == GetCurrentProcessId()) + { + s_flag = 0; + grm = s_grm; + } + else + { + if (GetEnvironmentVariable( L"ANSICON_REVERSE", NULL, 0 )) + { + SetEnvironmentVariable( L"ANSICON_REVERSE", NULL ); + grm.reverse = TRUE; + grm.foreground = attr2ansi[(csbi.wAttributes >> 4) & 7]; + grm.background = attr2ansi[csbi.wAttributes & 7]; + grm.bold = (csbi.wAttributes & BACKGROUND_INTENSITY) >> 4; + grm.underline = (csbi.wAttributes & FOREGROUND_INTENSITY) << 4; + } + else + { + grm.foreground = attr2ansi[csbi.wAttributes & 7]; + grm.background = attr2ansi[(csbi.wAttributes >> 4) & 7]; + grm.bold = csbi.wAttributes & FOREGROUND_INTENSITY; + grm.underline = csbi.wAttributes & BACKGROUND_INTENSITY; + } + } + if (!GetEnvironmentVariable( L"ANSICON_DEF", NULL, 0 )) + { + TCHAR def[4]; + LPTSTR a = def; + if (grm.reverse) + { + *a++ = '-'; + csbi.wAttributes = ((csbi.wAttributes >> 4) & 15) + | ((csbi.wAttributes & 15) << 4); + } + wsprintf( a, L"%X", csbi.wAttributes & 255 ); + SetEnvironmentVariable( L"ANSICON_DEF", def ); + } set_ansicon( &csbi ); } @@ -1417,11 +1627,21 @@ BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved ) BOOL bResult = TRUE; HMODULE api; PHookFn hook; + TCHAR logstr[4]; if (dwReason == DLL_PROCESS_ATTACH) { + *logstr = '\0'; + GetEnvironmentVariable( L"ANSICON_LOG", logstr, lenof(logstr) ); + log_level = _wtoi( logstr ); + prog = get_program_name( NULL ); +#ifdef _WIN64 + hDllNameType = hDllName - 6 + +#endif + GetModuleFileName( hInstance, hDllName, lenof(hDllName) ); + hDllInstance = hInstance; // save Dll instance handle - DEBUGSTR( L"hDllInstance = %p", hDllInstance ); + DEBUGSTR( 1, L"hDllInstance = %p", hDllInstance ); // Get the entry points to the original functions. hKernel = GetModuleHandleA( APIKernel ); @@ -1441,14 +1661,17 @@ BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved ) { if (lpReserved == NULL) { - DEBUGSTR( L"Unloading" ); + DEBUGSTR( 1, L"Unloading" ); HookAPIAllMod( Hooks, TRUE ); } else { - DEBUGSTR( L"Terminating" ); + DEBUGSTR( 1, L"Terminating" ); + s_pid = GetCurrentProcessId(); + s_grm = grm; + s_flag = GRM_EXIT; } } - return( bResult ); + return bResult; } diff --git a/COPYING.MinGW-w64-runtime.txt b/COPYING.MinGW-w64-runtime.txt index 9544220..3d11e87 100644 --- a/COPYING.MinGW-w64-runtime.txt +++ b/COPYING.MinGW-w64-runtime.txt @@ -50,6 +50,57 @@ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +======================================== +getopt, getopt_long, and getop_long_only +======================================== + +Copyright (c) 2002 Todd C. Miller + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +Sponsored in part by the Defense Advanced Research Projects +Agency (DARPA) and Air Force Research Laboratory, Air Force +Materiel Command, USAF, under agreement number F39502-99-1-0512. + + * * * * * * * + +Copyright (c) 2000 The NetBSD Foundation, Inc. +All rights reserved. + +This code is derived from software contributed to The NetBSD Foundation +by Dieter Baron and Thomas Klausner. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + =============================================================== gdtoa: Converting between IEEE floating point numbers and ASCII @@ -167,3 +218,23 @@ permission from the author and then add a license into the Cephes files in MinGW runtime. At least on follow-up it is marked that debian sees the version a-like BSD one. As MinGW.org (where those cephes parts are coming from) distributes them now over 6 years, it should be fine. + +=================================== +Headers and IDLs imported from Wine +=================================== + +Some header and IDL files were imported from the Wine project. These files +are prominent maked in source. Their copyright belongs to contributors and +they are distributed under LGPL license. + +Disclaimer + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. diff --git a/ansi.rc b/ansi.rc index 8bef2dc..16a4585 100644 --- a/ansi.rc +++ b/ansi.rc @@ -5,6 +5,7 @@ */ #include +#include "version.h" #ifdef _WIN64 # define BITS "64" @@ -13,8 +14,8 @@ #endif 1 VERSIONINFO -FILEVERSION 1,4,0,0 -PRODUCTVERSION 1,4,0,0 +FILEVERSION PVERB +PRODUCTVERSION PVERB FILEOS VOS_NT FILETYPE VFT_DLL { @@ -25,12 +26,12 @@ FILETYPE VFT_DLL VALUE "Comments", "http://ansicon.adoxa.cjb.net/" VALUE "CompanyName", "Jason Hood" VALUE "FileDescription", "ANSI Console" - VALUE "FileVersion", "1.40" + VALUE "FileVersion", PVERSA VALUE "InternalName", "ANSI" BITS VALUE "LegalCopyright", "Freeware" VALUE "OriginalFilename", "ANSI" BITS ".dll" VALUE "ProductName", "ANSICON" - VALUE "ProductVersion", "1.40" + VALUE "ProductVersion", PVERSA } } diff --git a/ansicon.c b/ansicon.c index 2323a88..b79a2d3 100644 --- a/ansicon.c +++ b/ansicon.c @@ -47,18 +47,24 @@ make -p more robust; inject into GUI processes; -i implies -p. + + v1.50, 7 to 14 December, 2011: + -u does not imply -p; + add the PID to the debugging output; + use ANSICON_VER to test if already installed; + always place first in AutoRun; + logging is always available, controlled by ANSICON_LOG environment variable; + only restore the original color after program/echo/type; + return program's exit code. */ -#define PVERS L"1.40" -#define PDATE L"1 March, 2011" +#define PDATE L"14 December, 2011" #include "ansicon.h" -#include +#include "version.h" #include #include #include -#include -#include #ifdef __MINGW32__ int _CRT_glob = 0; @@ -79,33 +85,33 @@ int _CRT_glob = 0; void help( void ); void display( LPCTSTR, BOOL ); +void print_error( LPCTSTR, ... ); LPTSTR skip_spaces( LPTSTR ); -LPTSTR skip_arg( LPTSTR ); +void get_arg( LPTSTR, LPTSTR*, LPTSTR* ); void process_autorun( TCHAR ); BOOL find_proc_id( HANDLE snap, DWORD id, LPPROCESSENTRY32 ppe ); -BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi ); +BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi, LPTSTR ); // Find the name of the DLL and inject it. -BOOL Inject( LPPROCESS_INFORMATION ppi ) +BOOL Inject( LPPROCESS_INFORMATION ppi, BOOL* gui, LPCTSTR app ) { DWORD len; WCHAR dll[MAX_PATH]; int type; -#if (MYDEBUG > 0) - if (GetModuleFileNameEx( ppi->hProcess, NULL, dll, lenof(dll) )) - DEBUGSTR( L"%s", dll ); -#endif - type = ProcessType( ppi ); + DEBUGSTR( 1, L"%s", app ); + type = ProcessType( ppi, gui ); if (type == 0) + { + fwprintf( stderr, L"ANSICON: %s: unsupported process.\n", app ); return FALSE; + } - len = GetModuleFileName( NULL, dll, lenof(dll) ); - while (dll[len-1] != '\\') - --len; + len = (DWORD)(prog - prog_path); + memcpy( dll, prog_path, TSIZE(len) ); #ifdef _WIN64 wsprintf( dll + len, L"ANSI%d.dll", type ); if (type == 32) @@ -145,288 +151,302 @@ DWORD CtrlHandler( DWORD event ) } -//int _tmain( int argc, TCHAR* argv[] ) int main( void ) { STARTUPINFO si; PROCESS_INFORMATION pi; - TCHAR* cmd; - BOOL option; - BOOL opt_m; + LPTSTR argv, arg, cmd; + TCHAR logstr[4]; BOOL installed; + BOOL shell, run, gui; HMODULE ansi; + DWORD len; int rc = 0; - int argc; - LPWSTR* argv = CommandLineToArgvW( GetCommandLine(), &argc ); + argv = GetCommandLine(); + len = (DWORD)wcslen( argv ) + 1; + if (len < MAX_PATH) + len = MAX_PATH; + arg = malloc( TSIZE(len) ); + get_arg( arg, &argv, &cmd ); // skip the program name + get_arg( arg, &argv, &cmd ); - if (argc > 1) + if (*arg) { - if (lstrcmp( argv[1], L"--help" ) == 0 || - (argv[1][0] == '-' && (argv[1][1] == '?' || argv[1][1] == 'h')) || - (argv[1][0] == '/' && argv[1][1] == '?')) + if (wcscmp( arg, L"/?" ) == 0 || + wcscmp( arg, L"--help" ) == 0) { help(); return rc; } - if (lstrcmp( argv[1], L"--version" ) == 0) + if (wcscmp( arg, L"--version" ) == 0) { _putws( L"ANSICON (" BITS L"-bit) version " PVERS L" (" PDATE L")." ); return rc; } } -#if (MYDEBUG > 1) - DEBUGSTR( NULL ); // create a new file -#endif + prog = get_program_name( NULL ); + *logstr = '\0'; + GetEnvironmentVariable( L"ANSICON_LOG", logstr, lenof(logstr) ); + log_level = _wtoi( logstr ); + if (log_level && !(log_level & 8)) + DEBUGSTR( 1, NULL ); // create a new file - option = (argc > 1 && argv[1][0] == '-'); - if (option && (towlower( argv[1][1] ) == 'i' || - towlower( argv[1][1] ) == 'u')) + installed = (GetEnvironmentVariable( L"ANSICON_VER", NULL, 0 ) != 0); + // If it's already installed, remove it. This serves two purposes: preserves + // the parent's GRM; and unconditionally injects into GUI, without having to + // worry about ANSICON_GUI. + if (installed) { - process_autorun( argv[1][1] ); - argv[1][1] = 'p'; + fputws( L"\33[m", stdout ); + FreeLibrary( GetModuleHandle( L"ANSI" BITS L".dll" ) ); } + shell = run = TRUE; get_original_attr(); - opt_m = FALSE; - if (option && argv[1][1] == 'm') + while (*arg == '-') { - WORD attr = 7; - if (iswxdigit( argv[1][2] )) + switch (arg[1]) { - attr = iswdigit( argv[1][2] ) ? argv[1][2] - '0' - : (argv[1][2] | 0x20) - 'a' + 10; - if (iswxdigit( argv[1][3])) - { - attr <<= 4; - attr |= iswdigit( argv[1][3] ) ? argv[1][3] - '0' - : (argv[1][3] | 0x20) - 'a' + 10; - } - } - SetConsoleTextAttribute( hConOut, attr ); + case 'l': + SetEnvironmentVariable( L"ANSICON_LOG", arg + 2 ); + log_level = _wtoi( arg + 2 ); + if (!(log_level & 8)) // unless told otherwise + DEBUGSTR( 1, NULL ); // create a new file + break; - opt_m = TRUE; - ++argv; - --argc; - option = (argc > 1 && argv[1][0] == '-'); - } + case 'i': + case 'I': + case 'u': + case 'U': + shell = FALSE; + process_autorun( arg[1] ); + if (arg[1] == 'u' || arg[1] == 'U') + break; + // else fall through - installed = (GetEnvironmentVariable( L"ANSICON", NULL, 0 ) != 0); - - if (option && argv[1][1] == 'p') - { - // If it's already installed, there's no need to do anything. - if (installed) - ; - else if (GetParentProcessInfo( &pi )) - { - pi.hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId ); - pi.hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, pi.dwThreadId ); - SuspendThread( pi.hThread ); - if (!Inject( &pi )) - { - _putws( L"ANSICON: parent process type is not supported." ); - rc = 1; - } - ResumeThread( pi.hThread ); - CloseHandle( pi.hThread ); - CloseHandle( pi.hProcess ); - } - else - { - _putws( L"ANSICON: could not obtain the parent process." ); - rc = 1; - } - } - else - { - ansi = 0; - if (!installed) - { - ansi = LoadLibrary( L"ANSI" BITS L".dll" ); - if (!ansi) - { - fputws( L"ANSICON: failed to load ANSI" BITS L".dll.\n", stderr ); - rc = 1; - } - } - - if (option && (argv[1][1] == 't' || argv[1][1] == 'T')) - { - BOOL title = (argv[1][1] == 'T'); - if (argc == 2) - { - argv[2] = L"-"; - ++argc; - } - for (; argc > 2; ++argv, --argc) - { - if (title) - wprintf( L"==> %s <==\n", argv[2] ); - display( argv[2], title ); - if (title) - putwchar( '\n' ); - } - } - else - { - // Retrieve the original command line, skipping our name and the option. - cmd = skip_spaces( skip_arg( skip_spaces( GetCommandLine() ) ) ); - if (opt_m) - cmd = skip_spaces( skip_arg( cmd ) ); - - if (cmd[0] == '-' && (cmd[1] == 'e' || cmd[1] == 'E')) - { - fputws( cmd + 3, stdout ); - if (cmd[1] == 'e') - putwchar( '\n' ); - } - else if (!_isatty( 0 ) && *cmd == '\0') - { - display( L"-", FALSE ); - } - else - { - if (*cmd == '\0') + case 'p': + shell = FALSE; + // If it's already installed, there's no need to do anything. + if (installed) { - cmd = _wgetenv( L"ComSpec" ); - if (cmd == NULL) - cmd = L"cmd"; + DEBUGSTR( 1, L"Already installed" ); } - - ZeroMemory( &si, sizeof(si) ); - si.cb = sizeof(si); - if (CreateProcess( NULL, cmd, NULL,NULL, TRUE, 0, NULL,NULL, &si, &pi )) + else if (GetParentProcessInfo( &pi, arg )) { - BOOL console = FALSE; - TCHAR name[MAX_PATH]; - DWORD rc; - CoInitialize( NULL ); - do - { - // When I first tried doing this, it took a little while to - // succeed. Testing again shows it works immediately - perhaps the - // CoInitialize introduces enough of a delay. Still, play it safe - // and keep trying. And if you're wondering why I do it at all, - // ProcessType may detect GUI, even for a console process. That's - // fine after injection (including -p), but not here. We *need* to - // suspend our own execution whilst running the child, otherwise - // bad things happen (besides which, I want to restore the original - // attributes when the child exits). - if (GetModuleFileNameEx( pi.hProcess, NULL, name, lenof(name) )) - { - DWORD_PTR info; - info = SHGetFileInfo( name, 0, NULL, 0, SHGFI_EXETYPE ); - if (info == 0x00004550) // console PE - console = TRUE; - DEBUGSTR( L"%s", name ); - DEBUGSTR( L" %s (%p)", (console) ? L"Console" : L"Not console", - info ); - break; - } - Sleep( 10 ); - } while (GetExitCodeProcess( pi.hProcess, &rc ) && - rc == STILL_ACTIVE); - CoUninitialize(); - if (console) - { - SetConsoleCtrlHandler( (PHANDLER_ROUTINE)CtrlHandler, TRUE ); - WaitForSingleObject( pi.hProcess, INFINITE ); - } - CloseHandle( pi.hProcess ); + pi.hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId); + pi.hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, pi.dwThreadId ); + SuspendThread( pi.hThread ); + if (!Inject( &pi, &gui, arg )) + rc = 1; + ResumeThread( pi.hThread ); CloseHandle( pi.hThread ); + CloseHandle( pi.hProcess ); } else { - *skip_arg( cmd ) = '\0'; - wprintf( L"ANSICON: '%s' could not be executed.\n", cmd ); + fputws( L"ANSICON: could not obtain the parent process.\n", stderr ); rc = 1; } - } - } + break; - if (ansi) - FreeLibrary( ansi ); + case 'm': + { + int a = wcstol( arg + 2, NULL, 16 ); + if (a == 0) + a = (arg[2] == '-') ? -7 : 7; + if (a < 0) + { + SetEnvironmentVariable( L"ANSICON_REVERSE", L"1" ); + a = -a; + a = ((a >> 4) & 15) | ((a & 15) << 4); + } + SetConsoleTextAttribute( hConOut, a ); + SetEnvironmentVariable( L"ANSICON_DEF", NULL ); + break; + } + + case 'e': + case 'E': + case 't': + case 'T': + run = FALSE; + ++arg; + goto arg_out; + } + get_arg( arg, &argv, &cmd ); + } +arg_out: + if (run && *cmd == '\0') + { + if (!_isatty( 0 )) + { + *arg = 't'; + run = FALSE; + } + else if (!shell) + run = FALSE; } - set_original_attr(); + if (run) + { + if (*cmd == '\0') + { + cmd = _wgetenv( L"ComSpec" ); + if (cmd == NULL) + cmd = L"cmd"; + arg = cmd; + } + + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + if (CreateProcess( NULL, cmd, NULL, NULL, TRUE, CREATE_SUSPENDED, + NULL, NULL, &si, &pi )) + { + Inject( &pi, &gui, arg ); + ResumeThread( pi.hThread ); + if (!gui) + { + SetConsoleCtrlHandler( (PHANDLER_ROUTINE)CtrlHandler, TRUE ); + WaitForSingleObject( pi.hProcess, INFINITE ); + GetExitCodeProcess( pi.hProcess, (LPDWORD)(LPVOID)&rc ); + } + CloseHandle( pi.hProcess ); + CloseHandle( pi.hThread ); + } + else + { + print_error( arg, arg ); + rc = 1; + } + } + else if (*arg) + { + ansi = LoadLibrary( L"ANSI" BITS L".dll" ); + if (ansi == NULL) + { + print_error( L"ANSI" BITS L".dll" ); + rc = 1; + } + + if (*arg == 'e' || *arg == 'E') + { + cmd += 2; + if (*cmd == ' ' || *cmd == '\t') + ++cmd; + fputws( cmd, stdout ); + if (*arg == 'e') + putwchar( '\n' ); + } + else // (*arg == 't' || *arg == 'T') + { + BOOL title = (*arg == 'T'); + get_arg( arg, &argv, &cmd ); + if (*arg == '\0') + wcscpy( arg, L"-" ); + do + { + if (title) + { + wprintf( L"==> %s <==\n", arg ); + display( arg, title ); + putwchar( '\n' ); + } + else + display( arg, title ); + get_arg( arg, &argv, &cmd ); + } while (*arg); + } + + FreeLibrary( ansi ); + } + + if (run || *arg) + set_original_attr(); + else + CloseHandle( hConOut ); + return rc; } -void print_error( LPCTSTR name, BOOL title ) -{ - LPTSTR errmsg; - - FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, - NULL, GetLastError(), 0, (LPTSTR)(LPVOID)&errmsg, 0, NULL ); - if (!title) - wprintf( L"ANSICON: %s: ", name ); - fputws( errmsg, stdout ); - LocalFree( errmsg ); -} - - // Display a file. void display( LPCTSTR name, BOOL title ) { - HANDLE file; - int c; - LARGE_INTEGER size, offset; + HANDLE in, out; + BOOL pipe; + char buf[8192]; + DWORD len; - // Handle the pipe differently. if (*name == '-' && name[1] == '\0') { - if (title) - putwchar( '\n' ); - while ((c = getchar()) != EOF) - putchar( c ); - return; + pipe = TRUE; + in = GetStdHandle( STD_INPUT_HANDLE ); } - - file = CreateFile( name, GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, 0, NULL ); - if (file == INVALID_HANDLE_VALUE) + else { - print_error( name, title ); - return; - } - - GetFileSizeEx( file, &size ); - if (size.QuadPart != 0) - { - HANDLE map = CreateFileMapping( file, NULL, PAGE_READONLY, 0, 0, NULL ); - if (map) + pipe = FALSE; + in = CreateFile( name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL ); + if (in == INVALID_HANDLE_VALUE) { - if (title) - putwchar( '\n' ); - offset.QuadPart = 0; - do - { - DWORD len = (size.QuadPart > 65536) ? 65536 : size.LowPart; - LPVOID mem = MapViewOfFile( map, FILE_MAP_READ, offset.HighPart, - offset.LowPart, len ); - if (mem) - { - fwrite( mem, 1, len, stdout ); - UnmapViewOfFile( mem ); - } - else - { - print_error( name, title ); - break; - } - offset.QuadPart += len; - size.QuadPart -= len; - } while (size.QuadPart); - CloseHandle( map ); + print_error( name ); + return; } - else - print_error( name, title ); } - CloseHandle( file ); + if (title) + { + putwchar( '\n' ); + // Need to flush, otherwise it's written *after* STD_OUTPUT_HANDLE should + // it be redirected. + fflush( stdout ); + } + out = GetStdHandle( STD_OUTPUT_HANDLE ); + for (;;) + { + if (!ReadFile( in, buf, sizeof(buf), &len, NULL )) + { + if (GetLastError() != ERROR_BROKEN_PIPE) + print_error( name ); + break; + } + if (len == 0) + break; + WriteFile( out, buf, len, &len, NULL ); + } + if (!pipe) + CloseHandle( in ); +} + + +void print_error( LPCTSTR name, ... ) +{ + LPTSTR errmsg = NULL; + DWORD err = GetLastError(); + va_list arg; + + if (err == ERROR_BAD_EXE_FORMAT) + { + // This error requires an argument, which is a duplicate of name. + va_start( arg, name ); + FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL, err, 0, (LPTSTR)(LPVOID)&errmsg, 0, &arg ); + va_end( arg ); + fwprintf( stderr, L"ANSICON: %s", errmsg ); + } + else + { + FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL, err, 0, (LPTSTR)(LPVOID)&errmsg, 0, NULL ); + // Just in case there are other messages requiring args... + if (errmsg == NULL) + fwprintf( stderr, L"ANSICON: %s: Error %lu.\n", name, err ); + else + fwprintf( stderr, L"ANSICON: %s: %s", name, errmsg ); + } + LocalFree( errmsg ); } @@ -434,16 +454,19 @@ void display( LPCTSTR name, BOOL title ) void process_autorun( TCHAR cmd ) { HKEY cmdkey; - TCHAR ansicon[MAX_PATH+8]; + TCHAR ansicon[MAX_PATH+80]; + TCHAR logstr[80]; LPTSTR autorun, ansirun; DWORD len, type, exist; BOOL inst; - len = GetModuleFileName( NULL, ansicon+2, MAX_PATH ); - ansicon[0] = '&'; - ansicon[1] = ansicon[2+len] = '"'; - wcscpy( ansicon + 3+len, L" -p" ); - len += 6; + if (log_level) + _snwprintf( logstr, lenof(logstr), L"set ANSICON_LOG=%d&", log_level ); + else + *logstr = '\0'; + len = TSIZE(_snwprintf( ansicon, lenof(ansicon), + L"(if %%ANSICON_VER%%==^%%ANSICON_VER^%% %s\"%s\" -p)", + logstr, prog_path ) + 1); inst = (towlower( cmd ) == 'i'); RegCreateKeyEx( (iswlower( cmd )) ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, @@ -452,48 +475,58 @@ void process_autorun( TCHAR cmd ) &cmdkey, &exist ); exist = 0; RegQueryValueEx( cmdkey, AUTORUN, NULL, NULL, NULL, &exist ); - autorun = malloc( exist + len * sizeof(TCHAR) + sizeof(TCHAR) ); - // Let's assume there's sufficient memory. - if (exist > sizeof(TCHAR)) + if (exist == 0) { - exist += sizeof(TCHAR); + if (inst) + RegSetValueEx( cmdkey, AUTORUN, 0, REG_SZ, (PBYTE)ansicon, len ); + } + else + { + // Let's assume there's sufficient memory. + autorun = malloc( exist + len ); RegQueryValueEx( cmdkey, AUTORUN, NULL, &type, (PBYTE)autorun, &exist ); - ansirun = wcsstr( autorun, ansicon+1 ); + // Remove the existing command, if present. + ansirun = wcsstr( autorun, L"(if %ANSICON_VER%" ); + if (ansirun != NULL) + { + LPTSTR tmp = wcschr( ansirun, '"' ); // opening quote + tmp = wcschr( tmp + 1, '"' ); // closing quote + tmp = wcschr( tmp + 1, ')' ); // closing bracket + if (*++tmp == '&') + ++tmp; + if (*tmp == '&') + ++tmp; + if (*tmp == '\0') + { + if (ansirun > autorun && ansirun[-1] == '&') + --ansirun; + if (ansirun > autorun && ansirun[-1] == '&') + --ansirun; + } + wcscpy( ansirun, tmp ); + exist = TSIZE((DWORD)wcslen( autorun ) + 1); + } if (inst) { - if (!ansirun) + if (exist == sizeof(TCHAR)) + RegSetValueEx( cmdkey, AUTORUN, 0, REG_SZ, (PBYTE)ansicon, len ); + else { - wcscpy( (LPTSTR)((PBYTE)autorun + exist - sizeof(TCHAR)), ansicon ); - RegSetValueEx( cmdkey, AUTORUN, 0, type, (PBYTE)autorun, - exist + len*sizeof(TCHAR) ); + memmove( (PBYTE)autorun + len, autorun, exist ); + memcpy( autorun, ansicon, len ); + ((PBYTE)autorun)[len-sizeof(TCHAR)] = '&'; + RegSetValueEx( cmdkey, AUTORUN, 0, type, (PBYTE)autorun, exist+len ); } } else { - if (ansirun) - { - if (ansirun == autorun && exist == len*sizeof(TCHAR)) - RegDeleteValue( cmdkey, AUTORUN ); - else - { - if (ansirun > autorun && ansirun[-1] == '&') - --ansirun; - else if (autorun[len-1] != '&') - --len; - memcpy( ansirun, ansirun + len, exist - len*sizeof(TCHAR) ); - RegSetValueEx( cmdkey, AUTORUN, 0, type, (PBYTE)autorun, - exist - len*sizeof(TCHAR) ); - } - } + if (exist == sizeof(TCHAR)) + RegDeleteValue( cmdkey, AUTORUN ); + else + RegSetValueEx( cmdkey, AUTORUN, 0, type, (PBYTE)autorun, exist ); } + free( autorun ); } - else if (inst) - { - RegSetValueEx( cmdkey, AUTORUN, 0, REG_SZ, (PBYTE)(ansicon+1), - len*sizeof(TCHAR) ); - } - - free( autorun ); RegCloseKey( cmdkey ); } @@ -513,20 +546,19 @@ BOOL find_proc_id( HANDLE snap, DWORD id, LPPROCESSENTRY32 ppe ) // Obtain the process and thread identifiers of the parent process. -BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi ) +BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi, LPTSTR name ) { HANDLE hSnap; PROCESSENTRY32 pe; THREADENTRY32 te; - DWORD id = GetCurrentProcessId(); BOOL fOk; - hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS|TH32CS_SNAPTHREAD, id ); + hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS|TH32CS_SNAPTHREAD, 0 ); if (hSnap == INVALID_HANDLE_VALUE) return FALSE; - find_proc_id( hSnap, id, &pe ); + find_proc_id( hSnap, GetCurrentProcessId(), &pe ); if (!find_proc_id( hSnap, pe.th32ParentProcessID, &pe )) { CloseHandle( hSnap ); @@ -542,38 +574,53 @@ BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi ) ppi->dwProcessId = pe.th32ProcessID; ppi->dwThreadId = te.th32ThreadID; + wcscpy( name, pe.szExeFile ); return fOk; } -// Return the first non-space character from cmd. -LPTSTR skip_spaces( LPTSTR cmd ) +// Return the first non-space character from arg. +LPTSTR skip_spaces( LPTSTR arg ) { - while ((*cmd == ' ' || *cmd == '\t') && *cmd != '\0') - ++cmd; + while (*arg == ' ' || *arg == '\t') + ++arg; - return cmd; + return arg; } -// Return the end of the argument at cmd. -LPTSTR skip_arg( LPTSTR cmd ) +// Retrieve an argument from the command line. cmd gets the existing argv; argv +// is ready for the next argument. +void get_arg( LPTSTR arg, LPTSTR* argv, LPTSTR* cmd ) { - while (*cmd != ' ' && *cmd != '\t' && *cmd != '\0') - { - if (*cmd == '"') - { - do - ++cmd; - while (*cmd != '"' && *cmd != '\0'); - if (*cmd == '\0') - --cmd; - } - ++cmd; - } + LPTSTR line; - return cmd; + line = *cmd = skip_spaces( *argv ); + while (*line != '\0') + { + if (*line == ' ' || *line == '\t') + { + ++line; + break; + } + if (*line == '"') + { + while (*++line != '\0') + { + if (*line == '"') + { + ++line; + break; + } + *arg++ = *line; + } + } + else + *arg++ = *line++; + } + *arg = '\0'; + *argv = line; } @@ -590,10 +637,12 @@ L"Process ANSI escape sequences in Windows console programs.\n" L"Process ANSI escape sequences in Win32 console programs.\n" #endif L"\n" -L"ansicon -i|I | -u|U\n" -L"ansicon [-m[]] [-p | -e|E string | -t|T [file(s)] | program [args]]\n" +L"ansicon [-l] [-i] [-I] [-u] [-U] [-m[]] [-p]\n" +L" [-e|E string | -t|T [file(s)] | program [args]]\n" L"\n" -L" -i\t\tinstall - add ANSICON to the AutoRun entry (implies -p)\n" +L" -l\t\tset the logging level (1=process, 2=module, 3=function,\n" +L" \t\t +4=output, +8=append) for program (-p is unaffected)\n" +L" -i\t\tinstall - add ANSICON to the AutoRun entry (also implies -p)\n" L" -u\t\tuninstall - remove ANSICON from the AutoRun entry\n" L" -I -U\t\tuse local machine instead of current user\n" L" -m\t\tuse grey on black (\"monochrome\") or as default color\n" @@ -605,6 +654,7 @@ L" -T\t\tdisplay files, name first, blank line before and after\n" L" program\trun the specified program\n" L" nothing\trun a new command processor, or display stdin if redirected\n" L"\n" -L" is one or two hexadecimal digits; please use \"COLOR /?\" for details." - ); +L" is one or two hexadecimal digits; please use \"COLOR /?\" for details.\n" +L"It may start with '-' to reverse foreground and background (but not for -p)." + ); } diff --git a/ansicon.h b/ansicon.h index 608d062..8819186 100644 --- a/ansicon.h +++ b/ansicon.h @@ -18,29 +18,30 @@ #include #define lenof(array) (sizeof(array)/sizeof(*(array))) +#define TSIZE(size) ((size) * sizeof(TCHAR)) -int ProcessType( LPPROCESS_INFORMATION ); +typedef struct +{ + BYTE foreground; // ANSI base color (0 to 7; add 30) + BYTE background; // ANSI base color (0 to 7; add 40) + BYTE bold; // console FOREGROUND_INTENSITY bit + BYTE underline; // console BACKGROUND_INTENSITY bit + BYTE rvideo; // swap foreground/bold & background/underline + BYTE concealed; // set foreground/bold to background/underline + BYTE reverse; // swap console foreground & background attributes +} GRM, *PGRM; // Graphic Rendition Mode + + +int ProcessType( LPPROCESS_INFORMATION, BOOL* ); void InjectDLL32( LPPROCESS_INFORMATION, LPCTSTR ); void InjectDLL64( LPPROCESS_INFORMATION, LPCTSTR ); +extern TCHAR prog_path[MAX_PATH]; +extern LPTSTR prog; +LPTSTR get_program_name( LPTSTR ); -// ========== Auxiliary debug function - -#ifndef MYDEBUG -# define MYDEBUG 0 // 0 - no debugging - // 1 - use OutputDebugString - // 2 - use %temp%\ansicon.log -#endif - -#if (MYDEBUG > 0) - void DEBUGSTR( LPTSTR szFormat, ... ); -#else -# if defined(_MSC_VER) && _MSC_VER <= 1400 - #define DEBUGSTR (void) -# else -# define DEBUGSTR(...) -# endif -#endif +extern int log_level; +void DEBUGSTR( int level, LPTSTR szFormat, ... ); #endif diff --git a/ansicon.rc b/ansicon.rc index 6e53bae..09b03c1 100644 --- a/ansicon.rc +++ b/ansicon.rc @@ -5,10 +5,11 @@ */ #include +#include "version.h" 1 VERSIONINFO -FILEVERSION 1,4,0,0 -PRODUCTVERSION 1,4,0,0 +FILEVERSION PVERB +PRODUCTVERSION PVERB FILEOS VOS_NT FILETYPE VFT_APP { @@ -19,12 +20,12 @@ FILETYPE VFT_APP VALUE "Comments", "http://ansicon.adoxa.cjb.net/" VALUE "CompanyName", "Jason Hood" VALUE "FileDescription", "ANSI Console" - VALUE "FileVersion", "1.40" + VALUE "FileVersion", PVERSA VALUE "InternalName", "ansicon" VALUE "LegalCopyright", "Freeware" VALUE "OriginalFilename", "ansicon.exe" VALUE "ProductName", "ANSICON" - VALUE "ProductVersion", "1.40" + VALUE "ProductVersion", PVERSA } } diff --git a/debugstr.c b/debugstr.c deleted file mode 100644 index b4f3aac..0000000 --- a/debugstr.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - debugstr.c - Auxiliary debug functionality. -*/ - -#include "ansicon.h" - -#if (MYDEBUG > 1) -char tempfile[MAX_PATH]; -#endif - -#if (MYDEBUG > 0) -void DEBUGSTR( LPTSTR szFormat, ... ) // sort of OutputDebugStringf -{ - TCHAR szBuffer[1024], szEscape[1024]; - va_list pArgList; - -#if (MYDEBUG > 1) - if (*tempfile == '\0') - _snprintf( tempfile, MAX_PATH, "%s\\ansicon.log", getenv( "TEMP" ) ); - if (szFormat == NULL) - { - DeleteFileA( tempfile ); - return; - } -#endif - - va_start( pArgList, szFormat ); - _vsnwprintf( szBuffer, lenof(szBuffer), szFormat, pArgList ); - va_end( pArgList ); - - szFormat = szBuffer; - if (*szFormat == '\33') - { - BOOL first = TRUE; - LPTSTR pos = szEscape; - while (*++szFormat != '\0' && pos < szEscape + lenof(szEscape) - 4) - { - if (*szFormat < 32) - { - *pos++ = '\\'; - switch (*szFormat) - { - case '\a': *pos++ = 'a'; break; - case '\b': *pos++ = 'b'; break; - case '\t': *pos++ = 't'; break; - case '\r': *pos++ = 'r'; break; - case '\n': *pos++ = 'n'; break; - case 27 : *pos++ = 'e'; break; - default: - pos += _snwprintf( pos, 32, L"%.*o", - (szFormat[1] >= '0' && szFormat[1] <= '7') ? 3 : 1, - *szFormat ); - } - } - else if (*szFormat == '"') - { - if (first) - first = FALSE; - else if (szFormat[1] == '\0') - ; - else - *pos++ = '\\'; - *pos++ = '"'; - } - else - { - *pos++ = *szFormat; - } - } - *pos = '\0'; - szFormat = szEscape; - } -#if (MYDEBUG > 1) - { - HANDLE mutex = CreateMutex( NULL, FALSE, L"ANSICON_debug_file" ); - DWORD wait = WaitForSingleObject( mutex, 500 ); - FILE* file = fopen( tempfile, "at" ); // _fmode might be binary - if (file != NULL) - { - TCHAR path[MAX_PATH]; - LPTSTR prog, ext; - GetModuleFileName( NULL, path, lenof(path) ); - prog = wcsrchr( path, '\\' ); - if (prog != NULL) - ++prog; - else - prog = path; - ext = wcsrchr( prog, '.' ); - if (ext != NULL) - *ext = '\0'; - fwprintf( file, L"%s: %s\n", prog, szFormat ); - fclose( file ); - } - if (wait == WAIT_OBJECT_0) - ReleaseMutex( mutex ); - CloseHandle( mutex ); - } -#else - OutputDebugString( szFormat ); -#endif -} -#endif diff --git a/injdll32.c b/injdll32.c index 08d2a16..25ed7ae 100644 --- a/injdll32.c +++ b/injdll32.c @@ -43,17 +43,16 @@ void InjectDLL32( LPPROCESS_INFORMATION ppi, LPCTSTR dll ) LPVOID mem; DWORD mem32; #define CODESIZE 20 - BYTE code[CODESIZE+MAX_PATH*sizeof(TCHAR)]; + BYTE code[CODESIZE+TSIZE(MAX_PATH)]; union { PBYTE pB; PDWORD pL; } ip; - len = lstrlen( dll ) + 1; - if (len > MAX_PATH) + len = TSIZE(lstrlen( dll ) + 1); + if (len > TSIZE(MAX_PATH)) return; - len *= sizeof(TCHAR); if (LLW == 0) { @@ -66,7 +65,7 @@ void InjectDLL32( LPPROCESS_INFORMATION ppi, LPCTSTR dll ) // Assume if one is defined, so is the other. if (Wow64GetThreadContext == 0) { - DEBUGSTR( L"Failed to get pointer to Wow64GetThreadContext.\n" ); + DEBUGSTR( 1, L"Failed to get pointer to Wow64GetThreadContext.\n" ); return; } #endif @@ -76,13 +75,13 @@ void InjectDLL32( LPPROCESS_INFORMATION ppi, LPCTSTR dll ) ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); // ...ANSI32.dll\0 - CopyMemory( code, dll, len - 7*sizeof(TCHAR) ); + CopyMemory( code, dll, len - TSIZE(7) ); // ...ANSI-LLW.exe\0 - CopyMemory( code + len - 7*sizeof(TCHAR), L"-LLW.exe", 9*sizeof(TCHAR) ); + CopyMemory( code + len - TSIZE(7), L"-LLW.exe", TSIZE(9) ); if (!CreateProcess( (LPCTSTR)code, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi )) { - DEBUGSTR( L"Failed to execute \"%s\".\n", (LPCTSTR)code ); + DEBUGSTR( 1, L"Failed to execute \"%s\".\n", (LPCTSTR)code ); return; } WaitForSingleObject( pi.hProcess, INFINITE ); diff --git a/injdll64.c b/injdll64.c index 8b987b0..c46bd46 100644 --- a/injdll64.c +++ b/injdll64.c @@ -29,7 +29,7 @@ void InjectDLL64( LPPROCESS_INFORMATION ppi, LPCTSTR dll ) PDWORD64 pL; } ip; #define CODESIZE 92 - static BYTE code[CODESIZE+MAX_PATH*sizeof(TCHAR)] = { + static BYTE code[CODESIZE+TSIZE(MAX_PATH)] = { 0,0,0,0,0,0,0,0, // original rip 0,0,0,0,0,0,0,0, // LoadLibraryW 0x9C, // pushfq @@ -72,10 +72,9 @@ void InjectDLL64( LPPROCESS_INFORMATION ppi, LPCTSTR dll ) 0, // dword alignment for LLW, fwiw }; - len = lstrlen( dll ) + 1; - if (len > MAX_PATH) + len = TSIZE(lstrlen( dll ) + 1); + if (len > TSIZE(MAX_PATH)) return; - len *= sizeof(TCHAR); CopyMemory( code + CODESIZE, dll, len ); len += CODESIZE; diff --git a/makefile b/makefile index fdd6721..db84cf6 100644 --- a/makefile +++ b/makefile @@ -1,29 +1,32 @@ # Makefile for ANSICON. # Jason Hood, 11 March, 2006. Updated 20 June, 2009. -# I've used TDM64 (gcc 4.5.0), building the 32-bit version in the x86 directory +# I've used TDM64 (gcc 4.6.1), building the 32-bit version in the x86 directory # and the 64-bit version in the x64 directory. MinGW32 (gcc 3.4.5) will also -# build the 32-bit version, but will of course fail on the 64-bit. +# build the 32-bit version. # 19 November, 2010: # explicitly use 64-bit flags, in case the compiler isn't. +# +# 13 December, 2011: +# use CMD for file operations, not programs from fileutils. CC = gcc CFLAGS = -O2 -Wall -X86OBJS = x86/proctype.o x86/injdll32.o x86/debugstr.o -X64OBJS = x64/proctype.o x64/injdll64.o x64/injdll32.o x64/debugstr.o +X86OBJS = x86/proctype.o x86/injdll32.o x86/util.o +X64OBJS = x64/proctype.o x64/injdll64.o x64/injdll32.o x64/util.o x86/%.o: %.c ansicon.h - $(CC) -m32 -c $(CFLAGS) $(CPPFLAGS) $< -o $@ + $(CC) -m32 -c $(CFLAGS) $< -o $@ -x86/%v.o: %.rc +x86/%v.o: %.rc version.h windres -U _WIN64 -F pe-i386 $< $@ x64/%.o: %.c ansicon.h - $(CC) -m64 -c $(CFLAGS) $(CPPFLAGS) $< -o $@ + $(CC) -m64 -c $(CFLAGS) $< -o $@ -x64/%v.o: %.rc +x64/%v.o: %.rc version.h windres -F pe-x86-64 $< $@ all: ansicon32 ansicon64 @@ -33,34 +36,40 @@ ansicon32: x86 x86/ansicon.exe x86/ANSI32.dll ansicon64: x64 x64/ansicon.exe x64/ANSI64.dll x64/ANSI32.dll x64/ANSI-LLW.exe x86: - mkdir x86 + cmd /c "mkdir x86" x86/ansicon.exe: x86/ansicon.o $(X86OBJS) x86/ansiconv.o - $(CC) -m32 $+ -s -o $@ -lpsapi -lole32 + $(CC) -m32 $+ -s -o $@ x86/ANSI32.dll: x86/ANSI.o $(X86OBJS) x86/ansiv.o $(CC) -m32 $+ -s -o $@ -mdll -Wl,-shared x64: - mkdir x64 + cmd /c "mkdir x64" x64/ansicon.exe: x64/ansicon.o $(X64OBJS) x64/ansiconv.o - $(CC) -m64 $+ -s -o $@ -lpsapi -lole32 + $(CC) -m64 $+ -s -o $@ x64/ANSI64.dll: x64/ANSI.o $(X64OBJS) x64/ansiv.o $(CC) -m64 $+ -s -o $@ -mdll -Wl,-shared x64/ANSI32.dll: x86/ANSI32.dll - cp -p x86/ANSI32.dll x64/ANSI32.dll + cmd /c "copy/y x86\ANSI32.dll x64\ANSI32.dll >nul" x64/ANSI-LLW.exe: ANSI-LLW.c $(CC) -m32 $(CFLAGS) $< -s -o $@ +x86/ansicon.o: version.h +x86/ANSI.o: version.h +x64/ansicon.o: version.h +x64/ANSI.o: version.h x86/ansiconv.o: ansicon.rc x86/ansiv.o: ansi.rc x64/ansiconv.o: ansicon.rc x64/ansiv.o: ansi.rc +# Need two commands, because if the directory doesn't exist, it won't delete +# anything at all. clean: - -rm x86/*.o - -rm x64/*.o + -cmd /c "del x86\*.o 2>nul" + -cmd /c "del x64\*.o 2>nul" diff --git a/makefile.vc b/makefile.vc index 5ae87e9..fd6cee5 100644 --- a/makefile.vc +++ b/makefile.vc @@ -26,10 +26,13 @@ DIR = x64 CC = cl CFLAGS = /nologo /W3 /Ox /GF /D_CRT_SECURE_NO_WARNINGS -LIBS = advapi32.lib shell32.lib user32.lib psapi.lib ole32.lib +LIBS = advapi32.lib user32.lib -X86OBJS = x86\proctype.obj x86\injdll32.obj x86\debugstr.obj -X64OBJS = x64\proctype.obj x64\injdll64.obj x64\injdll32.obj x64\debugstr.obj +# This is required for the 2003 Platform SDK, but not for Visual Studio 2010. +#LIBS64 = bufferoverflowu.lib + +X86OBJS = x86\proctype.obj x86\injdll32.obj x86\util.obj +X64OBJS = x64\proctype.obj x64\injdll64.obj x64\injdll32.obj x64\util.obj {}.c{$(DIR)}.obj: $(CC) /c $(CFLAGS) /Fo$@ $< @@ -58,20 +61,22 @@ x64: mkdir x64 x64\ansicon.exe: x64\ansicon.obj $(X64OBJS) x64\ansicon.res - $(CC) /nologo /Fe$@ $** $(LIBS) bufferoverflowu.lib + $(CC) /nologo /Fe$@ $** $(LIBS) $(LIBS64) x64\ANSI64.dll: x64\ANSI.obj $(X64OBJS) x64\ansi.res - $(CC) /nologo /LD /Fe$@ $** $(LIBS) bufferoverflowu.lib + $(CC) /nologo /LD /Fe$@ $** $(LIBS) $(LIBS64) x64\ANSI32.dll: x86\ANSI32.dll copy x86\ANSI32.dll x64\ANSI32.dll x64\ANSI-LLW.exe: ANSI-LLW.c - $(CC) $(CFLAGS) /Fe$@ /Fo$*.obj $? bufferoverflowu.lib + $(CC) $(CFLAGS) /Fe$@ /Fo$*.obj $? $(LIBS64) -ansicon.c: ansicon.h -ANSI.c: ansicon.h -debugstr.c: ansicon.h +ansicon.c: ansicon.h version.h +ansicon.rc: version.h +ANSI.c: ansicon.h version.h +ANSI.rc: version.h +util.c: ansicon.h injdll32.c: ansicon.h injdll64.c: ansicon.h proctype.c: ansicon.h diff --git a/proctype.c b/proctype.c index 457b75b..cc665d1 100644 --- a/proctype.c +++ b/proctype.c @@ -8,67 +8,75 @@ hardly seems worth it. There's GetModuleInformation, but passing in NULL just returns a base of NULL, so that's no help. Since 64/32 is sufficient, let ansicon.exe handle the difference between console/GUI. + + Update: ignore images characterised as DLL. */ #include "ansicon.h" -int ProcessType( LPPROCESS_INFORMATION pinfo ) +int ProcessType( LPPROCESS_INFORMATION pinfo, BOOL* gui ) { + char* ptr; MEMORY_BASIC_INFORMATION minfo; - char* ptr = 0; + IMAGE_DOS_HEADER dos_header; + IMAGE_NT_HEADERS nt_header; + SIZE_T read; - while (VirtualQueryEx( pinfo->hProcess, ptr, &minfo, sizeof(minfo) )) + *gui = FALSE; + for (ptr = NULL; + VirtualQueryEx( pinfo->hProcess, ptr, &minfo, sizeof(minfo) ); + ptr += minfo.RegionSize) { - IMAGE_DOS_HEADER dos_header; - SIZE_T read; if (minfo.BaseAddress == minfo.AllocationBase && ReadProcessMemory( pinfo->hProcess, minfo.AllocationBase, &dos_header, sizeof(dos_header), &read )) { if (dos_header.e_magic == IMAGE_DOS_SIGNATURE) { - IMAGE_NT_HEADERS nt_header; if (ReadProcessMemory( pinfo->hProcess, (char*)minfo.AllocationBase + dos_header.e_lfanew, &nt_header, sizeof(nt_header), &read )) { - if (nt_header.Signature == IMAGE_NT_SIGNATURE) + if (nt_header.Signature == IMAGE_NT_SIGNATURE && + (nt_header.FileHeader.Characteristics & + (IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL)) + == IMAGE_FILE_EXECUTABLE_IMAGE) { - BOOL gui = (nt_header.OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI); - if (nt_header.OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI || - gui ) + *gui = (nt_header.OptionalHeader.Subsystem + == IMAGE_SUBSYSTEM_WINDOWS_GUI); + if (nt_header.OptionalHeader.Subsystem == + IMAGE_SUBSYSTEM_WINDOWS_CUI || *gui) { if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_I386) { - DEBUGSTR( L" %p: 32-bit %s", - minfo.AllocationBase, (gui) ? L"GUI" : L"console" ); + DEBUGSTR( 1, L" 32-bit %s (base = %p)", + (*gui) ? L"GUI" : L"console", minfo.AllocationBase ); return 32; } #ifdef _WIN64 if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64) { - DEBUGSTR( L" %p: 64-bit %s", - minfo.AllocationBase, (gui) ? L"GUI" : L"console" ); + DEBUGSTR( 1, L" 64-bit %s (base = %p)", + (*gui) ? L"GUI" : L"console", minfo.AllocationBase ); return 64; } #endif - DEBUGSTR( L" Ignoring unsupported machine (0x%X)", + DEBUGSTR( 1, L" Ignoring unsupported machine (0x%X)", nt_header.FileHeader.Machine ); } else { - DEBUGSTR( L" Ignoring non-Windows subsystem (%u)", + DEBUGSTR( 1, L" Ignoring unsupported subsystem (%u)", nt_header.OptionalHeader.Subsystem ); } + return 0; } } - return 0; } } - ptr += minfo.RegionSize; } - DEBUGSTR( L" Ignoring non-Windows process" ); + DEBUGSTR( 1, L" Ignoring non-Windows process" ); return 0; } diff --git a/readme.txt b/readme.txt index a4013f6..847e01f 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ Copyright 2005-2011 Jason Hood - Version 1.40. Freeware + Version 1.50. Freeware =========== @@ -30,9 +30,9 @@ use option `-i' (or `-I') to install it permanently, by adding an entry to CMD.EXE's AutoRun registry value (current user or local machine, respectively). Uninstall simply involves closing any programs that are - currently using it, running with `-u' (and again with `-U') to remove - the AutoRun entry/ies, then removing the directory from PATH or deleting - the files. No other changes are made. + currently using it, running with `-u' (and/or `-U') to remove the Auto- + Run entry/ies, then removing the directory from PATH or deleting the + files. No other changes are made. --------- Upgrading @@ -40,6 +40,7 @@ Delete ANSI.dll, it has been replaced with ANSI32.dll. Delete ANSI-LLA.dll, it has been replaced with ANSI-LLW.dll. + Uninstall with your current version and install with this version. ===== @@ -48,6 +49,8 @@ Options (case sensitive): + -l Log to %temp%\ansicon.log. + -p Enable the parent process (i.e. the command shell used to run ANSICON) to recognise escapes. @@ -55,7 +58,7 @@ ("monochrome"), or the attribute following the `m' (please use `COLOR /?' for attribute values). - -e Echo the command line - the character after the `e' is + -e Echo the command line - a space or tab after the `e' is ignored, the remainder is displayed verbatim. -E As above, but no newline is added. @@ -74,12 +77,46 @@ Eg: `ansicon -m30 -t file.ans' will display `file.ans' using black on cyan as the default color. + The attribute may start with "-" to permanently reverse the foreground + and background colors (but not when using `-p'). Eg: `ansicon -m-f0 -t + file.log' will use reversed black on white as the default (i.e. white on + black, with foreground sequences changing the background). + + If you experience trouble with certain programs, the log may help in + finding the cause; it can be found at "%TEMP%\ansicon.log". A number + should follow the `l': + + 0 No logging + 1 Log process start and end + 2 Above, plus log modules used by the process + 3 Above, plus log functions that are hooked + 4 Log console output (add to any of the above) + 8 Append to the existing file (add to any of the above) + + The log option will not work with `-p'; set the environment variable + ANSICON_LOG instead. The variable is only read once when a new process + is started; changing it won't affect running processes. If you identify + a module that causes problems (one known is "nvd3d9wrap.dll") add it to + the ANSICON_EXC environment variable (see ANSICON_API below, but the + extension is required). + Once installed, the ANSICON environment variable will be created. This variable is of the form "WxH (wxh)", where W & H are the width and height of the buffer and w & h are the width and height of the window. The variable is updated whenever a program reads it directly (i.e. as an individual request, not as part of the entire environment block). For example, "set an" will not update it, but "echo %ansicon%" will. + Also created is ANSICON_VER, which contains the version without the + point (1.50 becomes "150"). This variable does not exist as part of the + environment block ("set an" will not show it). + + If installed, GUI programs will not be hooked. Either start the program + directly with `ansicon', or add it to the ANSICON_GUI variable (see + ANSICON_API below). + + Using `ansicon' after install will always start with the default attrib- + utes, restoring the originals on exit; all other programs will use the + current attributes. The shift state is always reset for a new process. The Windows API WriteFile and WriteConsoleA functions will set the num- ber of characters written, not the number of bytes. When using a multi- @@ -142,7 +179,10 @@ I make a distinction between "\e[m" and "\e[0;...m". Both will restore the original foreground/background colors (and so "0" should be the first parameter); the former will also restore the original bold and - underline attributes, whilst the latter will explicitly reset them. + underline attributes, whilst the latter will explicitly reset them. The + environment variable ANSICON_DEF can be used to change the default col- + ors (same value as `-m'; setting the variable does not change the cur- + rent colors). ================= @@ -217,8 +257,6 @@ The entire console buffer is used, not just the visible window. - If running CMD.EXE, its own COLOR will be the initial color. - The 64-bit version can inject into a 32-bit process, but the 32-bit version will not inject into a 64-bit process. @@ -231,6 +269,19 @@ Legend: + added, - bug-fixed, * changed. + 1.50 - 14 December, 2011: + - -u does not imply -p; + - return the program's exit code; + - -p by itself will not restore original color; + - output error messages to stderr; + * logging is always available, with various levels; include the pid; + * don't automatically hook GUI programs, use `ansicon' or ANSICON_GUI; + * always place first in AutoRun; don't run if already installed; + + global reverse video capability; + + added ANSICON_VER to provide version/install test; + + added ANSICON_EXC to exclude selected modules; + + added ANSICON_DEF to explicitly set the default SGM. + 1.40 - 1 March, 2011: - hook GetProcAddress (now PowerShell works); + add SO/SI, using the DEC Special Graphics Character Set for G1; @@ -364,5 +415,5 @@ in the version text and a source diff is included. - ========================== - Jason Hood, 3 March, 2011. + ============================== + Jason Hood, 14 December, 2011. diff --git a/util.c b/util.c new file mode 100644 index 0000000..0867ddb --- /dev/null +++ b/util.c @@ -0,0 +1,125 @@ +/* + util.c - Utility functions. +*/ + +#include "ansicon.h" + + +TCHAR prog_path[MAX_PATH]; +LPTSTR prog; +int log_level; +char tempfile[MAX_PATH]; +DWORD pid; + + +// Get just the name of the program: "C:\path\program.exe" -> "program". +// Returns a pointer within program; it is modified to remove the extension. +LPTSTR get_program_name( LPTSTR program ) +{ + LPTSTR name, ext; + + if (program == NULL) + { + GetModuleFileName( NULL, prog_path, lenof(prog_path) ); + program = prog_path; + } + name = wcsrchr( program, '\\' ); + if (name != NULL) + ++name; + else + name = program; + ext = wcsrchr( name, '.' ); + if (ext != NULL && ext != name) + *ext = '\0'; + + return name; +} + + +void DEBUGSTR( int level, LPTSTR szFormat, ... ) +{ + TCHAR szBuffer[1024], szEscape[1024]; + va_list pArgList; + HANDLE mutex; + DWORD wait; + FILE* file; + + if ((log_level & 3) < level && !(level & 4 & log_level)) + return; + + if (*tempfile == '\0') + { + _snprintf( tempfile, MAX_PATH, "%s\\ansicon.log", getenv( "TEMP" ) ); + pid = GetCurrentProcessId(); + } + if (szFormat == NULL) + { + file = fopen( tempfile, "wt" ); + if (file != NULL) + { + SYSTEMTIME now; + GetLocalTime( &now ); + fprintf( file, "Logging started %d-%.2d-%.2d %d:%.2d:%.2d\n", + now.wYear, now.wMonth, now.wDay, + now.wHour, now.wMinute, now.wSecond ); + fclose( file ); + } + return; + } + + va_start( pArgList, szFormat ); + _vsnwprintf( szBuffer, lenof(szBuffer), szFormat, pArgList ); + va_end( pArgList ); + + szFormat = szBuffer; + if (*szFormat == '\33') + { + BOOL first = TRUE; + LPTSTR pos = szEscape; + while (*++szFormat != '\0' && pos < szEscape + lenof(szEscape) - 4) + { + if (*szFormat < 32) + { + *pos++ = '\\'; + switch (*szFormat) + { + case '\a': *pos++ = 'a'; break; + case '\b': *pos++ = 'b'; break; + case '\t': *pos++ = 't'; break; + case '\r': *pos++ = 'r'; break; + case '\n': *pos++ = 'n'; break; + case 27 : *pos++ = 'e'; break; + default: + pos += _snwprintf( pos, 32, L"%.*o", + (szFormat[1] >= '0' && szFormat[1] <= '7') ? 3 : 1, + *szFormat ); + } + } + else + { + if (*szFormat == '"') + { + if (first) + first = FALSE; + else if (szFormat[1] != '\0') + *pos++ = '\\'; + } + *pos++ = *szFormat; + } + } + *pos = '\0'; + szFormat = szEscape; + } + + mutex = CreateMutex( NULL, FALSE, L"ANSICON_debug_file" ); + wait = WaitForSingleObject( mutex, 500 ); + file = fopen( tempfile, "at" ); // _fmode might be binary + if (file != NULL) + { + fwprintf( file, L"%s (%lu): %s\n", prog, pid, szFormat ); + fclose( file ); + } + if (wait == WAIT_OBJECT_0) + ReleaseMutex( mutex ); + CloseHandle( mutex ); +} diff --git a/version.h b/version.h new file mode 100644 index 0000000..c6ec754 --- /dev/null +++ b/version.h @@ -0,0 +1,9 @@ +/* + version.h - Version defines. +*/ + +#define PVERS L"1.50" // wide string +#define PVERSA "1.50" // ANSI string (windres 2.16.91 didn't like L) +#define PVERE L"150" // wide environment string +#define PVEREA "150" // ANSI environment string +#define PVERB 1,5,0,0 // binary (resource)