Как я могу создавать графические инструменты с интерфейсом командной строки? - PullRequest
2 голосов
/ 25 февраля 2011

Я создаю инструмент, который может быть как графическим, так и чисто с интерфейсом командной строки.Пользователь должен решить с помощью параметра командной строки "--command_line_only".Моя IDE - Visual Studio 2008.

Не получается найти правильное свойство проекта, чтобы убедиться, что

  1. стандартные выходные данные отображаются при использовании режима интерфейса командной строки
  2. при использовании инструмента в графическом режиме окно командной строки не открывается

Есть ли способ сделать это?Кажется, что devenv в Visual Studio ведет себя так, и я уверен, что это возможно!

Ответы [ 3 ]

1 голос
/ 25 февраля 2011

Создайте приложение как приложение Win32 (не консольное приложение) и проверьте параметры, чтобы решить, следует ли использовать окно консоли.

Следующий код - на основе этого .

Для использования консоли;создайте класс с именем CConsoleAttacher.Используйте его следующим образом, в основном, если есть аргументы, откройте консоль, которая подключится к окну CMD, если вы начали с этого.Очевидно, что с приложениями Win32, когда вы запускаете главные окна, он отсоединяется от консоли, так что это нужно обработать на ранних стадиях, прежде чем создавать окна приложений (что в любом случае вы хотите сделать ...)класс называется CConsoleAttacher.

CConsoleAttacher.cpp

#include "StdAfx.h"

#include <windows.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <iostream>
#include <fstream>
#include "ConsoleAttacher.h"

#ifndef _USE_OLD_IOSTREAMS
using namespace std;
#endif
static const WORD MAX_CONSOLE_LINES = 500;

CConsoleAttacher::CConsoleAttacher(void)
{
    argv = CommandLineToArgvW(GetCommandLineW(), &argc);

}


CConsoleAttacher::~CConsoleAttacher(void)
{
    LocalFree(argv);
}


int CConsoleAttacher::getArgumentCount(void)
{
    return argc;
}


CString CConsoleAttacher::getArgument(int id)
{
    CString arg ;
    if (id < argc)
    {
        arg = argv[id];
    }
    return arg;
}

bool CConsoleAttacher::hasArguments(void)
{
    return argc > 1;
}

void CConsoleAttacher::ConnectToConsole(void)
{
    int hConHandle;
    HANDLE lStdHandle;
    CONSOLE_SCREEN_BUFFER_INFO coninfo;
    FILE *fp;

    // allocate a console for this app
    if (!AttachConsole(ATTACH_PARENT_PROCESS))
    {
        if (!AllocConsole())
            return;
    }

    // set the screen buffer to be big enough to let us scroll text
    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),&coninfo);
    coninfo.dwSize.Y = MAX_CONSOLE_LINES;

    SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),coninfo.dwSize);

    // redirect unbuffered STDOUT to the console
    lStdHandle = GetStdHandle(STD_OUTPUT_HANDLE);
    hConHandle = _open_osfhandle((intptr_t)lStdHandle, _O_TEXT);
    fp = _fdopen( hConHandle, "w" );

    *stdout = *fp;
    setvbuf( stdout, NULL, _IONBF, 0 );

    // redirect unbuffered STDIN to the console
    lStdHandle = GetStdHandle(STD_INPUT_HANDLE);
    hConHandle = _open_osfhandle((intptr_t)lStdHandle, _O_TEXT);
    fp = _fdopen( hConHandle, "r" );

    *stdin = *fp;
    setvbuf( stdin, NULL, _IONBF, 0 );

    // redirect unbuffered STDERR to the console
    lStdHandle = GetStdHandle(STD_ERROR_HANDLE);
    hConHandle = _open_osfhandle((intptr_t)lStdHandle, _O_TEXT);
    fp = _fdopen( hConHandle, "w" );

    *stderr = *fp;
    setvbuf( stderr, NULL, _IONBF, 0 );

    // make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog
    // point to console as well
    ios::sync_with_stdio();
}

CConsoleAttacher.h

#pragma once
class CConsoleAttacher
{
private:
    int argc;
    wchar_t** argv;

public:
    CConsoleAttacher(void);
    ~CConsoleAttacher(void);
    int getArgumentCount(void);
    CString CConsoleAttacher::getArgument(int id);
    void ConnectToConsole(void);
    bool hasArguments(void);
};
1 голос
/ 25 февраля 2011

Это нелегко сделать, IIRC. Проблема состоит в том, что в Windows сам исполняемый файл имеет флаг, будь то приложение с графическим интерфейсом или консольное приложение и, например, cmd.exe ведет себя по-разному при выполнении одного или другого. Я предлагаю разделить основные функциональные возможности вашего приложения на библиотеку и создать отдельные интерфейсы CLI и GUI.

РЕДАКТИРОВАТЬ : Если вы действительно настаиваете, это имеет большое значение для достижения цели. goto существует по историческим причинам, его можно заменить на if сейчас:

static
bool
usable_handle (HANDLE h)
{
    return h && h != INVALID_HANDLE_VALUE;
}


static
bool
try_reopen_std_handle (int dest_handle, DWORD os_handle_num, HANDLE os_handle,
    int flags)
{
    if (! usable_handle (os_handle))
        return false;

    int ret = SetStdHandle (os_handle_num, os_handle);
    assert (ret);
    if (! ret)
        return false;

    int base_flags = 0;
#if defined (UNICODE)
    //base_flags = _O_WTEXT;
#endif

    int opened_handle = _open_osfhandle (reinterpret_cast<intptr_t>(os_handle),
        flags | base_flags);
    assert (opened_handle != -1 && "_open_osfhandle");
    if (opened_handle == -1)
        return false;

    int dupd_handle = _dup2 (opened_handle, dest_handle);
    assert (dupd_handle != -1 && "_dup2");

    return dupd_handle == 0;
}


static
bool
try_fdopen (FILE * f, int handle, char const * mode)
{
    FILE * tmp = _fdopen (handle, mode);
    if (tmp && f)
        *f = *tmp;
    return !! tmp;
}


static
HANDLE
try_dup_os_handle (HANDLE src)
{
    if (! usable_handle (src))
        return INVALID_HANDLE_VALUE;

    HANDLE dest = INVALID_HANDLE_VALUE;
    HANDLE const process = GetCurrentProcess ();
    if (DuplicateHandle (process, src, process, &dest, 0, TRUE,
        DUPLICATE_SAME_ACCESS))
        return dest;
    else
        return INVALID_HANDLE_VALUE;
}


static
void
init_std_io ()
{
    // Retrieve inherited standard handles. AttachConsole() will close
    // the existing standard handles, so we duplicate them here first
    // to keep them alive.

    HANDLE os_stdin = try_dup_os_handle (GetStdHandle (STD_INPUT_HANDLE));
    HANDLE os_stdout = try_dup_os_handle (GetStdHandle (STD_OUTPUT_HANDLE));
    HANDLE os_stderr = try_dup_os_handle (GetStdHandle (STD_ERROR_HANDLE));

    // Attach existing console or allocate a new one.

    int ret = AttachConsole (ATTACH_PARENT_PROCESS);
    if (ret)
        OutputDebugString (_T("Attached existing console.\n"));
    else
    {
        ret = AllocConsole ();
        if (ret)
            OutputDebugString (_T("Allocated new console.\n"));
        else
            OutputDebugString (_T("Failed to allocate new console.\n"));
        assert (ret);
    }

    // Open a "POSIX" handle for each OS handle and then fdopen() a C stream
    // for each such "POSIX" handle.
    //
    // Only use the standard handle provided by AttachConsole() if the standard
    // handle from before AttachConsole() is not usable.
    //
    // Finally, re-open standard C stream.

    if (! usable_handle (os_stdin))
        os_stdin = GetStdHandle (STD_INPUT_HANDLE);
    ret = try_reopen_std_handle (0, STD_INPUT_HANDLE, os_stdin, _O_RDONLY);
    if (! ret)
        goto do_stdout;
    try_fdopen (stdin, 0, "r");

do_stdout:
    if (! usable_handle (os_stdout))
        os_stdout = GetStdHandle (STD_OUTPUT_HANDLE);
    ret = try_reopen_std_handle (1, STD_OUTPUT_HANDLE, os_stdout, _O_WRONLY);
    if (! ret)
        goto do_stderr;
    try_fdopen (stdout, 1, "w");

do_stderr:
    if (! usable_handle (os_stderr))
        os_stderr = GetStdHandle (STD_ERROR_HANDLE);
    ret = try_reopen_std_handle (2, STD_ERROR_HANDLE, os_stderr, _O_WRONLY);
    if (! ret)
        goto done_stderr;
    try_fdopen (stderr, 2, "w");

done_stderr:;
}
1 голос
/ 25 февраля 2011

РЕДАКТИРОВАТЬ: Я, кажется, ответил на ваш заголовок, но, перечитывая ваш вопрос, я не уверен, что это то, что вы спрашиваете. Я оставляю ответ здесь, так как он может быть полезен для тех, кто ищет ваш заголовок

Да, вы можете сделать это, но в целом визуальные исследования не позволяют легко отделить код от графического интерфейса пользователя (в частности, от примеров hello world, которые очень связаны). Если вы хотите смешать ввод, то вы действительно хотите убедиться, что это сделано очень хорошо с самого начала.

Мой совет - начать с параметров командной строки. Если вам разрешено использовать boost (по причинам лицензирования), тогда используйте boost :: program_options .

Затем, как только вы это сделаете, вы можете добавить графический интерфейс сверху. Кроме того, я бы рекомендовал использовать библиотеку графического интерфейса, такую ​​как gtk ++, поскольку она кроссплатформенная.

...