2011-02-25 60 views
2

我正在构建一个既可以是图形又可以纯粹使用命令行界面的工具。 由用户决定使用命令行选项“--command_line_only”。我的IDE是Visual Studio的2008如何使用命令行界面创建图形工具?

我似乎无法找到合适的项目属性,以确保

  1. 标准输出打印使用命令行界面模式
  2. 没有命令行框中显示的时在其图形模式下使用该工具时打开

有没有办法做到这一点? Visual Studio的devenv看起来像这样,所以我很确定它可以完成!

回答

1

编辑:我似乎已经回答了您的标题,但重新阅读您的问题时,我不确定这是你问的问题。我留下的答案在这里,因为它可能是有用的人搜索您的标题

是的,你可以做到这一点,但一般来说,视觉研究并不容易分离代码从gui(特别是从你好世界的例子是非常有约束力)。如果你想混合输入,那么你真的想确保从一开始就做得很好。

我的建议是从命令行选项开始。如果您可以使用增强功能(用于监听原因),然后使用boost::program_options

然后,一旦你有了这个,你可以在上面添加gui。另外,我会推荐使用gui库,比如gtk ++,因为它是跨平台的。

1

将应用程序构建为Win32应用程序(而不是控制台应用程序),并检查参数以决定是否使用控制台窗口。

以下代码是based on this

使用控制台;创建一个名为CConsoleAttacher的类。使用它如下,基本上如果有参数,然后打开一个控制台,将连接到CMD窗口,如果你从那开始。显然,与Win32应用程序,当您启动它从控制台脱离所以这需要创建应用程序的窗口前早就处理的主窗口(这是您无论如何要怎么办...)

CConsoleAttacher ca; 
    if (ca.hasArguments()) 
    { 
     ca.ConnectToConsole(); 
    } 
    printf ("Test output \n"); 

创建一个类称为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); 
}; 
+0

你的答案似乎很有趣,我不得不包括但它似乎并不足够。 _O_TEXT仍未定义。我应该包含哪些文件? –

+0

我找到了包含正确的文件,但不起作用。当从cmd框启动时,它创建一个新的(可能在那里打印东西),然后关闭它。 –

+0

这似乎有点奇怪。 'HANDLE'可以有8个字节宽,但是你把它的长度设置为4个字节。你确定? –

1

这不是一件容易的事,IIRC。问题在于,在Windows上,可执行文件本身具有标志,无论它是GUI应用程序还是控制台应用程序,例如当执行一个或另一个时,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:; 
} 
+0

如果我必须这样做,这很容易做到。我将简单地编译我的可执行文件的两个版本,第一个使用标志控制台,第二个使用标志窗口!但显然我想只有一个。因此我的问题。 –

+0

@Benoît:IIRC,问题是用GUI标记的可执行文件。 cmd.exe不会等待它返回。 – wilx