Tuesday, November 1, 2016

"Total Commander" starter / loader.

src\mcrt.c

//    _ __   _____      _________  _ __ _ __ ___
//   | '_ \ / _ \ \ /\ / /_  / _ \| '__| '__/ _ \
//   | | | | (_) \ V  V / / / (_) | |  | | | (_) |
//   |_| |_|\___/ \_/\_/ /___\___/|_|  |_|  \___/
//
//
//   https://nowzorro.blogspot.com/2016/11/total-commander-starter.html
//   Version: 2017-05-22
//

#include 

#if _WIN64

#pragma function(memset)
void* __cdecl memset(void *dest, int value, size_t num)
{
    unsigned char *p = dest;
    while (num-- > 0)
        *p++ = (unsigned char)value;
    return dest;
}

#pragma function(memcpy)
void* __cdecl memcpy(void *dest, const void *source, size_t num)
{
    char *pd = dest;
    const char *ps = source;
    while (num-- > 0)
        *pd++ = *ps++;
    return dest;
}

#pragma function(wcslen)
size_t __cdecl wcslen(const wchar_t *str)
{
    size_t len = 0;
    while (*str++)
        ++len;
    return len;    
}

#pragma function(strlen)
size_t __cdecl strlen(const char *str)
{
    size_t len = 0;
    while (*str++)
        ++len;
    return len;
}

#else // _WIN32

#pragma function(memset)
void* __cdecl memset(void *dest, int value, size_t num)
{
    _asm
    {
        push ecx
        push edi
        mov eax, value
        mov ecx, num
        mov edi, dest
        rep stosb
        pop edi
        pop ecx
    }
    return dest;
}

#pragma function(memcpy)
void* __cdecl memcpy(void *dest, const void *source, size_t num)
{
    _asm
    {
        push ecx
        push edi
        push esi
        mov  ecx, num
        mov  edi, dest
        mov  esi, source
        rep  movsb
        pop  esi
        pop  edi
        pop  ecx
    }
    return dest;
}

#pragma function(wcslen)
size_t __cdecl wcslen(const wchar_t *s)
{
    _asm
    {
        push  edi
        mov   edi, s
        sub   ecx, ecx
        not   ecx
        sub   ax, ax
        cld
        repne scasw
        not   ecx
        pop   edi
        lea   eax, [ecx-1]
    }
}

#pragma function(strlen)
size_t __cdecl strlen(const char *s)
{
    _asm
    {
        push  edi
        mov   edi, s
        sub   ecx, ecx
        not   ecx
        sub   al, al
        cld
        repne scasb
        not   ecx
        pop   edi
        lea   eax, [ecx-1]
    }
}

#endif // _WIN64

src\tc_starter.c

//    _ __   _____      _________  _ __ _ __ ___
//   | '_ \ / _ \ \ /\ / /_  / _ \| '__| '__/ _ \
//   | | | | (_) \ V  V / / / (_) | |  | | | (_) |
//   |_| |_|\___/ \_/\_/ /___\___/|_|  |_|  \___/
//
//
//   https://nowzorro.blogspot.com/2016/11/total-commander-starter.html
//   Version: 2017-05-22
//

#include 

#define INIT_WAIT_TIMEOUT 5000
#define TRY_FIND_TIMEOUT  5000
#define TRY_FIND_SLEEP    10
#define TRY_FIND_COUNT    (TRY_FIND_TIMEOUT / TRY_FIND_SLEEP)

enum
{
    E_OK = 0,
    E_RUN,
    E_HOOK,
    E_BTN
};

const TCHAR *MESSAGE[] =
{
    0,
    TEXT("Cannot run Total Commander."),
    TEXT("Initialization hook error."),
    TEXT("Button window not found")
};

const TCHAR *NAG_CLASS = TEXT("TNASTYNAGSCREEN");
const TCHAR *NAG_TITLE = TEXT("Total Commander");

#if _WIN64

const TCHAR *PATHS[] =
{
    TEXT("TOTALCMD64.EXE"),
    TEXT("c:\\totalcmd\\TOTALCMD64.EXE"),
    TEXT("c:\\Program Files\\totalcmd\\TOTALCMD64.EXE")
};

const TCHAR *TITLES[] =
{
    TEXT("Window"),
    TEXT("Window"),
    TEXT("Window"),
    
    TEXT("Window"),
    TEXT("Button")
};

#else // !_WIN64

const TCHAR *PATHS[] =
{
    TEXT("TOTALCMD.EXE"),
    TEXT("c:\\totalcmd\\TOTALCMD.EXE"),
    TEXT("c:\\Program Files\\totalcmd\\TOTALCMD.EXE")
};

const TCHAR *TITLES[] =
{
    TEXT("TNotebook"),
    TEXT("TPage"),
    TEXT("TPanel"),
    
    TEXT("TPanel"),
    TEXT("TButton")
};

#endif // _WIN64

extern void* __cdecl memset(void*, int, size_t);
extern void* __cdecl memcpy(void*, const void*, size_t);
extern size_t __cdecl wcslen(const wchar_t*);
extern size_t __cdecl strlen(const char*);

#ifdef _UNICODE
#define tcslen wcslen
#else
#define tcslen strlen
#endif

// Return process handle
HANDLE runProcess(const TCHAR *path)
{
    TCHAR buf[MAX_PATH];
    size_t size = tcslen(path) * sizeof(TCHAR);
    if (!size || size >= sizeof(buf))
        return NULL;
    memset(&buf, 0, sizeof(buf));
    memcpy(&buf, path, size);

    STARTUPINFO si;
    memset(&si, 0, sizeof(si));
    si.cb = sizeof(si);
    PROCESS_INFORMATION pi;
    memset(&pi, 0, sizeof(pi));

    if (!CreateProcess(NULL, buf, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
        return NULL;
    CloseHandle(pi.hThread);
    return pi.hProcess;
}

TCHAR* skipClExePath(TCHAR *s)
{
    if (TEXT('"') == *s)
    {
        ++s;
        for (; *s && *s != TEXT('"'); ++s);
        if (TEXT('"') == *s)
            ++s;
    }
    else
        for (; *s > TEXT(' '); ++s);
    for (; *s && *s <= TEXT(' '); ++s);
    return s;
}

HWND findNearLabel(HWND hParent)
{
    const TCHAR *LBL_CLASS = TITLES[_countof(TITLES) - 2];
    const TCHAR *BTN_CLASS = TITLES[_countof(TITLES) - 1];
    TCHAR title[3] = TEXT("&\0");
    HWND hLabel = NULL;    
    while (hLabel = FindWindowEx(hParent, hLabel, LBL_CLASS, NULL))
    {
        int len = (int)GetWindowText(hLabel, &title[1], 2);
        if ((len == 1) &&
            ((title[1] == '1') || (title[1] == '2') || (title[1] == '3')))
        {
            return FindWindowEx(hParent, NULL, BTN_CLASS, title);
        }
    }
    return NULL;
}

HWND scanSubTreeWnd(HWND hParent, size_t titleZIndex)
{
    HWND hChild = NULL;
    while (hChild = FindWindowEx(hParent, hChild, TITLES[titleZIndex], NULL))
    {
        if (titleZIndex == _countof(TITLES) - 3)
            return findNearLabel(hChild);
        HWND ret = scanSubTreeWnd(hChild, titleZIndex + 1);
        if (ret)
            return ret;
    }
    return NULL;
}

int work(TCHAR *commandLine)
{
    HANDLE hProcess = 0;
    if (!commandLine || commandLine[0] == TEXT('\0'))
    {
        for (size_t i = 0; i < _countof(PATHS) && !hProcess; i++)
            hProcess = runProcess(PATHS[i]);
    }
    else
        hProcess = runProcess(commandLine);

    if (!hProcess)
        return E_RUN;

    // Wait until app has finished initialization
    if (WaitForInputIdle(hProcess, INIT_WAIT_TIMEOUT) != 0)
    {
        CloseHandle(hProcess);
        return E_HOOK;
    }
    CloseHandle(hProcess);

    // Find windows
    HWND hParent = NULL;
    HWND hButton = NULL;
    for (size_t i = TRY_FIND_COUNT; i--; Sleep(TRY_FIND_SLEEP))
    {
        if (!hParent)
            hParent = FindWindow(NAG_CLASS, NAG_TITLE);
        if (!hParent)
            continue;
        if (hButton = scanSubTreeWnd(hParent, 0))
        {
            SetForegroundWindow(hParent);
            SendMessage(hButton, WM_LBUTTONDOWN, 0, 0);
            SendMessage(hButton, WM_LBUTTONUP, 0, 0);             
            return E_OK;
        }
    }

    return E_BTN;
}

void WINAPI main()
{
    TCHAR *cl = GetCommandLine();
    cl = skipClExePath(cl);
    int ret = work(cl);
    if (ret != E_OK)
        MessageBox(NULL, MESSAGE[ret], TEXT("Error"), MB_OK | MB_ICONERROR);
    ExitProcess(ret);
}

make.cmd

::    _ __   _____      _________  _ __ _ __ ___
::   | '_ \ / _ \ \ /\ / /_  / _ \| '__| '__/ _ \
::   | | | | (_) \ V  V / / / (_) | |  | | | (_) |
::   |_| |_|\___/ \_/\_/ /___\___/|_|  |_|  \___/
::
::   Usage: this.cmd [x64] [ansi]
::

@echo off

:: Parse params
:PARSE_PARAMS_LOOP
if (%1)==(x64) set NOWZORRO_ARCH=amd64
if (%1)==(ansi) set NOWZORRO_ANSI=A
shift
if (%1)==() goto :PARAMS_PARSED
goto :PARSE_PARAMS_LOOP
:PARAMS_PARSED

:: Init
if not (%NOWZORRO_ARCH%)==() (
    set NOWZORRO_PATHSUF=\x64
    set NOWZORRO_OUTSUF=64
    set NOWZORRO_CL=
    set NOWZORRO_LINK=/SUBSYSTEM:WINDOWS,5.02
) else (
    set NOWZORRO_ARCH=x86
    set NOWZORRO_PATHSUF=
    set NOWZORRO_OUTSUF=
    set NOWZORRO_CL=/GL
    set NOWZORRO_LINK=/SUBSYSTEM:WINDOWS,5.01 /LTCG
)

:: XP Toolset
set SDK71PATH=%ProgramFiles%\Microsoft SDKs\Windows\7.1A
path %SDK71PATH%\Bin%NOWZORRO_PATHSUF%;%PATH%
set CL=%NOWZORRO_CL% /W3 /O1 /GF /GS- /MT /EHsa- ^
    /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D_USING_V110_SDK71_  
if (%NOWZORRO_ANSI%)==() set CL=%CL% /D "_UNICODE" /D "UNICODE"
set LINK=%NOWZORRO_LINK% /OPT:REF /OPT:ICF /DYNAMICBASE:NO ^
    /NXCOMPAT:NO /FIXED /NODEFAULTLIB /MERGE:.rdata=.text /ENTRY:main
set INCLUDE=%INCLUDE%;%SDK71PATH%\Include
set LIB=%LIB%;%SDK71PATH%\Lib%NOWZORRO_PATHSUF%

:: Prepare VC++2017 compiler
set VSPATH=%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Community
call "%VSPATH%\Common7\Tools\VsDevCmd.bat" -arch=%NOWZORRO_ARCH%
if errorlevel 1 pause & goto :EOF

:: Build
if not exist bin mkdir bin
cl.exe /nologo src\mcrt.c src\tc_starter.c "kernel32.lib" "user32.lib" ^
       /link /NOLOGO /OUT:bin\tc_starter%NOWZORRO_OUTSUF%%NOWZORRO_ANSI%.exe ^
       & del *.obj
if errorlevel 1 pause
       
:EOF
exit

usage.txt

    _ __   _____      _________  _ __ _ __ ___
   | '_ \ / _ \ \ /\ / /_  / _ \| '__| '__/ _ \
   | | | | (_) \ V  V / / / (_) | |  | | | (_) |
   |_| |_|\___/ \_/\_/ /___\___/|_|  |_|  \___/



1) If TC installed in:
    - Current dir
    - c:\totalcmd\
    - c:\Program Files\totalcmd\
    
    then simple run starter:
    tc_starter.exe [-uac]

2) Or set command line parameter in shortcut:
    "STARTER_PATH\tc_starter.exe" [-uac] "TOTALCMD_PATH\TOTALCMD.exe"
    "STARTER_PATH\tc_starter.exe" [-uac] "TOTALCMD_PATH\TOTALCMD.exe"^
        "I=INI_PATH\wincmd.ini"
 
3) Or use in .bat/.cmd
    start "" "STARTER_PATH\tc_starter.exe" [-uac] "TOTALCMD_PATH\TOTALCMD.exe"
    start "" "STARTER_PATH\tc_starter.exe" [-uac] "TOTALCMD_PATH\TOTALCMD.exe"^
        "I=INI_PATH\wincmd.ini"
    cmd /k   "STARTER_PATH\tc_starter.exe" [-uac] "TOTALCMD_PATH\TOTALCMD.exe"
    cmd /k   "STARTER_PATH\tc_starter.exe" [-uac] "TOTALCMD_PATH\TOTALCMD.exe"^
        "I=INI_PATH\wincmd.ini"    
    
Note: Use icon from TOTALCMD.exe in shortcut.

No comments:

Post a Comment