2009-02-21 8 views
36

Tôi có quyền truy cập vào thư viện của bên thứ ba có "nội dung hay". Nó phát hành thông báo trạng thái và tiến trình để xuất dữ liệu. Trong ứng dụng Console, tôi có thể thấy các thông báo này tốt. Trong một ứng dụng Windows, họ chỉ cần vào nhóm bit.Làm cách nào để chuyển hướng stdout sang một số màn hình hiển thị có thể nhìn thấy trong Ứng dụng Windows?

Có cách khá đơn giản để chuyển hướng stdout và stderr sang điều khiển văn bản hoặc địa điểm hiển thị khác không. Lý tưởng nhất, điều này sẽ không yêu cầu bất kỳ biên dịch lại mã của bên thứ ba. Nó sẽ chỉ chặn hơi nước ở mức thấp. Tôi muốn một giải pháp mà tôi chỉ #include tiêu đề, gọi hàm khởi tạo và liên kết các thư viện như trong ...

#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"; 
} 

Thậm chí tốt hơn sẽ là nếu nó được sử dụng một số giao diện mà tôi có thể ghi đè lên nên nó là không gắn với bất kỳ thư viện GUI cụ thể nào.

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

Tôi chỉ mơ? Hay có ai biết điều gì đó có thể làm điều gì đó như thế này không?

Chỉnh sửa sau hai câu trả lời: Tôi nghĩ việc sử dụng freopen để chuyển hướng tệp là một bước đầu tiên tốt. Đối với một giải pháp hoàn chỉnh, sẽ cần phải tạo một chuỗi mới để đọc tệp và hiển thị đầu ra. Để gỡ lỗi, làm một 'đuôi -f' trong một cửa sổ vỏ Cygwin sẽ là đủ. Đối với một ứng dụng được đánh bóng hơn ... Đó là những gì tôi muốn viết ... sẽ có thêm một số công việc để tạo chủ đề, v.v.

+0

'AllocConsole' là phần còn thiếu. Nó tạo ra cửa sổ giao diện quen thuộc. Các chức năng đọc và ghi là khó xử, nhưng bạn có thể chuyển hướng stdout/stdin đến/từ nó khá dễ dàng bằng cách sử dụng phương pháp này: https://stackoverflow.com/a/15547699/133836 –

Trả lời

16

Bạn có thể chuyển hướng stdout, stderr và stdin bằng cách sử dụng freopen.

Từ liên kết ở trên:

/* 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; 
} 

Bạn cũng có thể chạy chương trình của bạn thông qua cửa sổ lệnh như vậy:

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

Khi bạn tạo ra một quá trình sử dụng CreateProcess() bạn có thể chọn một HANDLE mà stdout và stderr sẽ được viết. Điều này HANDLE có thể là một tập tin mà bạn trực tiếp đầu ra.

Điều này sẽ cho phép bạn sử dụng mã mà không cần biên dịch lại. Chỉ cần thực hiện nó và thay vì sử dụng system() hoặc không có gì, hãy sử dụng CreateProcess().

Tay cầm bạn cung cấp cho CreateProcess() cũng có thể là của ống bạn đã tạo và sau đó bạn có thể đọc từ đường ống và làm điều gì đó khác với dữ liệu.

3

Bạn có thể làm một cái gì đó như thế này với cout hoặc 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); 

Hoặc, bạn có thể làm điều đó với một std::stringstream hoặc một số lớp khác có nguồn gốc từ std::ostream.

Để chuyển hướng thiết bị xuất chuẩn, bạn cần phải mở lại xử lý tệp. This thread có một số ý tưởng về bản chất này.

+0

Điều này sẽ không giúp bạn chuyển hướng khác (sinh ra) chương trình của stdout để bất cứ nơi nào theo ý thích của bạn. –

+0

Câu hỏi đề cập đến thư viện của bên thứ ba. Ông không đề cập đến sự cần thiết phải chuyển hướng đầu ra của một quy trình khác. – greyfade

18

Bạn cần phải tạo ống (với CreatePipe()), sau đó đính kèm stdout để nó viết kết thúc với SetStdHandle(), sau đó bạn có thể đọc từ cuối đọc ống với ReadFile() và đặt văn bản mà bạn nhận được từ có vị trí mong muốn.

0

Đây là những gì tôi muốn làm:

  1. CreatePipe().
  2. CreateProcess() với tay cầm từ CreatePipe() được sử dụng làm giá trị cho quá trình mới.
  3. Tạo bộ hẹn giờ hoặc chuỗi gọi ReadFile() trên bộ xử lý đó ngay bây giờ và sau đó đặt dữ liệu vào một hộp văn bản hoặc không có gì.
1

Nhờ có sự liên kết gamedev trong câu trả lời của greyfade, tôi đã có thể viết và kiểm tra mảnh này đơn giản mã

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; 

Nó hoạt động và là đủ để gỡ lỗi. Đưa văn bản vào phần tử GUI hấp dẫn hơn sẽ mất nhiều công sức hơn một chút.

13

Có lẽ bạn đang tìm kiếm cái gì đó dọc những dòng:

#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; 
    } 

Lớp này sẽ làm điều đó:

#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; 
} 

Dưới đây là kết quả:

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

Rất hữu ích. Đúng thứ tôi cần. –

+0

Rất hữu ích cho tôi, cảm ơn – alexpov

2

Dưới đây chúng tôi sẽ đặt điểm nhập mới consoleMain ghi đè điểm nhập của riêng bạn.

  1. Xác định điểm vào của ứng dụng của bạn. Trong VisualStudio, chọn Thuộc tính dự án/Trình liên kết/Điểm nâng cao/Entry. Chúng ta hãy gọi nó là defaultMain.
  2. Một nơi nào đó trong mã nguồn của bạn khai báo điểm vào ban đầu (vì vậy chúng tôi có thể xích vào nó) và điểm vào mới. Cả hai phải được khai báo extern "C" để ngăn chặn mangling tên.

    extern "C" 
    { 
        int defaultMain (void); 
        int consoleMain (void); 
    } 
    
  3. Thực hiện chức năng điểm nhập.

    __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. Thêm mã kiểm tra của bạn ở đâu đó, ví dụ: trong thao tác nhấp nút.

    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. Set consoleMain như là điểm mục mới (Project Properties/Linker/Advanced/Entry Point).