2011-07-13 15 views
15

Mam aplikację GUI, którą opracowuję dla różnych platform dla systemów Linux i Windows. W Linuksie wszystko działa sprawnie. Jednak wpadłem w hak w systemie Windows. Chciałbym móc logować określone komunikaty do konsoli za pomocą aplikacji GUI w systemie Windows, w stylu Linux.Windows GUI + Console Output, Linux-style

Co mam na myśli w stylu Linuksa, jeśli program zostanie otwarty z konsoli, wyjście przejdzie do konsoli, ale jeśli program zostanie otwarty, na przykład, poprzez menu startowe, użytkownik nigdy nie zobaczy wyjście konsoli. Wydaje się, że jest to trudniejsze niż w Windowsie.

Obecnie używam następujący oszustwa w main():

#if _WINDOWS /* Fix console output on Windows */ 
if (AttachConsole(ATTACH_PARENT_PROCESS)) { 
    freopen("CONOUT$","wb",stdout); 
    freopen("CONOUT$","wb",stderr); 
} 
#endif 

To pozwala mi tworzyć wyjście przed okno jest rzeczywiście otwarty przez program, jak reagować na „--help” z wiersz poleceń. Jednak po zainicjowaniu i otwarciu okna przez mój program, konsola jest zwracana. Potrzebuję rozwiązania, które umożliwi mi nieprzerwany dostęp do konsoli przez cały okres mojego programu, bez otwierania nowej konsoli, jeśli żadna z nich nie była pierwotnie używana.

+1

related: [INFO: Wywoływanie procedur CRT Wyjście z aplikacji GUI] (http://support.microsoft.com/kb/105305) (zwłaszcza w sekcji "więcej informacji") – jfs

+0

related: [Jak to zrobić uzyskać dane wyjściowe konsoli w C++ za pomocą programu Windows?] (http://stackoverflow.com/q/191842/95735) –

Odpowiedz

0

Pamiętam, że przeczytałem coś na ten temat i jeśli dobrze pamiętam, rozwiązanie polegało na dodaniu gui do projektu konsolowego zamiast dodania konsoli do projektu GUI, ponieważ można to było zrobić tylko otwierając nową konsolę.

0

Myślę, że powinieneś stworzyć aplikację konsolową, a następnie sprawdzić kto zainicjował proces (prawdopodobnie cmd.exe) iw zależności od tego możesz ukryć okno konsoli. Potem utworzysz w nim okno ... na tym polega to, że okno konsoli może być otwarte przez chwilę, dopóki go nie ukryjesz i będzie wyglądało bardzo brzydko, tak przypuszczam. Otwieranie później konsoli nie ma tego problemu, ale nie wiem, czy standardowe wyjście przekierowuje do niego, tak jak ma to miejsce w aplikacjach konsolowych, czy też trzeba to jakoś ustawić, lub może trzeba przekierować w każdym wywołaniu ... nie musi być lepszy sposób!

2

Używamy :: AllocConsole() zamiast :: AttachConsole i pozostaje otwarte w całej aplikacji. Spróbuj tego?

0

Najlepszym rozwiązaniem, jakie znalazłem do tej pory, jest posiadanie dwóch plików wykonywalnych.

  • program.exe to aplikacja GUI.
  • program.com to aplikacja wiersza poleceń pomocnika, która spawnuje program.exe i przekazuje do niej standardowe operacje wejścia/wyjścia. (To nie jest wykonywalny COM z DOS, to tylko przemianowany norma PE wykonywalny, ponieważ .com jest przed .exe w domyślnej preferencji kolejności cmd.exe można wpisać program a zostanie ona automatycznie wywołać program.com zamiast program.exe jeśli oba są w ścieżce.)

z tej konfiguracji, można wpisać program w wierszu polecenia systemu Windows, pisać do standardowego wyjścia w program.exe i będzie prawidłowo wyświetlane na konsoli; żadne okna konsoli nie zostaną wyświetlone po otwarciu program.exe z GUI.

Oto przykład realizacja programu pomocnik, zaczerpnięte z Inkscape: http://bazaar.launchpad.net/~inkscape.dev/inkscape/trunk/view/head:/src/winconsole.cpp

Pomocnik tworzy trzy fajki i ikra program GUI z CreateProcess, nadając mu odpowiednie końce rur. Następnie tworzy trzy wątki, które kopiują dane między rurami a standardowymi we/wy programu pomocniczego w nieskończonej pętli.Pomocnik jest skompilowany jako aplikacja konsolowa (ważne) - przełącznik -mconsole w MinGW.

/** 
* \file 
* Command-line wrapper for Windows. 
* 
* Windows has two types of executables: GUI and console. 
* The GUI executables detach immediately when run from the command 
* prompt (cmd.exe), and whatever you write to standard output 
* disappears into a black hole. Console executables 
* do display standard output and take standard input from the console, 
* but when you run them from the GUI, an extra console window appears. 
* It's possible to hide it, but it still flashes for a fraction 
* of a second. 
* 
* To provide an Unix-like experience, where the application will behave 
* correctly in command line mode and at the same time won't create 
* the ugly console window when run from the GUI, we have to have two 
* executables. The first one, inkscape.exe, is the GUI application. 
* Its entry points are in main.cpp and winmain.cpp. The second one, 
* called inkscape.com, is a small helper application contained in 
* this file. It spawns the GUI application and redirects its output 
* to the console. 
* 
* Note that inkscape.com has nothing to do with "compact executables" 
* from DOS. It's a normal PE executable renamed to .com. The trick 
* is that cmd.exe picks .com over .exe when both are present in PATH, 
* so when you type "inkscape" into the command prompt, inkscape.com 
* gets run. The Windows program loader does not inspect the extension, 
* just like an Unix program loader; it determines the binary format 
* based on the contents of the file. 
* 
*//* 
* Authors: 
* Jos Hirth <[email protected]> 
* Krzysztof Kosinski <[email protected]> 
* 
* Copyright (C) 2008-2010 Authors 
* 
* Released under GNU GPL, read the file 'COPYING' for more information 
*/ 

#ifdef WIN32 
#undef DATADIR 
#include <windows.h> 

struct echo_thread_info { 
    HANDLE echo_read; 
    HANDLE echo_write; 
    unsigned buffer_size; 
}; 

// thread function for echoing from one file handle to another 
DWORD WINAPI echo_thread(void *info_void) 
{ 
    echo_thread_info *info = static_cast<echo_thread_info*>(info_void); 
    char *buffer = reinterpret_cast<char *>(LocalAlloc(LMEM_FIXED, info->buffer_size)); 
    DWORD bytes_read, bytes_written; 

    while(true){ 
     if (!ReadFile(info->echo_read, buffer, info->buffer_size, &bytes_read, NULL) || bytes_read == 0) 
      if (GetLastError() == ERROR_BROKEN_PIPE) 
       break; 

     if (!WriteFile(info->echo_write, buffer, bytes_read, &bytes_written, NULL)) { 
      if (GetLastError() == ERROR_NO_DATA) 
       break; 
     } 
    } 

    LocalFree(reinterpret_cast<HLOCAL>(buffer)); 
    CloseHandle(info->echo_read); 
    CloseHandle(info->echo_write); 

    return 1; 
} 

int main() 
{ 
    // structs that will store information for our I/O threads 
    echo_thread_info stdin = {NULL, NULL, 4096}; 
    echo_thread_info stdout = {NULL, NULL, 4096}; 
    echo_thread_info stderr = {NULL, NULL, 4096}; 
    // handles we'll pass to inkscape.exe 
    HANDLE inkscape_stdin, inkscape_stdout, inkscape_stderr; 
    HANDLE stdin_thread, stdout_thread, stderr_thread; 

    SECURITY_ATTRIBUTES sa; 
    sa.nLength=sizeof(SECURITY_ATTRIBUTES); 
    sa.lpSecurityDescriptor=NULL; 
    sa.bInheritHandle=TRUE; 

    // Determine the path to the Inkscape executable. 
    // Do this by looking up the name of this one and redacting the extension to ".exe" 
    const int pathbuf = 2048; 
    WCHAR *inkscape = reinterpret_cast<WCHAR*>(LocalAlloc(LMEM_FIXED, pathbuf * sizeof(WCHAR))); 
    GetModuleFileNameW(NULL, inkscape, pathbuf); 
    WCHAR *dot_index = wcsrchr(inkscape, L'.'); 
    wcsncpy(dot_index, L".exe", 4); 

    // we simply reuse our own command line for inkscape.exe 
    // it guarantees perfect behavior w.r.t. quoting 
    WCHAR *cmd = GetCommandLineW(); 

    // set up the pipes and handles 
    stdin.echo_read = GetStdHandle(STD_INPUT_HANDLE); 
    stdout.echo_write = GetStdHandle(STD_OUTPUT_HANDLE); 
    stderr.echo_write = GetStdHandle(STD_ERROR_HANDLE); 
    CreatePipe(&inkscape_stdin, &stdin.echo_write, &sa, 0); 
    CreatePipe(&stdout.echo_read, &inkscape_stdout, &sa, 0); 
    CreatePipe(&stderr.echo_read, &inkscape_stderr, &sa, 0); 

    // fill in standard IO handles to be used by the process 
    PROCESS_INFORMATION pi; 
    STARTUPINFOW si; 

    ZeroMemory(&si,sizeof(STARTUPINFO)); 
    si.cb = sizeof(STARTUPINFO); 
    si.dwFlags = STARTF_USESTDHANDLES; 
    si.hStdInput = inkscape_stdin; 
    si.hStdOutput = inkscape_stdout; 
    si.hStdError = inkscape_stderr; 

    // spawn inkscape.exe 
    CreateProcessW(inkscape, // path to inkscape.exe 
        cmd, // command line as a single string 
        NULL, // process security attributes - unused 
        NULL, // thread security attributes - unused 
        TRUE, // inherit handles 
        0, // flags 
        NULL, // environment - NULL = inherit from us 
        NULL, // working directory - NULL = inherit ours 
        &si, // startup info - see above 
        &pi); // information about the created process - unused 

    // clean up a bit 
    LocalFree(reinterpret_cast<HLOCAL>(inkscape)); 
    CloseHandle(pi.hThread); 
    CloseHandle(pi.hProcess); 
    CloseHandle(inkscape_stdin); 
    CloseHandle(inkscape_stdout); 
    CloseHandle(inkscape_stderr); 

    // create IO echo threads 
    DWORD unused; 
    stdin_thread = CreateThread(NULL, 0, echo_thread, (void*) &stdin, 0, &unused); 
    stdout_thread = CreateThread(NULL, 0, echo_thread, (void*) &stdout, 0, &unused); 
    stderr_thread = CreateThread(NULL, 0, echo_thread, (void*) &stderr, 0, &unused); 

    // wait until the standard output thread terminates 
    WaitForSingleObject(stdout_thread, INFINITE); 

    return 0; 
} 

#endif