1. Post #1
    Xentec's Avatar
    October 2007
    49 Posts
    Greetings Facepunch

    I'm writing a cross-plattform server manager application which should start Source servers silently (no window) and capture their output.
    But the problem is that srcds.exe doesn't like it and refuses to cooperate.

    In detail:
    Code:
    //defined in process.h
    ::HANDLE hIn, hOut, hErr;
    ::PROCESS_INFORMATION pid;
    //-------------
    void Process::start(std::string exe) {
        std::wstring uexe;
        utf8::utf8to16(exe.begin(),exe.end(),std::back_inserter(uexe));
    
        ::STARTUPINFO ProcSi;
        ::memset(&ProcSi, 0, sizeof(ProcSi));
        ProcSi.cb = sizeof(ProcSi);
    
        ::SECURITY_ATTRIBUTES ProcSA;
        ProcSA.nLength = sizeof(SECURITY_ATTRIBUTES);
        ProcSA.lpSecurityDescriptor = 0;
        ProcSA.bInheritHandle = 1;
    
        ::HANDLE hInTmp[2], hOutTmp[2], hErrTmp[2];
    
        //::CreatePipe(&hInTmp[1], &hInTmp[0], &ProcSA,0);
        ::CreatePipe(&hOutTmp[0],&hOutTmp[1],&ProcSA,0);
        ::CreatePipe(&hErrTmp[0],&hErrTmp[1],&ProcSA,0);
    
        //::DuplicateHandle(GetCurrentProcess(),hInTmp[0],  GetCurrentProcess(),&hIn,  0,0,DUPLICATE_SAME_ACCESS);
        ::DuplicateHandle(GetCurrentProcess(),hOutTmp[0], GetCurrentProcess(),&hOut, 0,0,DUPLICATE_SAME_ACCESS);
        ::DuplicateHandle(GetCurrentProcess(),hErrTmp[0], GetCurrentProcess(),&hErr, 0,0,DUPLICATE_SAME_ACCESS);
    
        //::CloseHandle(hInTmp[0]);
        ::CloseHandle(hOutTmp[0]);
        ::CloseHandle(hErrTmp[0]);
    
        ProcSi.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
        ProcSi.wShowWindow = SW_HIDE;
    
        //ProcSi.hStdInput = hInTmp[1];
        ProcSi.hStdOutput = hOutTmp[1];
        ProcSi.hStdError = hErrTmp[1];
    
        BOOL e = ::CreateProcess( NULL,   // No module name (use command line)
            const_cast<wchar_t*>(uexe.c_str()),        // Command line
            NULL,                // Process handle not inheritable
            NULL,                // Thread handle not inheritable
            TRUE,                // handle inheritance
            0,                    // No creation flags
            NULL,                // Use parent's environment block
            NULL,                // Use parent's starting directory
            &ProcSi,            // Pointer to STARTUPINFO structure
            &pid );                // Pointer to PROCESS_INFORMATION structure
        DWORD err = ::GetLastError();
    
        //::CloseHandle(hInTmp[1]);
        ::CloseHandle(hOutTmp[1]);
        ::CloseHandle(hErrTmp[1]);
        if(!e && err)
            throw new Exception(__func__,err);
    }
    
    long Process::readStdout(char* data, ulong size) {
    	DWORD read;
    	BOOL e = ReadFile(hOut, data, (DWORD)size, &read, 0);
    	DWORD err = ::GetLastError();
    	if(!e && err)
    		throw new Exception(__func__,err);
    	return (long) read;
    }
    CreateProcess (WinAPI) is configured to redirect stdin, -out and -err of the child process through a pipe to parent and then start srcds.
    Works well until srcds crashes with the error:
    CTextConsoleWin32::GetLine: !GetNumberOfConsoleInputEvents

    I've searched for solutions and one is not to redirect stdin which I tried (as seen in code) but it still won't work. The only way
    without crash is to remove STARTF_USESTDHANDLES flag that also prevents from getting the std output.

    Hiding the srcds window appears also impossible with dwFlags and CreateProcess creation flag parameter.


    So my questions are:

    o Is there a workaround to get srcds.exe console output?
    o Is there also a way to hide it's window properly?
    o And how can I filter these junk bytes which I get by reading the stdout pipe?

    Thanks in advance.

  2. Post #2
    Gold Member
    Jookia's Avatar
    July 2007
    6,534 Posts
    Use a shell script and pipes.
    Reply With Quote Edit / Delete Reply Linux Australia Show Events Agree Agree x 2 (list)

  3. Post #3
    Xentec's Avatar
    October 2007
    49 Posts
    Nope. Already tried with simple redirection to text files.
    Result:
    stdout.txt posted:
    Using breakpad minidump system
    [stopped server here]
    Reference Count for Material __depthwrite00 (-1) != 0
    Reference Count for Material __depthwrite01 (-1) != 0
    Reference Count for Material __depthwrite10 (-1) != 0
    Reference Count for Material __depthwrite11 (-1) != 0
    Reference Count for Material __particlesdepthwrite (1) != 0
    That's all I got.
    As I said, Source hates pipes so I need something other.

  4. Post #4
    Gold Member
    jack5500's Avatar
    November 2007
    91 Posts
    Tryed it in C#, seemed impossible too. :/
    Reply With Quote Edit / Delete Reply Windows 7 Germany Show Events Agree Agree x 2 (list)

  5. Post #5
    burak575's Avatar
    January 2008
    83 Posts
    If srcds.exe is a win32 application it probably creating its own console window by using win32 api which is "AllocConsole" function.
    Then setting the std in out and err to different values which will work with new console.

    So creating the pipes when process is first started maybe taking the pointers which going to be replaced.

    There is lot of ways to accomplish this stuff. I am not investigated exe but, most hardcore way will be injecting dll which finds and patches AllocConsole or even printf function and pipe wherever you want (if they did this way).

    Let me investigate executables for accurate solution.

    Edited:

    Okay first findings,

    srcds.exe is just a placeholder executable which loads the bin/dedicated.dll and informs the steam about start of application.

    dedicated.dll uses AllocConsole, FreeConsole stuff. When AllocConsole success it does some calls.

    So you may try DuplicateHandle after this point. Like using simple random untrustable delays of few second sleep or waiting for it properly.

    If you want to wait properly;

    Program stores its handles for console output and input after AllocConsole in virtual address of 100E4E40 and 100E4E2C in dedicated.dll. If you want to find this address you can create signatures for this function 10009F40.

    But it will be better to attach as debugger to your child process, and just search that signature and create a break point where it calls getstdhandle and use that handle for your dirty jobs.

    Another way is creating a small placeholder asm function or dll that you could inject to process and modify import table of dedicated.dll to route AllocConsole to your function. Which will call original AllocConsole and GetStdHandle and inform your application.

    There could be simple ways, but I like hackish ways :P

    For cross-platform , I don't think they used stuff like these in linux. So I assume regular piping should work.

    If you making this as open source or you willing send the code, I can implement these stuff in it.
    Reply With Quote Edit / Delete Reply Windows 7 Turkey Show Events Informative Informative x 3Friendly Friendly x 1Artistic Artistic x 1 (list)

  6. Post #6
    Xentec's Avatar
    October 2007
    49 Posts
    Many thanks!

    I've made also some progress.
    srcds crash was resolved by letting real stdin to child process through.
    - ProcSi.hStdInput = hInTmp[1];
    + ProcSi.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
    And "junk bytes" were removed by using utf16to8 conversion on the stdout pipe.

  7. Post #7
    Gold Member
    deathshead's Avatar
    June 2005
    25 Posts
    stop trying
    Reply With Quote Edit / Delete Reply Windows 7 United States Show Events Dumb Dumb x 8Late Late x 1 (list)

  8. Post #8
    stop posting
    Reply With Quote Edit / Delete Reply Netherlands Show Events Winner Winner x 3Friendly Friendly x 1 (list)

  9. Post #9
    Se1f_Distruct's Avatar
    April 2011
    633 Posts
    Patch the undesired function calls in olly?
    Reply With Quote Edit / Delete Reply United States Show Events Dumb Dumb x 1 (list)