programing

Windows 응용 프로그램에서 보이는 디스플레이로 stdout을 리디렉션하려면 어떻게 해야 합니까?

cafebook 2023. 8. 26. 12:17
반응형

Windows 응용 프로그램에서 보이는 디스플레이로 stdout을 리디렉션하려면 어떻게 해야 합니까?

"좋은 일"을 하는 타사 라이브러리에 액세스할 수 있습니다.stdout에 상태 및 진행률 메시지를 발행합니다.콘솔 응용 프로그램에서 이러한 메시지를 볼 수 있습니다.Windows 애플리케이션에서는 비트 버킷으로 이동합니다.

stdout 및 stderr를 텍스트 컨트롤 또는 다른 보이는 위치로 리디렉션하는 상당히 간단한 방법이 있습니까?이상적으로는 타사 코드의 재컴파일이 필요하지 않습니다.그것은 낮은 수준에서 증기를 차단할 것입니다.헤더를 #포함하고 초기화 함수를 호출하여 라이브러리를 링크하는 솔루션을 원합니다.

#include "redirectStdFiles.h"

void function(args...)
{
  TextControl* text = new TextControl(args...);
  initializeRedirectLibrary(text, ...);

  printf("Message that will show up in the TextControl\n");
  std::cout << "Another message that also shows up in TextControl\n";
}

특정 GUI 라이브러리에 연결되지 않도록 오버라이드할 수 있는 인터페이스를 사용한다면 더욱 좋을 것입니다.

class StdFilesRedirector
{
  public:
    writeStdout(std::string const& message) = 0;
    writeStderr(std::string const& errorMessage) = 0;
    readStdin(std::string &putReadStringHere) = 0;
};

나는 그냥 꿈을 꾸고 있는 건가요?아니면 이런 일을 할 수 있는 것을 아는 사람이 있습니까?

두 개의 답변 뒤에 편집:freopen을 사용하여 파일을 리디렉션하는 것이 좋은 첫 단계라고 생각합니다.전체 솔루션을 사용하려면 파일을 읽고 출력을 표시하기 위해 새 스레드가 생성되어야 합니다.디버깅의 경우, cygwin 쉘 창에서 'tail-f'를 수행하면 충분할 것입니다.좀 더 세련된 응용을 위해서는제가 쓰고 싶은 것은...스레드를 만드는 등 추가 작업이 있을 것입니다.

파이프를 만든 다음(CreatePipe()사용하여) SetStdHandle()을 사용하여 파이프의 쓰기 끝에 stdout을 연결한 다음 ReadFile()을 사용하여 파이프의 읽기 끝에서 읽고 원하는 위치에 텍스트를 넣을 수 있습니다.

freopen을 사용하여 stdout, stderr 및 stdin을 리디렉션할 수 있습니다.

위 링크에서:

/* freopen example: redirecting stdout */
#include <stdio.h>

int main ()
{
  freopen ("myfile.txt","w",stdout);
  printf ("This sentence is redirected to a file.");
  fclose (stdout);
  return 0;
}

다음과 같은 명령 프롬프트를 통해 프로그램을 실행할 수도 있습니다.

a.exe > stdout.txt 2> stderr.txt

당신은 아마도 다음과 같은 것을 찾고 있을 것입니다.

    #define OUT_BUFF_SIZE 512

    int main(int argc, char* argv[])
    {
        printf("1: stdout\n");

        StdOutRedirect stdoutRedirect(512);
        stdoutRedirect.Start();
        printf("2: redirected stdout\n");
        stdoutRedirect.Stop();

        printf("3: stdout\n");

        stdoutRedirect.Start();
        printf("4: redirected stdout\n");
        stdoutRedirect.Stop();

        printf("5: stdout\n");

        char szBuffer[OUT_BUFF_SIZE];
        int nOutRead = stdoutRedirect.GetBuffer(szBuffer,OUT_BUFF_SIZE);
        if(nOutRead)
            printf("Redirected outputs: \n%s\n",szBuffer);

        return 0;
    }

이 클래스는 다음 작업을 수행합니다.

#include <windows.h>

#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <iostream>

#ifndef _USE_OLD_IOSTREAMS
using namespace std;
#endif

#define READ_FD 0
#define WRITE_FD 1

#define CHECK(a) if ((a)!= 0) return -1;

class StdOutRedirect
{
    public:
        StdOutRedirect(int bufferSize);
        ~StdOutRedirect();

        int Start();
        int Stop();
        int GetBuffer(char *buffer, int size);

    private:
        int fdStdOutPipe[2];
        int fdStdOut;
};

StdOutRedirect::~StdOutRedirect()
{
    _close(fdStdOut);
    _close(fdStdOutPipe[WRITE_FD]);
    _close(fdStdOutPipe[READ_FD]);
}
StdOutRedirect::StdOutRedirect(int bufferSize)
{
    if (_pipe(fdStdOutPipe, bufferSize, O_TEXT)!=0)
    {
        //treat error eventually
    }
    fdStdOut = _dup(_fileno(stdout));
}

int StdOutRedirect::Start()
{
    fflush( stdout );
    CHECK(_dup2(fdStdOutPipe[WRITE_FD], _fileno(stdout)));
    ios::sync_with_stdio();
    setvbuf( stdout, NULL, _IONBF, 0 ); // absolutely needed
    return 0;
}

int StdOutRedirect::Stop()
{
    CHECK(_dup2(fdStdOut, _fileno(stdout)));
    ios::sync_with_stdio();
    return 0;
}

int StdOutRedirect::GetBuffer(char *buffer, int size)
{
    int nOutRead = _read(fdStdOutPipe[READ_FD], buffer, size);
    buffer[nOutRead] = '\0';
    return nOutRead;
}

결과는 다음과 같습니다.

1: stdout
3: stdout
5: stdout
Redirected outputs:
2: redirected stdout
4: redirected stdout

CreateProcess()사용하여 프로세스를 생성할 때 다음을 선택할 수 있습니다.HANDLE될 .stdout과 stderr이 됩니다. 것이.HANDLE출력을 지시하는 파일일 수 있습니다.

이렇게 하면 코드를 다시 컴파일하지 않고 사용할 수 있습니다.사용하지 않고 실행하기만 하면 됩니다.system()또는 무엇을 사용하지 않음CreateProcess().

이 주핸들는에게 .CreateProcess()작성한 파이프의 파이프일 수도 있습니다. 그런 다음 파이프에서 데이터를 읽고 다른 작업을 수행할 수 있습니다.

당신은 cout이나 cerr로 다음과 같은 것을 할 수 있습니다.

// open a file stream
ofstream out("filename");
// save cout's stream buffer
streambuf *sb = cout.rdbuf();
// point cout's stream buffer to that of the open file
cout.rdbuf(out.rdbuf());
// now you can print to file by writing to cout
cout << "Hello, world!";
// restore cout's buffer back
cout.rdbuf(sb);

아니면, 당신은 그것을 할 수 있습니다.std::stringstream또는 에서 파생된 다른 클래스.std::ostream.

stdout을 리디렉션하려면 파일 핸들을 다시 열어야 합니다. 실은 이런 종류의 아이디어를 가지고 있습니다.

제가 할 일은 다음과 같습니다.

  1. 파이프를 만듭니다.
  2. 새 프로세스에 대한 stdout으로 사용된 CreatePipe()의 핸들을 사용하여 CreateProcess().
  3. 때때로 해당 핸들에서 ReadFile()을 호출하고 읽은 데이터를 텍스트 상자에 넣는 타이머나 스레드를 만듭니다.

여기서 새로운 진입점을 설정합니다.consoleMain당신 자신의 것을 무시합니다.

  1. 응용프로그램의 시작 지점을 결정합니다.Visual Studio에서 Project Properties/Linker/Advanced/Entry Point를 선택합니다.끝내자꾸나defaultMain.
  2. 원본 코드의 어딘가에서 원래 진입점(체인으로 연결할 수 있도록)과 새 진입점을 선언합니다.둘 다 선언해야 합니다.extern "C"이름의 망글링을 방지하기 위해.

    extern "C"
    {
      int defaultMain (void);
      int consoleMain (void);
    }
    
  3. 진입점 기능을 구현합니다.

    __declspec(noinline) int consoleMain (void)
    {
      // __debugbreak(); // Break into the program right at the entry point!
      AllocConsole();    // Create a new console
      freopen("CON", "w", stdout);
      freopen("CON", "w", stderr);
      freopen("CON", "r", stdin); // Note: "r", not "w".
      return defaultMain();
    }
    
  4. 예를 들어 버튼 클릭 동작에서 테스트 코드를 추가합니다.

    fwprintf(stdout, L"This is a test to stdout\n");
    fwprintf(stderr, L"This is a test to stderr\n");
    cout<<"Enter an Integer Number Followed by ENTER to Continue" << endl;
    _flushall();
    int i = 0;
    int Result = wscanf( L"%d", &i);
    printf ("Read %d from console. Result = %d\n", i, Result);
    
  5. 세트consoleMain새 진입점(프로젝트 속성/링커/고급/진입점)으로 지정합니다.

그레이페이드의 답변에 있는 게임 개발 링크 덕분에, 저는 이 간단한 코드 조각을 쓰고 테스트할 수 있었습니다.

AllocConsole();

*stdout = *_tfdopen(_open_osfhandle((intptr_t) GetStdHandle(STD_OUTPUT_HANDLE), _O_WRONLY), _T("a"));
*stderr = *_tfdopen(_open_osfhandle((intptr_t) GetStdHandle(STD_ERROR_HANDLE), _O_WRONLY), _T("a"));
*stdin = *_tfdopen(_open_osfhandle((intptr_t) GetStdHandle(STD_INPUT_HANDLE), _O_WRONLY), _T("r"));


printf("A printf to stdout\n");
std::cout << "A << to std::cout\n";
std::cerr << "A << to std::cerr\n";
std::string input;
std::cin >> input;

std::cout << "value read from std::cin is " << input << std::endl;

작동하며 디버깅에 적합합니다.텍스트를 더 매력적인 GUI 요소로 전환하려면 작업이 조금 더 필요합니다.

언급URL : https://stackoverflow.com/questions/573724/how-can-i-redirect-stdout-to-some-visible-display-in-a-windows-application

반응형