Many changes, bad programmer!
Just copying the history from the source: recognize the standard handle defines in WriteFile; minor speed improvement by caching GetConsoleMode; keep track of three handles (ostensibly stdout, stderr and a file); test a DOS header exists before writing to e_oemid; more flexible/robust handling of data directories; files writing to the console will always succeed; log: use API file functions and a custom printf; add a blank line between processes; set function name for MyWriteConsoleA; scan imports from "kernel32" (without extension); added dynamic environment variable CLICOLOR; removed _hwrite (it's the same address as _lwrite); join multibyte characters split across separate writes; remove wcstok, avoiding potential interference with the host; similarly, use a private heap instead of malloc.
This commit is contained in:
parent
2f18f10719
commit
40f59c543c
25
ansicon.c
25
ansicon.c
@ -87,7 +87,7 @@
|
||||
add error codes to some message.
|
||||
*/
|
||||
|
||||
#define PDATE L"26 February, 2014"
|
||||
#define PDATE L"24 December, 2015"
|
||||
|
||||
#include "ansicon.h"
|
||||
#include "version.h"
|
||||
@ -169,6 +169,7 @@ int my_fputws( const wchar_t* s, FILE* f )
|
||||
#define _putws( s ) my_fputws( s L"\n", stdout )
|
||||
|
||||
|
||||
HANDLE hHeap;
|
||||
#if defined(_WIN64)
|
||||
LPTSTR DllNameType;
|
||||
#endif
|
||||
@ -183,7 +184,7 @@ BOOL Inject( LPPROCESS_INFORMATION ppi, BOOL* gui, LPCTSTR app )
|
||||
#ifdef _WIN64
|
||||
if (app != NULL)
|
||||
#endif
|
||||
DEBUGSTR( 1, L"%s (%lu)", app, ppi->dwProcessId );
|
||||
DEBUGSTR( 1, "%S (%u)", app, ppi->dwProcessId );
|
||||
type = ProcessType( ppi, &base, gui );
|
||||
if (type <= 0)
|
||||
{
|
||||
@ -230,7 +231,7 @@ void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app )
|
||||
int type;
|
||||
#endif
|
||||
|
||||
DEBUGSTR( 1, L"%s (%lu)", app, ppi->dwProcessId );
|
||||
DEBUGSTR( 1, "%S (%u)", app, ppi->dwProcessId );
|
||||
|
||||
// Find the base address of kernel32.dll.
|
||||
ticks = GetTickCount();
|
||||
@ -242,7 +243,7 @@ void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app )
|
||||
#ifndef _WIN64
|
||||
if (err == ERROR_PARTIAL_COPY)
|
||||
{
|
||||
DEBUGSTR( 1, L" Ignoring 64-bit process (use x64\\ansicon)" );
|
||||
DEBUGSTR( 1, " Ignoring 64-bit process (use x64\\ansicon)" );
|
||||
fputws( L"ANSICON: parent is 64-bit (use x64\\ansicon).\n", stderr );
|
||||
return;
|
||||
}
|
||||
@ -251,10 +252,10 @@ void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app )
|
||||
// two seconds to avoid a potentially infinite loop.
|
||||
if (err == ERROR_BAD_LENGTH && GetTickCount() - ticks < 2000)
|
||||
{
|
||||
Sleep( 0 );
|
||||
Sleep( 1 );
|
||||
continue;
|
||||
}
|
||||
DEBUGSTR( 1, L" Unable to create snapshot (%lu)", err );
|
||||
DEBUGSTR( 1, " Unable to create snapshot (%u)", err );
|
||||
no_go:
|
||||
fputws( L"ANSICON: unable to inject into parent.\n", stderr );
|
||||
return;
|
||||
@ -272,7 +273,7 @@ void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app )
|
||||
CloseHandle( hSnap );
|
||||
if (LLW == NULL)
|
||||
{
|
||||
DEBUGSTR( 1, L" Unable to locate kernel32.dll (%lu)", GetLastError() );
|
||||
DEBUGSTR( 1, " Unable to locate kernel32.dll" );
|
||||
goto no_go;
|
||||
}
|
||||
|
||||
@ -292,7 +293,7 @@ void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app )
|
||||
mem = VirtualAllocEx( ppi->hProcess, NULL, len, MEM_COMMIT, PAGE_READWRITE );
|
||||
if (mem == NULL)
|
||||
{
|
||||
DEBUGSTR( 1, L" Unable to allocate virtual memory (%lu)", GetLastError() );
|
||||
DEBUGSTR(1, " Failed to allocate virtual memory (%u)", GetLastError());
|
||||
goto no_go;
|
||||
}
|
||||
WriteProcMem( mem, DllName, TSIZE(len + 11) );
|
||||
@ -357,6 +358,8 @@ int main( void )
|
||||
}
|
||||
}
|
||||
|
||||
hHeap = HeapCreate( 0, 0, 65 * 1024 );
|
||||
|
||||
prog = get_program_name( NULL );
|
||||
*buf = '\0';
|
||||
GetEnvironmentVariable( L"ANSICON_LOG", buf, lenof(buf) );
|
||||
@ -369,7 +372,7 @@ int main( void )
|
||||
pi.hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId );
|
||||
if (pi.hProcess == NULL)
|
||||
{
|
||||
DEBUGSTR( 1, L" Unable to open process %lu (%lu)",
|
||||
DEBUGSTR( 1, " Unable to open process %u (%u)",
|
||||
pi.dwProcessId, GetLastError() );
|
||||
}
|
||||
else
|
||||
@ -738,7 +741,7 @@ BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi, LPTSTR name )
|
||||
hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
|
||||
if (hSnap == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DEBUGSTR( 1, L"Failed to create snapshot (%lu)", GetLastError() );
|
||||
DEBUGSTR( 1, "Failed to create snapshot (%u)", GetLastError() );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -747,7 +750,7 @@ BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi, LPTSTR name )
|
||||
CloseHandle( hSnap );
|
||||
if (!fOk)
|
||||
{
|
||||
DEBUGSTR( 1, L"Failed to locate parent" );
|
||||
DEBUGSTR( 1, "Failed to locate parent" );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,7 @@
|
||||
// Macro for adding pointers/DWORDs together without C arithmetic interfering
|
||||
#define MakeVA( cast, offset ) (cast)((DWORD_PTR)pDosHeader + (DWORD)(offset))
|
||||
|
||||
#define DATADIRS OptionalHeader.NumberOfRvaAndSizes
|
||||
#define EXPORTDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
|
||||
#define IMPORTDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
|
||||
#define BOUNDDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT]
|
||||
@ -61,6 +62,7 @@ DWORD GetProcRVA( LPCTSTR, LPCSTR, int );
|
||||
DWORD GetProcRVA( LPCTSTR, LPCSTR );
|
||||
#endif
|
||||
|
||||
extern HANDLE hHeap;
|
||||
|
||||
extern TCHAR prog_path[MAX_PATH];
|
||||
extern LPTSTR prog;
|
||||
@ -74,6 +76,6 @@ extern char* ansi_bits;
|
||||
void set_ansi_dll( void );
|
||||
|
||||
extern int log_level;
|
||||
void DEBUGSTR( int level, LPTSTR szFormat, ... );
|
||||
void DEBUGSTR( int level, LPCSTR szFormat, ... );
|
||||
|
||||
#endif
|
||||
|
110
injdll.c
110
injdll.c
@ -38,11 +38,35 @@ static PVOID FindMem( HANDLE hProcess, PBYTE base, DWORD len )
|
||||
}
|
||||
|
||||
|
||||
// Count the imports directly (including the terminator), since the size of the
|
||||
// import directory is not necessarily correct (Windows doesn't use it at all).
|
||||
static DWORD sizeof_imports( LPPROCESS_INFORMATION ppi,
|
||||
PBYTE pBase, DWORD rImports )
|
||||
{
|
||||
IMAGE_IMPORT_DESCRIPTOR import;
|
||||
PIMAGE_IMPORT_DESCRIPTOR pImports;
|
||||
DWORD cnt;
|
||||
|
||||
if (rImports == 0)
|
||||
return 0;
|
||||
|
||||
pImports = (PIMAGE_IMPORT_DESCRIPTOR)(pBase + rImports);
|
||||
cnt = 0;
|
||||
do
|
||||
{
|
||||
++cnt;
|
||||
ReadProcVar( pImports++, &import );
|
||||
} while (import.Name != 0);
|
||||
|
||||
return cnt * sizeof(import);
|
||||
}
|
||||
|
||||
|
||||
void InjectDLL( LPPROCESS_INFORMATION ppi, PBYTE pBase )
|
||||
{
|
||||
DWORD rva;
|
||||
PVOID pMem;
|
||||
DWORD len;
|
||||
DWORD len, import_size;
|
||||
DWORD pr;
|
||||
IMAGE_DOS_HEADER DosHeader;
|
||||
IMAGE_NT_HEADERS NTHeader, *pNTHeader;
|
||||
@ -59,18 +83,19 @@ void InjectDLL( LPPROCESS_INFORMATION ppi, PBYTE pBase )
|
||||
pNTHeader = (PIMAGE_NT_HEADERS)(pBase + DosHeader.e_lfanew);
|
||||
ReadProcVar( pNTHeader, &NTHeader );
|
||||
|
||||
len = 4 * PTRSZ + ansi_len + sizeof(*pImports) + NTHeader.IMPORTDIR.Size;
|
||||
pImports = malloc( len );
|
||||
import_size = sizeof_imports( ppi, pBase, NTHeader.IMPORTDIR.VirtualAddress );
|
||||
len = 2 * PTRSZ + ansi_len + sizeof(*pImports) + import_size;
|
||||
pImports = HeapAlloc( hHeap, 0, len );
|
||||
if (pImports == NULL)
|
||||
{
|
||||
DEBUGSTR( 1, L" Failed to allocate memory." );
|
||||
DEBUGSTR( 1, " Failed to allocate memory" );
|
||||
return;
|
||||
}
|
||||
pMem = FindMem( ppi->hProcess, pBase, len );
|
||||
if (pMem == NULL)
|
||||
{
|
||||
DEBUGSTR( 1, L" Failed to allocate virtual memory." );
|
||||
free( pImports );
|
||||
DEBUGSTR( 1, " Failed to allocate virtual memory (%u)", GetLastError() );
|
||||
HeapFree( hHeap, 0, pImports );
|
||||
return;
|
||||
}
|
||||
rva = (DWORD)((PBYTE)pMem - pBase);
|
||||
@ -78,37 +103,39 @@ void InjectDLL( LPPROCESS_INFORMATION ppi, PBYTE pBase )
|
||||
ip.pL = (PLONG_PTR)pImports;
|
||||
*ip.pL++ = IMAGE_ORDINAL_FLAG + 1;
|
||||
*ip.pL++ = 0;
|
||||
*ip.pL++ = IMAGE_ORDINAL_FLAG + 1;
|
||||
*ip.pL++ = 0;
|
||||
memcpy( ip.pB, ansi_dll, ansi_len );
|
||||
ip.pB += ansi_len;
|
||||
ip.pI->OriginalFirstThunk = rva + 2 * PTRSZ;
|
||||
ip.pI->OriginalFirstThunk = 0;
|
||||
ip.pI->TimeDateStamp = 0;
|
||||
ip.pI->ForwarderChain = 0;
|
||||
ip.pI->Name = rva + 4 * PTRSZ;
|
||||
ip.pI->Name = rva + 2 * PTRSZ;
|
||||
ip.pI->FirstThunk = rva;
|
||||
ReadProcMem( pBase + NTHeader.IMPORTDIR.VirtualAddress,
|
||||
ip.pI + 1, NTHeader.IMPORTDIR.Size );
|
||||
ReadProcMem( pBase+NTHeader.IMPORTDIR.VirtualAddress, ip.pI+1, import_size );
|
||||
WriteProcMem( pMem, pImports, len );
|
||||
free( pImports );
|
||||
HeapFree( hHeap, 0, pImports );
|
||||
|
||||
// If there's no IAT, copy the original IDT (to allow writable ".idata").
|
||||
if (NTHeader.IATDIR.VirtualAddress == 0)
|
||||
if (NTHeader.DATADIRS > IMAGE_DIRECTORY_ENTRY_IAT &&
|
||||
NTHeader.IATDIR.VirtualAddress == 0)
|
||||
NTHeader.IATDIR = NTHeader.IMPORTDIR;
|
||||
|
||||
NTHeader.IMPORTDIR.VirtualAddress = rva + 4 * PTRSZ + ansi_len;
|
||||
NTHeader.IMPORTDIR.Size += sizeof(*pImports);
|
||||
NTHeader.IMPORTDIR.VirtualAddress = rva + 2 * PTRSZ + ansi_len;
|
||||
//NTHeader.IMPORTDIR.Size += sizeof(*pImports);
|
||||
|
||||
// Remove bound imports, so the updated import table is used.
|
||||
NTHeader.BOUNDDIR.VirtualAddress = 0;
|
||||
NTHeader.BOUNDDIR.Size = 0;
|
||||
if (NTHeader.DATADIRS > IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT)
|
||||
{
|
||||
NTHeader.BOUNDDIR.VirtualAddress = 0;
|
||||
//NTHeader.BOUNDDIR.Size = 0;
|
||||
}
|
||||
|
||||
VirtProtVar( pNTHeader, PAGE_READWRITE );
|
||||
WriteProcVar( pNTHeader, &NTHeader );
|
||||
VirtProtVar( pNTHeader, pr );
|
||||
|
||||
// Remove the IL-only flag on a managed process.
|
||||
if (NTHeader.COMDIR.VirtualAddress != 0 && NTHeader.COMDIR.Size != 0)
|
||||
if (NTHeader.DATADIRS > IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR &&
|
||||
NTHeader.COMDIR.VirtualAddress != 0)
|
||||
{
|
||||
pComHeader = (PIMAGE_COR20_HEADER)(pBase + NTHeader.COMDIR.VirtualAddress);
|
||||
ReadProcVar( pComHeader, &ComHeader );
|
||||
@ -128,7 +155,7 @@ void InjectDLL32( LPPROCESS_INFORMATION ppi, PBYTE pBase )
|
||||
{
|
||||
DWORD rva;
|
||||
PVOID pMem;
|
||||
DWORD len;
|
||||
DWORD len, import_size;
|
||||
DWORD pr;
|
||||
IMAGE_DOS_HEADER DosHeader;
|
||||
IMAGE_NT_HEADERS32 NTHeader, *pNTHeader;
|
||||
@ -145,18 +172,19 @@ void InjectDLL32( LPPROCESS_INFORMATION ppi, PBYTE pBase )
|
||||
pNTHeader = (PIMAGE_NT_HEADERS32)(pBase + DosHeader.e_lfanew);
|
||||
ReadProcVar( pNTHeader, &NTHeader );
|
||||
|
||||
len = 16 + ansi_len + sizeof(*pImports) + NTHeader.IMPORTDIR.Size;
|
||||
pImports = malloc( len );
|
||||
import_size = sizeof_imports( ppi, pBase, NTHeader.IMPORTDIR.VirtualAddress );
|
||||
len = 8 + ansi_len + sizeof(*pImports) + import_size;
|
||||
pImports = HeapAlloc( hHeap, 0, len );
|
||||
if (pImports == NULL)
|
||||
{
|
||||
DEBUGSTR( 1, L" Failed to allocate memory." );
|
||||
DEBUGSTR( 1, " Failed to allocate memory" );
|
||||
return;
|
||||
}
|
||||
pMem = FindMem( ppi->hProcess, pBase, len );
|
||||
if (pMem == NULL)
|
||||
{
|
||||
DEBUGSTR( 1, L" Failed to allocate virtual memory." );
|
||||
free( pImports );
|
||||
DEBUGSTR( 1, " Failed to allocate virtual memory" );
|
||||
HeapFree( hHeap, 0, pImports );
|
||||
return;
|
||||
}
|
||||
rva = (DWORD)((PBYTE)pMem - pBase);
|
||||
@ -164,31 +192,33 @@ void InjectDLL32( LPPROCESS_INFORMATION ppi, PBYTE pBase )
|
||||
ip.pL = (PLONG)pImports;
|
||||
*ip.pL++ = IMAGE_ORDINAL_FLAG32 + 1;
|
||||
*ip.pL++ = 0;
|
||||
*ip.pL++ = IMAGE_ORDINAL_FLAG32 + 1;
|
||||
*ip.pL++ = 0;
|
||||
memcpy( ip.pB, ansi_dll, ansi_len );
|
||||
ip.pB += ansi_len;
|
||||
ip.pI->OriginalFirstThunk = rva + 8;
|
||||
ip.pI->OriginalFirstThunk = 0;
|
||||
ip.pI->TimeDateStamp = 0;
|
||||
ip.pI->ForwarderChain = 0;
|
||||
ip.pI->Name = rva + 16;
|
||||
ip.pI->Name = rva + 8;
|
||||
ip.pI->FirstThunk = rva;
|
||||
ReadProcMem( pBase + NTHeader.IMPORTDIR.VirtualAddress,
|
||||
ip.pI + 1, NTHeader.IMPORTDIR.Size );
|
||||
ReadProcMem( pBase+NTHeader.IMPORTDIR.VirtualAddress, ip.pI+1, import_size );
|
||||
WriteProcMem( pMem, pImports, len );
|
||||
free( pImports );
|
||||
HeapFree( hHeap, 0, pImports );
|
||||
|
||||
if (NTHeader.IATDIR.VirtualAddress == 0)
|
||||
if (NTHeader.DATADIRS > IMAGE_DIRECTORY_ENTRY_IAT &&
|
||||
NTHeader.IATDIR.VirtualAddress == 0)
|
||||
NTHeader.IATDIR = NTHeader.IMPORTDIR;
|
||||
NTHeader.IMPORTDIR.VirtualAddress = rva + 16 + ansi_len;
|
||||
NTHeader.IMPORTDIR.Size += sizeof(*pImports);
|
||||
NTHeader.BOUNDDIR.VirtualAddress = 0;
|
||||
NTHeader.BOUNDDIR.Size = 0;
|
||||
NTHeader.IMPORTDIR.VirtualAddress = rva + 8 + ansi_len;
|
||||
//NTHeader.IMPORTDIR.Size += sizeof(*pImports);
|
||||
if (NTHeader.DATADIRS > IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT)
|
||||
{
|
||||
NTHeader.BOUNDDIR.VirtualAddress = 0;
|
||||
//NTHeader.BOUNDDIR.Size = 0;
|
||||
}
|
||||
VirtProtVar( pNTHeader, PAGE_READWRITE );
|
||||
WriteProcVar( pNTHeader, &NTHeader );
|
||||
VirtProtVar( pNTHeader, pr );
|
||||
|
||||
if (NTHeader.COMDIR.VirtualAddress != 0 && NTHeader.COMDIR.Size != 0)
|
||||
if (NTHeader.DATADIRS > IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR &&
|
||||
NTHeader.COMDIR.VirtualAddress != 0)
|
||||
{
|
||||
pComHeader = (PIMAGE_COR20_HEADER)(pBase + NTHeader.COMDIR.VirtualAddress);
|
||||
ReadProcVar( pComHeader, &ComHeader );
|
||||
@ -233,7 +263,7 @@ static PBYTE get_ntdll( LPPROCESS_INFORMATION ppi )
|
||||
}
|
||||
}
|
||||
|
||||
DEBUGSTR( 1, L" Failed to find ntdll.dll!" );
|
||||
DEBUGSTR( 1, " Failed to find ntdll.dll!" );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -266,7 +296,7 @@ void InjectDLL64( LPPROCESS_INFORMATION ppi )
|
||||
PAGE_EXECUTE_READ );
|
||||
if (pMem == NULL)
|
||||
{
|
||||
DEBUGSTR( 1, L" Failed to allocate virtual memory (%lu)", GetLastError() );
|
||||
DEBUGSTR(1, " Failed to allocate virtual memory (%u)", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -43,10 +43,10 @@ DWORD GetProcRVA( LPCTSTR module, LPCSTR func )
|
||||
if (hMod == NULL)
|
||||
{
|
||||
#ifdef _WIN64
|
||||
DEBUGSTR( 1, L"Unable to load %d-bit %s (%lu)!",
|
||||
DEBUGSTR( 1, "Unable to load %u-bit %S (%u)!",
|
||||
bits, module, GetLastError() );
|
||||
#else
|
||||
DEBUGSTR( 1, L"Unable to load %s (%lu)!", module, GetLastError() );
|
||||
DEBUGSTR( 1, "Unable to load %S (%u)!", module, GetLastError() );
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
@ -70,9 +70,9 @@ DWORD GetProcRVA( LPCTSTR module, LPCSTR func )
|
||||
if (pFunc == NULL)
|
||||
{
|
||||
#ifdef _WIN64
|
||||
DEBUGSTR( 1, L"Could not find %d-bit %s!", bits, func );
|
||||
DEBUGSTR( 1, "Could not find %u-bit %s!", bits, func );
|
||||
#else
|
||||
DEBUGSTR( 1, L"Could not find %s!", func );
|
||||
DEBUGSTR( 1, "Could not find %s!", func );
|
||||
#endif
|
||||
rva = 0;
|
||||
}
|
||||
|
46
proctype.c
46
proctype.c
@ -76,8 +76,8 @@ int ProcessType( LPPROCESS_INFORMATION ppi, PBYTE* pBase, BOOL* gui )
|
||||
if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_I386)
|
||||
{
|
||||
PIMAGE_NT_HEADERS32 pNTHeader = (PIMAGE_NT_HEADERS32)&nt_header;
|
||||
if (pNTHeader->COMDIR.VirtualAddress != 0 &&
|
||||
pNTHeader->COMDIR.Size != 0)
|
||||
if (pNTHeader->DATADIRS > IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR &&
|
||||
pNTHeader->COMDIR.VirtualAddress != 0)
|
||||
{
|
||||
IMAGE_COR20_HEADER ComHeader, *pComHeader;
|
||||
pComHeader = (PIMAGE_COR20_HEADER)((PBYTE)minfo.BaseAddress
|
||||
@ -87,57 +87,51 @@ int ProcessType( LPPROCESS_INFORMATION ppi, PBYTE* pBase, BOOL* gui )
|
||||
!(ComHeader.Flags & COMIMAGE_FLAGS_32BITREQUIRED))
|
||||
{
|
||||
#if defined(_WIN64) || !defined(W32ON64) // W32ON64 will log due to -P
|
||||
DEBUGSTR( 1, L" AnyCPU %s (base = %.8X)",
|
||||
(*gui) ? L"GUI" : L"console",
|
||||
PtrToUint( minfo.BaseAddress ) );
|
||||
DEBUGSTR( 1, " AnyCPU %s (base = %q)",
|
||||
(*gui) ? "GUI" : "console", minfo.BaseAddress );
|
||||
#endif
|
||||
#if defined(_WIN64) || defined(W32ON64)
|
||||
return 48;
|
||||
#else
|
||||
if (ProcessIs64( ppi->hProcess ))
|
||||
{
|
||||
DEBUGSTR( 1, L" Unsupported (use x64\\ansicon)" );
|
||||
DEBUGSTR( 1, " Unsupported (use x64\\ansicon)" );
|
||||
return 0;
|
||||
}
|
||||
return 32;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
DEBUGSTR( 1, L" 32-bit %s (base = %.8X)",
|
||||
(*gui) ? L"GUI" : L"console",
|
||||
PtrToUint( minfo.BaseAddress ) );
|
||||
DEBUGSTR( 1, " 32-bit %s (base = %q)",
|
||||
(*gui) ? "GUI" : "console", minfo.BaseAddress );
|
||||
return 32;
|
||||
}
|
||||
if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
|
||||
{
|
||||
#ifdef _WIN64
|
||||
DEBUGSTR( 1, L" 64-bit %s (base = %.8X_%.8X)",
|
||||
(*gui) ? L"GUI" : L"console",
|
||||
(DWORD)((DWORD_PTR)minfo.BaseAddress >> 32),
|
||||
PtrToUint( minfo.BaseAddress ) );
|
||||
DEBUGSTR( 1, " 64-bit %s (base = %p)",
|
||||
(*gui) ? "GUI" : "console", minfo.BaseAddress );
|
||||
return 64;
|
||||
#elif defined(W32ON64)
|
||||
// Console will log due to -P, but GUI may be ignored (if not,
|
||||
// this'll show up twice).
|
||||
if (*gui)
|
||||
DEBUGSTR( 1, L" 64-bit GUI (base = 00000000_%.8X)",
|
||||
PtrToUint( minfo.BaseAddress ) );
|
||||
DEBUGSTR( 1, " 64-bit GUI (base = %P)", minfo.BaseAddress );
|
||||
return 64;
|
||||
#else
|
||||
DEBUGSTR( 1, L" 64-bit %s (base = 00000000_%.8X)",
|
||||
(*gui) ? L"GUI" : L"console",
|
||||
PtrToUint( minfo.BaseAddress ) );
|
||||
DEBUGSTR( 1, L" Unsupported (use x64\\ansicon)" );
|
||||
DEBUGSTR( 1, " 64-bit %s (base = %P)",
|
||||
(*gui) ? "GUI" : "console", minfo.BaseAddress );
|
||||
DEBUGSTR( 1, " Unsupported (use x64\\ansicon)" );
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
DEBUGSTR( 1, L" Ignoring unsupported machine (0x%X)",
|
||||
nt_header.FileHeader.Machine );
|
||||
DEBUGSTR( 1, " Ignoring unsupported machine (0x%X)",
|
||||
nt_header.FileHeader.Machine );
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUGSTR( 1, L" Ignoring unsupported subsystem (%u)",
|
||||
nt_header.OptionalHeader.Subsystem );
|
||||
DEBUGSTR( 1, " Ignoring unsupported subsystem (%u)",
|
||||
nt_header.OptionalHeader.Subsystem );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -147,16 +141,16 @@ int ProcessType( LPPROCESS_INFORMATION ppi, PBYTE* pBase, BOOL* gui )
|
||||
if (((DWORD)ptr >> 12) + ((DWORD)minfo.RegionSize >> 12) >= 0x100000)
|
||||
{
|
||||
#ifdef W32ON64
|
||||
DEBUGSTR( 1, L" Pointer overflow: assuming 64-bit" );
|
||||
DEBUGSTR( 1, " Pointer overflow: assuming 64-bit" );
|
||||
return 64;
|
||||
#else
|
||||
DEBUGSTR( 1, L" Ignoring apparent 64-bit process (use x64\\ansicon)" );
|
||||
DEBUGSTR( 1, " Ignoring apparent 64-bit process (use x64\\ansicon)" );
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
DEBUGSTR( 1, L" Ignoring non-Windows process" );
|
||||
DEBUGSTR( 1, " Ignoring non-Windows process" );
|
||||
return 0;
|
||||
}
|
||||
|
110
readme.md
110
readme.md
@ -5,8 +5,8 @@ provides much the same functionality as `ANSI.SYS` does for MS-DOS.
|
||||
|
||||
## Requirements
|
||||
|
||||
* 32-bit: Windows 2000 Professional or later (it won't work with NT or 9X).
|
||||
* 64-bit: Vista or later (it won't work with XP64).
|
||||
* 32-bit: Windows 2000 Professional and later (it won't work with NT or 9X).
|
||||
* 64-bit: AMD64 (it won't work with IA64).
|
||||
|
||||
## Installation
|
||||
|
||||
@ -111,15 +111,17 @@ Using `ansicon` after install will always start with the default attributes,
|
||||
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 number of
|
||||
characters written, not the number of bytes. When using a multibyte character
|
||||
set, this results in a smaller number (since multiple bytes are used to
|
||||
represent a single character). Some programs recognise this as a reduced write
|
||||
and will inadvertently repeat previous characters. If you discover such a
|
||||
program, use the `ANSICON_API` environment variable to record it and override
|
||||
the API, returning the original byte count. Ruby (prior to 1.9.3) is an example
|
||||
of such a program, so use `set ANSICON_API=ruby` to avoid the repitition. The
|
||||
full syntax is:
|
||||
My version of `WriteConsoleA` will always set the number of characters written,
|
||||
not the number of bytes. This means writing a double-byte character as two
|
||||
bytes will set 0 the first write (nothing was written) and 1 the second (when
|
||||
the character was actually written); Windows normally sets 1 for both writes.
|
||||
Similarly, writing the individual bytes of a multibyte character will set 0 for
|
||||
all but the last byte, then 1 on the last; Windows normally sets 1 for each
|
||||
byte, writing the undefined character. However, my `WriteFile` (and
|
||||
`_lwrite`/`_hwrite`) will always set what was received; Windows, using a
|
||||
multibyte character set (but not DBCS), would set the characters. You can have
|
||||
`WriteConsoleA` return the original byte count by using the `ANSICON_API`
|
||||
environment variable:
|
||||
|
||||
ANSICON_API=[!]program;program;program...
|
||||
|
||||
@ -127,14 +129,16 @@ PROGRAM is the name of the program, with no path and extension. The leading
|
||||
exclamation inverts the usage, meaning the API will always be overridden, unless
|
||||
the program is in the list. The variable can be made permanent by going to
|
||||
_System Properties_, selecting the _Advanced_ tab (with Vista onwards, this can
|
||||
be done by running _"SystemPropertiesAdvanced"_) and clicking _Environment
|
||||
be done by running `SystemPropertiesAdvanced`) and clicking _Environment
|
||||
Variables_.
|
||||
|
||||
|
||||
## Limitations
|
||||
|
||||
- The entire console buffer is used, not just the visible window.
|
||||
- There's a conflict with NVIDIA's drivers, requiring the setting of the
|
||||
- Line sequences use the window; column sequences use the buffer.
|
||||
- An application using multiple screen buffers will not have separate
|
||||
attributes in each buffer.
|
||||
- There may be a conflict with NVIDIA's drivers, requiring the setting of the
|
||||
Environment Variable:
|
||||
|
||||
ANSICON_EXC=nvd3d9wrap.dll;nvd3d9wrapx.dll
|
||||
@ -146,38 +150,46 @@ Variables_.
|
||||
|
||||
The following escape sequences are recognised.
|
||||
|
||||
\e]0;titleBEL Set (xterm) window's title (and icon)
|
||||
\e[21t Report (xterm) window's title
|
||||
\e[s Save Cursor
|
||||
\e[u Restore Cursor
|
||||
\e[#G CHA Cursor Character Absolute
|
||||
\e[#E CNL Cursor Next Line
|
||||
\e[#F CPL Cursor Preceding Line
|
||||
\e[#D CUB Cursor Left
|
||||
\e[#B CUD Cursor Down
|
||||
\e[#C CUF Cursor Right
|
||||
\e[#;#H CUP Cursor Position
|
||||
\e[#A CUU Cursor Up
|
||||
\e[#P DCH Delete Character
|
||||
\e]0;titleBEL xterm: Set window's title (and icon, ignored)
|
||||
\e]2;titleBEL xterm: Set window's title
|
||||
\e[21t xterm: Report window's title
|
||||
\e[s ANSI.SYS: Save Cursor Position
|
||||
\e[u ANSI.SYS: Restore Cursor Position
|
||||
\e[#Z CBT Cursor Backward Tabulation
|
||||
\e[#G CHA Cursor Character Absolute
|
||||
\e[#I CHT Cursor Forward Tabulation
|
||||
\e[#E CNL Cursor Next Line
|
||||
\e[#F CPL Cursor Preceding Line
|
||||
\e[3h CRM Control Representation Mode (display controls)
|
||||
\e[3l CRM Control Representation Mode (perform controls)
|
||||
\e[#D CUB Cursor Left
|
||||
\e[#B CUD Cursor Down
|
||||
\e[#C CUF Cursor Right
|
||||
\e[#;#H CUP Cursor Position
|
||||
\e[#A CUU Cursor Up
|
||||
\e[#P DCH Delete Character
|
||||
\e[?7h DECAWM DEC Autowrap Mode (autowrap)
|
||||
\e[?7l DECAWM DEC Autowrap Mode (no autowrap)
|
||||
\e[?25h DECTCEM DEC Text Cursor Enable Mode (show cursor)
|
||||
\e[?25l DECTCEM DEC Text Cursor Enable Mode (hide cursor)
|
||||
\e[#M DL Delete Line
|
||||
\e[#n DSR Device Status Report
|
||||
\e[#X ECH Erase Character
|
||||
\e[#J ED Erase In Page
|
||||
\e[#K EL Erase In Line
|
||||
\e[#` HPA Character Position Absolute
|
||||
\e[#j HPB Character Position Backward
|
||||
\e[#a HPR Character Position Forward
|
||||
\e[#;#f HVP Character And Line Position
|
||||
\e[#@ ICH Insert Character
|
||||
\e[#L IL Insert Line
|
||||
SI LS0 Locking-shift Zero (see below)
|
||||
SO LS1 Locking-shift One
|
||||
\e[#;#;#m SGR Select Graphic Rendition
|
||||
\e[#d VPA Line Position Absolute
|
||||
\e[#k VPB Line Position Backward
|
||||
\e[#e VPR Line Position Forward
|
||||
\e[#M DL Delete Line
|
||||
\e[#n DSR Device Status Report
|
||||
\e[#X ECH Erase Character
|
||||
\e[#J ED Erase In Page
|
||||
\e[#K EL Erase In Line
|
||||
\e[#` HPA Character Position Absolute
|
||||
\e[#j HPB Character Position Backward
|
||||
\e[#a HPR Character Position Forward
|
||||
\e[#;#f HVP Character And Line Position
|
||||
\e[#@ ICH Insert Character
|
||||
\e[#L IL Insert Line
|
||||
SI LS0 Locking-shift Zero (see below)
|
||||
SO LS1 Locking-shift One
|
||||
\e[#b REP Repeat
|
||||
\e[#;#;#m SGR Select Graphic Rendition
|
||||
\e[#d VPA Line Position Absolute
|
||||
\e[#k VPB Line Position Backward
|
||||
\e[#e VPR Line Position Forward
|
||||
|
||||
- `\e` represents the escape character (ASCII 27).
|
||||
- `#` represents a decimal number (optional, in most cases defaulting to 1).
|
||||
@ -193,6 +205,12 @@ the latter will explicitly reset them. The environment variable `ANSICON_DEF`
|
||||
can be used to change the default colors (same value as `-m`; setting the
|
||||
variable does not change the current colors).
|
||||
|
||||
The first time a program clears the screen (`\e[2J`) will actually scroll in a
|
||||
new window (assuming the buffer is bigger than the window, of course).
|
||||
Subsequent clears will then blank the window. However, if the window has
|
||||
scrolled, or the cursor is on the last line of the buffer, it will again scroll
|
||||
in a new window.
|
||||
|
||||
|
||||
### Ignored Sequences
|
||||
|
||||
@ -214,7 +232,7 @@ http://vt100.net/docs/vt220-rm/table2-4.html.
|
||||
|
||||
Char Unicode Code Point & Name
|
||||
---- -------------------------
|
||||
_ U+0020 Space (blank)
|
||||
_ U+00A0 No-Break Space (blank)
|
||||
` U+2666 Black Diamond Suit
|
||||
a U+2592 Medium Shade
|
||||
b U+2409 Symbol For Horizontal Tabulation
|
||||
@ -299,4 +317,4 @@ In particular, the supplied binaries are freely redistributable.
|
||||
A formal license (zlib) is available in `LICENSE.txt`.
|
||||
|
||||
---
|
||||
Copyright 2005-2014 Jason Hood
|
||||
Copyright 2005-2015 Jason Hood
|
||||
|
53
readme.txt
53
readme.txt
@ -1,9 +1,9 @@
|
||||
|
||||
ANSICON
|
||||
|
||||
Copyright 2005-2014 Jason Hood
|
||||
Copyright 2005-2015 Jason Hood
|
||||
|
||||
Version 1.71. Freeware
|
||||
Version 1.72. Freeware
|
||||
|
||||
|
||||
Description
|
||||
@ -110,9 +110,10 @@ Usage
|
||||
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).
|
||||
created are ANSICON_VER, which contains the version without the point (1.50
|
||||
becomes "150"), and CLICOLOR (see http://bixense.com/clicolors/), which
|
||||
contains "1". These variables do not exist as part of the environment
|
||||
block (e.g. 'set an' will not show ANSICON_VER).
|
||||
|
||||
If installed, GUI programs will not be hooked. Either start the program
|
||||
directly with 'ansicon', or add it to the ANSICON_GUI variable (see
|
||||
@ -122,15 +123,17 @@ Usage
|
||||
utes, restoring the originals on exit; all other programs will use the cur-
|
||||
rent attributes. The shift state is always reset for a new process.
|
||||
|
||||
The Windows API WriteFile and WriteConsoleA functions will set the number
|
||||
of characters written, not the number of bytes. When using a multibyte
|
||||
character set, this results in a smaller number (since multiple bytes are
|
||||
used to represent a single character). Some programs recognise this as a
|
||||
reduced write and will inadvertently repeat previous characters. If you
|
||||
discover such a program, use the ANSICON_API environment variable to record
|
||||
it and override the API, returning the original byte count. Ruby (prior to
|
||||
1.9.3) is an example of such a program, so use 'set ANSICON_API=ruby' to
|
||||
avoid the repitition. The full syntax is:
|
||||
My version of WriteConsoleA will always set the number of characters writt-
|
||||
en, not the number of bytes. This means writing a double-byte character as
|
||||
two bytes will set 0 the first write (nothing was written) and 1 the second
|
||||
(when the character was actually written); Windows normally sets 1 for both
|
||||
writes. Similarly, writing the individual bytes of a multibyte character
|
||||
will set 0 for all but the last byte, then 1 on the last; Windows normally
|
||||
sets 1 for each byte, writing the undefined character. However, my
|
||||
WriteFile (and _lwrite/_hwrite) will always set what was received; Windows,
|
||||
using a multibyte character set (but not DBCS), would set the characters.
|
||||
You can have WriteConsoleA return the original byte count by using the
|
||||
ANSICON_API environment variable:
|
||||
|
||||
ANSICON_API=[!]program;program;program...
|
||||
|
||||
@ -277,7 +280,7 @@ Limitations
|
||||
Line sequences use the window; column sequences use the buffer.
|
||||
Tabs are fixed at eight columns.
|
||||
|
||||
There's a conflict with NVIDIA's drivers, requiring the setting of the
|
||||
There may be a conflict with NVIDIA's drivers, requiring the setting of the
|
||||
Environment Variable:
|
||||
|
||||
ANSICON_EXC=nvd3d9wrap.dll;nvd3d9wrapx.dll
|
||||
@ -291,8 +294,24 @@ Version History
|
||||
|
||||
Legend: + added, - bug-fixed, * changed.
|
||||
|
||||
1.72 - 24 December, 2015:
|
||||
- handle STD_OUTPUT_HANDLE & STD_ERROR_HANDLE in WriteFile;
|
||||
- better handling of unusual PE files;
|
||||
* cache GetConsoleMode for an improvement in speed;
|
||||
* files writing to the console always succeed (should mostly remove the
|
||||
need for ANSICON_API);
|
||||
* log: add a blank line between processes;
|
||||
remove the separate line for WriteFile & _lwrite;
|
||||
write byte strings as-is, wide strings using the current code page;
|
||||
use caret notation for control characters, with hexadecimal "^xNN"
|
||||
and "^uNNNN" for characters not in the code page (custom printf);
|
||||
* join multibyte characters split across separate writes;
|
||||
* remove wcstok, avoiding potential interference with the host program;
|
||||
* similarly, remove malloc & friends, using a private heap;
|
||||
+ add CLICOLOR dynamic environment variable.
|
||||
|
||||
1.71 - 23 October, 2015:
|
||||
+ add _CRT_NON_CONFORMING_WCSTOK define
|
||||
+ add _CRT_NON_CONFORMING_WCSTOK define for VS2015.
|
||||
|
||||
1.70 - 26 February, 2014:
|
||||
- don't hook again if using LoadLibrary or LoadLibraryEx;
|
||||
@ -501,4 +520,4 @@ Distribution
|
||||
|
||||
|
||||
==============================
|
||||
Jason Hood, 26 February, 2014.
|
||||
Jason Hood, 24 December, 2015.
|
||||
|
365
util.c
365
util.c
@ -74,99 +74,316 @@ void set_ansi_dll( void )
|
||||
}
|
||||
|
||||
|
||||
void DEBUGSTR( int level, LPTSTR szFormat, ... )
|
||||
static LPSTR buf;
|
||||
static DWORD buf_len;
|
||||
static BOOL quote, alt;
|
||||
|
||||
static DWORD str_format( DWORD pos, BOOL wide, DWORD_PTR str, DWORD len )
|
||||
{
|
||||
static char tempfile[MAX_PATH];
|
||||
static DWORD pid;
|
||||
|
||||
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')
|
||||
static UINT cp;
|
||||
static DWORD flags;
|
||||
static BOOL def, *pDef, start_trail;
|
||||
union
|
||||
{
|
||||
_snprintf( tempfile, MAX_PATH, "%s\\ansicon.log", getenv( "TEMP" ) );
|
||||
pid = GetCurrentProcessId();
|
||||
LPSTR a;
|
||||
LPWSTR w;
|
||||
} src;
|
||||
int ch;
|
||||
BOOL trail;
|
||||
|
||||
src.a = (LPSTR)str;
|
||||
if (len == 0 && str != 0)
|
||||
len = (DWORD)(wide ? wcslen( src.w ) : strlen( src.a ));
|
||||
|
||||
if (pos + len * 6 + 8 >= buf_len)
|
||||
{
|
||||
LPVOID tmp = HeapReAlloc( hHeap, 0, buf, buf_len + len * 6 + 8 );
|
||||
if (tmp == NULL)
|
||||
return 0;
|
||||
buf = tmp;
|
||||
buf_len = (DWORD)HeapSize( hHeap, 0, buf );
|
||||
}
|
||||
if (szFormat == NULL)
|
||||
|
||||
if (len == 0)
|
||||
{
|
||||
// Explicitly use 't', as _fmode might be binary.
|
||||
file = fopen( tempfile, (log_level & 8) ? "at" : "wt" );
|
||||
if (file != NULL)
|
||||
if (str == 0)
|
||||
pos += wsprintfA( buf + pos, "<null>" );
|
||||
else if (quote)
|
||||
{
|
||||
SYSTEMTIME now;
|
||||
GetLocalTime( &now );
|
||||
fseek( file, 0, SEEK_END );
|
||||
if (ftell( file ) != 0)
|
||||
putc( '\n', file );
|
||||
fprintf( file, "ANSICON (" BITSA "-bit) v" PVERSA " log (%d) started "
|
||||
"%d-%.2d-%.2d %d:%.2d:%.2d\n",
|
||||
log_level,
|
||||
now.wYear, now.wMonth, now.wDay,
|
||||
now.wHour, now.wMinute, now.wSecond );
|
||||
fclose( file );
|
||||
buf[pos++] = '"';
|
||||
buf[pos++] = '"';
|
||||
}
|
||||
return;
|
||||
else if (alt)
|
||||
pos += wsprintfA( buf + pos, "<empty>" );
|
||||
return pos;
|
||||
}
|
||||
|
||||
va_start( pArgList, szFormat );
|
||||
_vsnwprintf( szBuffer, lenof(szBuffer), szFormat, pArgList );
|
||||
va_end( pArgList );
|
||||
|
||||
szFormat = szBuffer;
|
||||
if (*szFormat == '\33')
|
||||
if (cp != GetConsoleOutputCP())
|
||||
{
|
||||
BOOL first = TRUE;
|
||||
LPTSTR pos = szEscape;
|
||||
while (*++szFormat != '\0' && pos < szEscape + lenof(szEscape) - 4)
|
||||
cp = GetConsoleOutputCP();
|
||||
if (wide)
|
||||
{
|
||||
if (*szFormat < 32)
|
||||
wchar_t und = L'\xFFFF';
|
||||
flags = WC_NO_BEST_FIT_CHARS;
|
||||
pDef = &def;
|
||||
// Some code pages don't support the default character.
|
||||
if (!WideCharToMultiByte( cp, flags, &und, 1, buf + pos, 12, NULL, pDef ))
|
||||
{
|
||||
*pos++ = '\\';
|
||||
switch (*szFormat)
|
||||
flags = 0;
|
||||
pDef = NULL;
|
||||
def = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (quote)
|
||||
buf[pos++] = '"';
|
||||
|
||||
trail = FALSE;
|
||||
while (len-- != 0)
|
||||
{
|
||||
if (wide)
|
||||
ch = *src.w++;
|
||||
else
|
||||
ch = (BYTE)*src.a++;
|
||||
|
||||
if (ch < 32 || (quote && start_trail))
|
||||
{
|
||||
start_trail = FALSE;
|
||||
if (quote)
|
||||
{
|
||||
buf[pos++] = '\\';
|
||||
switch (ch)
|
||||
{
|
||||
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;
|
||||
case '\0': buf[pos++] = '0'; break;
|
||||
case '\a': buf[pos++] = 'a'; break;
|
||||
case '\b': buf[pos++] = 'b'; break;
|
||||
case '\t': buf[pos++] = 't'; break;
|
||||
case '\n': buf[pos++] = 'n'; break;
|
||||
case '\v': buf[pos++] = 'v'; break;
|
||||
case '\f': buf[pos++] = 'f'; break;
|
||||
case '\r': buf[pos++] = 'r'; break;
|
||||
case 27 : buf[pos++] = 'e'; break;
|
||||
default:
|
||||
pos += _snwprintf( pos, 32, L"%.*o",
|
||||
(szFormat[1] >= '0' && szFormat[1] <= '7') ? 3 : 1,
|
||||
*szFormat );
|
||||
pos += wsprintfA( buf + pos, "x%.2X", ch );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*szFormat == '"')
|
||||
{
|
||||
if (first)
|
||||
first = FALSE;
|
||||
else if (szFormat[1] != '\0')
|
||||
*pos++ = '\\';
|
||||
}
|
||||
*pos++ = *szFormat;
|
||||
buf[pos++] = '^';
|
||||
buf[pos++] = ch + '@';
|
||||
}
|
||||
}
|
||||
*pos = '\0';
|
||||
szFormat = szEscape;
|
||||
else if (quote && ch == '"')
|
||||
{
|
||||
buf[pos++] = '\\';
|
||||
buf[pos++] = ch;
|
||||
}
|
||||
else if (!wide)
|
||||
{
|
||||
if (quote && (cp == 932 || cp == 936 || cp == 949 || cp == 950))
|
||||
{
|
||||
if (trail)
|
||||
trail = FALSE;
|
||||
else if (IsDBCSLeadByteEx( cp, (char)ch ))
|
||||
{
|
||||
if (len == 0)
|
||||
start_trail = TRUE;
|
||||
else
|
||||
trail = TRUE;
|
||||
}
|
||||
}
|
||||
if (quote && start_trail)
|
||||
pos += wsprintfA( buf + pos, "\\x%.2X", ch );
|
||||
else
|
||||
buf[pos++] = ch;
|
||||
}
|
||||
else
|
||||
{
|
||||
int mb = WideCharToMultiByte( cp, flags, src.w - 1, 1, buf + pos, 12,
|
||||
NULL, pDef );
|
||||
if (def)
|
||||
mb = wsprintfA( buf + pos, ch < 0x100 ? "%cx%.2X" : "%cu%.4X",
|
||||
(quote) ? '\\' : '^', ch );
|
||||
pos += mb;
|
||||
}
|
||||
}
|
||||
|
||||
mutex = CreateMutex( NULL, FALSE, L"ANSICON_debug_file" );
|
||||
wait = WaitForSingleObject( mutex, 500 );
|
||||
file = fopen( tempfile, "at" );
|
||||
if (file != NULL)
|
||||
{
|
||||
fwprintf( file, L"%s (%lu): %s\n", prog, pid, szFormat );
|
||||
fclose( file );
|
||||
}
|
||||
if (wait == WAIT_OBJECT_0)
|
||||
ReleaseMutex( mutex );
|
||||
CloseHandle( mutex );
|
||||
if (quote)
|
||||
buf[pos++] = '"';
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
void DEBUGSTR( int level, LPCSTR szFormat, ... )
|
||||
{
|
||||
static int prefix_len;
|
||||
static HANDLE mutex;
|
||||
static DWORD size;
|
||||
|
||||
WCHAR temp[MAX_PATH];
|
||||
HANDLE file;
|
||||
va_list pArgList;
|
||||
DWORD len, slen, written;
|
||||
DWORD_PTR num;
|
||||
|
||||
if ((log_level & 3) < level && !(level & 4 & log_level))
|
||||
return;
|
||||
|
||||
if (mutex == NULL)
|
||||
{
|
||||
mutex = CreateMutex( NULL, FALSE, L"ANSICON_debug_file" );
|
||||
if (mutex == NULL)
|
||||
{
|
||||
file = INVALID_HANDLE_VALUE;
|
||||
return;
|
||||
}
|
||||
buf = HeapAlloc( hHeap, 0, 2048 );
|
||||
buf_len = (DWORD)HeapSize( hHeap, 0, buf );
|
||||
prefix_len = wsprintfA( buf, "%S (%lu): ", prog, GetCurrentProcessId() );
|
||||
}
|
||||
if (WaitForSingleObject( mutex, 500 ) == WAIT_TIMEOUT)
|
||||
return;
|
||||
|
||||
ExpandEnvironmentStrings( L"%TEMP%\\ansicon.log", temp, lenof(temp) );
|
||||
file = CreateFile( temp, GENERIC_WRITE, FILE_SHARE_READ, NULL,
|
||||
(szFormat != NULL || (log_level & 8)) ? OPEN_ALWAYS
|
||||
: CREATE_ALWAYS,
|
||||
0, NULL );
|
||||
if (file == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
ReleaseMutex( mutex );
|
||||
CloseHandle( mutex );
|
||||
return;
|
||||
}
|
||||
|
||||
len = SetFilePointer( file, 0, NULL, FILE_END );
|
||||
if (len == 0 || szFormat == NULL)
|
||||
{
|
||||
char buf[128];
|
||||
SYSTEMTIME now;
|
||||
|
||||
size = 0;
|
||||
|
||||
if (len != 0)
|
||||
{
|
||||
memset( buf + 2, '=', 72 );
|
||||
buf[0] = buf[74] = buf[76] = '\r';
|
||||
buf[1] = buf[75] = buf[77] = '\n';
|
||||
WriteFile( file, buf, 78, &written, NULL );
|
||||
}
|
||||
|
||||
GetLocalTime( &now );
|
||||
len = wsprintfA( buf, "ANSICON (" BITSA "-bit) v" PVERSA " log (%d)"
|
||||
" started %d-%.2d-%.2d %d:%.2d:%.2d\r\n",
|
||||
log_level,
|
||||
now.wYear, now.wMonth, now.wDay,
|
||||
now.wHour, now.wMinute, now.wSecond );
|
||||
WriteFile( file, buf, len, &written, NULL );
|
||||
if (szFormat == NULL)
|
||||
{
|
||||
CloseHandle( file );
|
||||
ReleaseMutex( mutex );
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (len != size)
|
||||
WriteFile( file, "\r\n", 2, &written, NULL );
|
||||
|
||||
va_start( pArgList, szFormat );
|
||||
|
||||
// Customized printf, mainly to handle wide-character strings the way I want.
|
||||
// It only supports:
|
||||
// %u unsigned 32-bit decimal
|
||||
// %X unsigned 32-bit upper case hexadecimal
|
||||
// %p native pointer
|
||||
// %q native pointer, display as 32 bits
|
||||
// %P 32-bit pointer, display as 64 bits
|
||||
// %s null-terminated byte characters
|
||||
// %S null-terminated wide characters
|
||||
//
|
||||
// s & S may be prefixed with (in this order):
|
||||
// " quote the string, using C-style escapes and <null> for NULL
|
||||
// # use <null> for NULL and <empty> for ""
|
||||
// < length of the string is the previous %u
|
||||
// * length of the string is the parameter before the string
|
||||
//
|
||||
// Wide strings are converted according to the current code page; if a
|
||||
// character could not be translated, hex is used.
|
||||
//
|
||||
// C-style escapes are the standard backslash sequences, plus '\e' for ESC,
|
||||
// with '\x' used for two hex digits and '\u' for four. Otherwise, caret
|
||||
// notation is used to represent controls, with '^x'/'^u' for hex.
|
||||
|
||||
num = 0;
|
||||
len = prefix_len;
|
||||
while (*szFormat != '\0')
|
||||
{
|
||||
if (*szFormat != '%')
|
||||
buf[len++] = *szFormat++;
|
||||
else
|
||||
{
|
||||
quote = alt = FALSE;
|
||||
++szFormat;
|
||||
if (*szFormat == '"')
|
||||
{
|
||||
quote = TRUE;
|
||||
++szFormat;
|
||||
}
|
||||
if (*szFormat == '#')
|
||||
{
|
||||
alt = TRUE;
|
||||
++szFormat;
|
||||
}
|
||||
slen = 0;
|
||||
if (*szFormat == '<')
|
||||
{
|
||||
slen = (DWORD)num;
|
||||
++szFormat;
|
||||
}
|
||||
if (*szFormat == '*')
|
||||
{
|
||||
slen = va_arg( pArgList, DWORD );
|
||||
++szFormat;
|
||||
}
|
||||
num = va_arg( pArgList, DWORD_PTR );
|
||||
switch (*szFormat++)
|
||||
{
|
||||
case 'u': len += wsprintfA( buf + len, "%u", (DWORD)num ); break;
|
||||
case 'X': len += wsprintfA( buf + len, "%X", (DWORD)num ); break;
|
||||
case 'p':
|
||||
#ifdef _WIN64
|
||||
len += wsprintfA( buf + len, "%.8X_%.8X",
|
||||
(DWORD)(num >> 32), (DWORD)num );
|
||||
break;
|
||||
#endif
|
||||
case 'q': len += wsprintfA( buf + len, "%.8X", (DWORD)num ); break;
|
||||
case 'P': len += wsprintfA( buf + len, "00000000_%.8X", (DWORD)num ); break;
|
||||
case 's': len = str_format( len, FALSE, num, slen ); break;
|
||||
case 'S': len = str_format( len, TRUE, num, slen ); break;
|
||||
default:
|
||||
buf[len++] = '%';
|
||||
if (szFormat[-1] == '\0')
|
||||
--szFormat;
|
||||
else
|
||||
buf[len++] = szFormat[-1];
|
||||
}
|
||||
if (len >= buf_len - 20)
|
||||
{
|
||||
LPVOID tmp = HeapReAlloc( hHeap, 0, buf, buf_len + 128 );
|
||||
if (tmp == NULL)
|
||||
break;
|
||||
buf = tmp;
|
||||
buf_len = (DWORD)HeapSize( hHeap, 0, buf );
|
||||
}
|
||||
}
|
||||
}
|
||||
buf[len++] = '\r';
|
||||
buf[len++] = '\n';
|
||||
|
||||
WriteFile( file, buf, len, &written, NULL );
|
||||
|
||||
size = GetFileSize( file, NULL );
|
||||
CloseHandle( file );
|
||||
ReleaseMutex( mutex );
|
||||
}
|
||||
|
10
version.h
10
version.h
@ -2,11 +2,11 @@
|
||||
version.h - Version defines.
|
||||
*/
|
||||
|
||||
#define PVERS L"1.71" // wide string
|
||||
#define PVERSA "1.71" // ANSI string (windres 2.16.91 didn't like L)
|
||||
#define PVERE L"171" // wide environment string
|
||||
#define PVEREA "171" // ANSI environment string
|
||||
#define PVERB 1,7,1,0 // binary (resource)
|
||||
#define PVERS L"1.72" // wide string
|
||||
#define PVERSA "1.72" // ANSI string (windres 2.16.91 didn't like L)
|
||||
#define PVERE L"172" // wide environment string
|
||||
#define PVEREA "172" // ANSI environment string
|
||||
#define PVERB 1,7,2,0 // binary (resource)
|
||||
|
||||
#ifdef _WIN64
|
||||
# define BITS L"64"
|
||||
|
Loading…
x
Reference in New Issue
Block a user