Prompting a user with an input box? [C++]
Asked Answered
T

8

17

My goal is to simply use a pop-up box to ask the user for an input. I've searched around quite a bit and pretty much all the results say that creating a messageBox is really easy:

MessageBox (NULL, "Hello World" , "Hello", MB_OKCANCEL);

But that creating a pop-up that takes input is more involved and there isn't a straight forward way to do it. All of the results I could find on Google were dated somewhere from 2001 to 2005. I guess I'm here asking if some more straight forward solution has come about in recent years.

Hopefully something nice and straight forward like in Java:

int number = JOptionPane.showInputDialog ("Enter an integer");

If that isn't the case, could I get a brief explanation of how to do it?


Edit: I couldn't get anything to work. :( I ended up writing the code to do the work in Java, and then wrote one line of C++ code to call the .jar file. :-/ Since the issue was time sensitive, it was better than nothing.

Tyus answered 17/11, 2010 at 4:7 Comment(1)
tinyfiedialogs offers the simplest way to add an inputbox to any C or C++ project. It's a single C file (+header). It also includes bindings to other languages. sourceforge.net/projects/tinyfiledialogs (I'm the author)Cristicristian
L
8

There is nothing like that for pure C++. Basically what you're trying to do can only be achieved by using an API call to the OS or by using some GUI library like Qt (which I recommend cause it's waaaaay easier then calling native APIs and it's also multi-platform)

Using Qt you can show an input dialog pretty much the same way you do it on java:

bool ok;
QString text = QInputDialog::getText(
        "MyApp 3000", "Enter your name:", QLineEdit::Normal,
        QString::null, &ok, this );
if ( ok && !text.isEmpty() ) {
    // user entered something and pressed OK
} else {
    // user entered nothing or pressed Cancel
}

You can download the Qt library here: qt.nokia.com/products/developer-tools/

Lineal answered 17/11, 2010 at 4:16 Comment(3)
I've downloaded the SDK and the VS2010 add-in; do you know the best way to go about adding this functionality to a preexisting project?Tyus
why not downloading qt from "qt.io/download"?Ignaz
Because this was answered almost 13 years ago.Lineal
T
8

If you are using Visual C++ Express there are a number of free resource editors that can be used to create dialogs. ResEdit is one of the better ones I've found.

You need to create a dialog resource in a .RC file that you add to your project.

Then, It is a very simple case of calling DialogBox - which will load the dialog box from your resource file and place it on the screen. The passed in DialogProc will be called with a number of notifications. Typically you would want to return FALSE for everything, but handle WM_INITDIALOG as a place to initialize the edit control with text, and WM_COMMAND will be sent when a button is clicked.

Trude answered 17/11, 2010 at 8:36 Comment(0)
U
4

Microsoft doesn't consider your use case to be common enough to optimize for, as with MessageBox. They expect you to lay out a dialog with many controls on it, perhaps with some complex interaction with the controls, and only respond once the dialog is fully filled in. What you're asking for is just the simplified version of that.

The resource editor is the easiest way to create a dialog, but that's not included in the free Express version of Visual Studio. You would design the dialog with a text control for the prompt and an edit control for the user to fill in. You present the dialog with the DialogBox Windows function, and it returns when the user hits the OK button or the X in the corner of the dialog. Microsoft has some documentation for it here.

There are a few platforms available that try to make the process easier, such as MFC, WTL, Qt, and wx, but this is how you'd do it with the pure Windows API.

Ubangi answered 17/11, 2010 at 4:40 Comment(0)
V
4

My answer is based on Stephen Quan's answer to How to load & call a VBScript function from within C++? Added full UTF-8 support, as you can gather from the code comments in the CPP file. Unlike using Microsoft Script Control to create the InputBox, this can be used in x86 and x64 executables, libraries, and controls.

"inputbox.h":

extern "C" char *InputBox(char *Prompt, char *Title = (char *)"", char *Default = (char *)"");
extern "C" char *PasswordBox(char *Prompt, char *Title = (char *)"", char *Default = (char *)"");

"inputbox.cpp":

#include "inputbox.h"

// Windows API
#include <windows.h>

// VBScript InputBox
#include <atlbase.h>
#include <activscp.h>
#include <comdef.h>

// UTF-8 Support
#include <wchar.h>
#include <string>
#include <vector>

using std::string;
using std::wstring;
using std::vector;

static wstring StringWiden(string Str) {
  const size_t wchar_tCount = Str.size() + 1;
  vector<wchar_t> Buffer(wchar_tCount);
  return wstring { Buffer.data(), (size_t)MultiByteToWideChar(CP_UTF8, 0, Str.c_str(), -1, Buffer.data(), wchar_tCount) };
}

static string StringShorten(wstring Str) {
  int nBytes = (size_t)WideCharToMultiByte(CP_UTF8, 0, Str.c_str(), (int)Str.length(), NULL, 0, NULL, NULL);
  vector<char> Buffer((size_t)nBytes);
  return string { Buffer.data(), (size_t)WideCharToMultiByte(CP_UTF8, 0, Str.c_str(), (int)Str.length(), Buffer.data(), nBytes, NULL, NULL) };
}

static string StringReplaceAll(string Str, string SubStr, string NewStr) {
  size_t Position = 0;
  const size_t SubLen = SubStr.length(), NewLen = NewStr.length();
  while ((Position = Str.find(SubStr, Position)) != string::npos) {
    Str.replace(Position, SubLen, NewStr);
    Position += NewLen;
  }
  return Str;
}

static string CPPNewLineToVBSNewLine(string NewLine) {
  size_t Position = 0;
  while (Position < NewLine.length()) {
    if (NewLine[Position] == '\r' || NewLine[Position] == '\n')
      NewLine.replace(Position, 2, "\" + vbNewLine + \"");
    Position += 1;
  }
  return NewLine;
}

class CSimpleScriptSite :
  public IActiveScriptSite,
  public IActiveScriptSiteWindow {
public:
  CSimpleScriptSite() : m_cRefCount(1), m_hWnd(NULL) { }

  // IUnknown
  STDMETHOD_(ULONG, AddRef)();
  STDMETHOD_(ULONG, Release)();
  STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject);

  // IActiveScriptSite
  STDMETHOD(GetLCID)(LCID* plcid) { *plcid = 0; return S_OK; }
  STDMETHOD(GetItemInfo)(LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown** ppiunkItem, ITypeInfo** ppti) { return TYPE_E_ELEMENTNOTFOUND; }
  STDMETHOD(GetDocVersionString)(BSTR* pbstrVersion) { *pbstrVersion = SysAllocString(L"1.0"); return S_OK; }
  STDMETHOD(OnScriptTerminate)(const VARIANT* pvarResult, const EXCEPINFO* pexcepinfo) { return S_OK; }
  STDMETHOD(OnStateChange)(SCRIPTSTATE ssScriptState) { return S_OK; }
  STDMETHOD(OnScriptError)(IActiveScriptError* pIActiveScriptError) { return S_OK; }
  STDMETHOD(OnEnterScript)(void) { return S_OK; }
  STDMETHOD(OnLeaveScript)(void) { return S_OK; }

  // IActiveScriptSiteWindow
  STDMETHOD(GetWindow)(HWND* phWnd) { *phWnd = m_hWnd; return S_OK; }
  STDMETHOD(EnableModeless)(BOOL fEnable) { return S_OK; }

  // Miscellaneous
  STDMETHOD(SetWindow)(HWND hWnd) { m_hWnd = hWnd; return S_OK; }

public:
  LONG m_cRefCount;
  HWND m_hWnd;
};

STDMETHODIMP_(ULONG) CSimpleScriptSite::AddRef() {
  return InterlockedIncrement(&m_cRefCount);
}

STDMETHODIMP_(ULONG) CSimpleScriptSite::Release() {
  if (!InterlockedDecrement(&m_cRefCount)) {
    delete this;
    return 0;
  }
  return m_cRefCount;
}

STDMETHODIMP CSimpleScriptSite::QueryInterface(REFIID riid, void **ppvObject)
{
  if (riid == IID_IUnknown || riid == IID_IActiveScriptSiteWindow) {
    *ppvObject = (IActiveScriptSiteWindow*)this;
    AddRef();
    return NOERROR;
  }
  if (riid == IID_IActiveScriptSite) {
    *ppvObject = (IActiveScriptSite*)this;
    AddRef();
    return NOERROR;
  }
  return E_NOINTERFACE;
}

static HHOOK hHook = 0;
static bool HideInput = false;

static LRESULT CALLBACK InputBoxProc(int nCode, WPARAM wParam, LPARAM lParam) {
  if (nCode < HC_ACTION)
    return CallNextHookEx(hHook, nCode, wParam, lParam);
  if (nCode = HCBT_ACTIVATE) {
    if (HideInput == true) {
      HWND TextBox = FindWindowExA((HWND)wParam, NULL, "Edit", NULL);
      SendDlgItemMessageW((HWND)wParam, GetDlgCtrlID(TextBox), EM_SETPASSWORDCHAR, L'\x25cf', 0);
    }
  }
  if (nCode = HCBT_CREATEWND) {
    if (!(GetWindowLongPtr((HWND)wParam, GWL_STYLE) & WS_CHILD))
      SetWindowLongPtr((HWND)wParam, GWL_EXSTYLE, GetWindowLongPtr((HWND)wParam, GWL_EXSTYLE) | WS_EX_DLGMODALFRAME);
  }
  return CallNextHookEx(hHook, nCode, wParam, lParam);
}

static char *InputBoxHelper(char *Prompt, char *Title, char *Default) {
  // Initialize
  HRESULT hr = S_OK;
  hr = CoInitialize(NULL);
  CSimpleScriptSite* pScriptSite = new CSimpleScriptSite();
  CComPtr<IActiveScript> spVBScript;
  CComPtr<IActiveScriptParse> spVBScriptParse;
  hr = spVBScript.CoCreateInstance(OLESTR("VBScript"));
  hr = spVBScript->SetScriptSite(pScriptSite);
  hr = spVBScript->QueryInterface(&spVBScriptParse);
  hr = spVBScriptParse->InitNew();

  // Replace quotes with double quotes
  string strPrompt = StringReplaceAll(Prompt, "\"", "\"\"");
  string strTitle = StringReplaceAll(Title, "\"", "\"\"");
  string strDefault = StringReplaceAll(Default, "\"", "\"\"");

  // Create evaluation string
  string Evaluation = "InputBox(\"" + strPrompt + "\", \"" + strTitle + "\", \"" + strDefault + "\")";
  Evaluation = CPPNewLineToVBSNewLine(Evaluation);
  wstring WideEval = StringWiden(Evaluation);

  // Run InpuBox
  CComVariant result;
  EXCEPINFO ei = {};
  DWORD ThreadID = GetCurrentThreadId();
  HINSTANCE ModHwnd = GetModuleHandle(NULL);
  hr = pScriptSite->SetWindow(GetAncestor(GetActiveWindow(), GA_ROOTOWNER));
  hHook = SetWindowsHookEx(WH_CBT, &InputBoxProc, ModHwnd, ThreadID);
  hr = spVBScriptParse->ParseScriptText(WideEval.c_str(), NULL, NULL, NULL, 0, 0, SCRIPTTEXT_ISEXPRESSION, &result, &ei);
  UnhookWindowsHookEx(hHook);

  // Cleanup
  spVBScriptParse = NULL;
  spVBScript = NULL;
  pScriptSite->Release();
  pScriptSite = NULL;
  CoUninitialize();

  // Result
  static string strResult;
  _bstr_t bstrResult = (_bstr_t)result;
  strResult = StringShorten((wchar_t*)bstrResult);
  return (char*)strResult.c_str();
}

char *InputBox(char *Prompt, char *Title, char *Default) {
  HideInput = false;
  return InputBoxHelper(Prompt, Title, Default);
}

char *PasswordBox(char *Prompt, char *Title, char *Default) {
  HideInput = true;
  return InputBoxHelper(Prompt, Title, Default);
}

Create the two above files and then add them to your Visual Studio project.

In any file you want the input or password box functions, (found in the header), simply include the header:

#include "inputbox.h"

// use functions here

I also got rid of the default Windows application icon in the title bar of the VBScript InputBox, because a lot of people I've seen complain about how ugly it is to see that there.

Let me know if you have any questions.

Vasodilator answered 15/10, 2018 at 0:23 Comment(4)
I know this is old, but with this example for some reason hitting the 'f' key bricks the program. I dropped the code into a brand-new project, called the function, typed 'asdf' but the 'f' never showed up, and I had to kill the debugger and re-runEntablature
@Entablature that's rather interesting, when i have time later today i'll do my best to investigate this issueVasodilator
@Entablature I can't seem to reproduce this. Try this sample program and see if it still does it for you: github.com/time-killer-games/Visual-CPP-InputBox (The exe needs at least one argument passed to it).Vasodilator
It doesn't work with Chinese characters. My system locale is set to Chinese (Simplified, Singapore), and when I write std::string name = InputBox("What is your name?", "Enter Name", "here");, and enter 毛泽东, and then do std::cout << "Hello " << name << "!\n";, it prints out Hello 姣涙辰涓?Goddess
M
2

I have to admit that I haven't really done much in the way of input boxes in ages, but you basically have to go outside C++ in order to get any kind of graphical input box. There's simply no mechanism built into the language for that kind of stuff for portability reasons. I don't remember if it applied to C++ as well, but C doesn't even assume you have a console. Anyway, your best bet would be something along the lines you were already trying: Win32 API, Qt, etc. If you can use the console, however, feel free to just use the iostream library to get the job done.

Myles answered 17/11, 2010 at 4:15 Comment(0)
R
1

Using a console window is better suited to the mode of communication where a program prompts the user, continues, prompts the user again, and so on.

And for that you can use the standard library's facilities like cin and cout.

Regelate answered 17/11, 2010 at 4:37 Comment(1)
Normally I would, but I'm working off of a preexisting code base that doesn't easily allow for that.Tyus
S
1

Unlike Visual Basic and other languages, there is no "built in" Input Box like command in c++. Unlike MessageBox that can be just invoked, InputBox() needs to be written. In fact, I have done so. The following article describes how to implement such InputBox as part of a small Static Library that can be used, with no Resources, from any Win32 c++ program. Source code at Github. It can be used as follow:

LPWSTR GetString(LPCTSTR szCaption, LPCTSTR szPrompt, LPCTSTR szDefaultText = L"");

For example:

LPWSTR result = SG_InputBox::GetString(
     L"Code Project Demo", 
     L"What is your name");
Subcommittee answered 1/8, 2019 at 19:25 Comment(1)
That‘s great. Easy to use.Bolshevik
B
0

try this: InputBox in c++ vs2010

#include "stdafx.h"
#using <system.windows.forms.dll>
#using <Microsoft.VisualBasic.dll>

using namespace System;

int main(array<System::String ^> ^args)
{
    Microsoft::VisualBasic::Interaction::InputBox(L"Hello", L"Title", L"DefResp", 500, 500);
    return 0;
}
Bircher answered 2/10, 2016 at 21:25 Comment(1)
This requires the Microsoft .NET runtime installed. You probably should have mentioned that here.Vasodilator

© 2022 - 2024 — McMap. All rights reserved.