Getting a list of all open windows in c++ and storing them - c++

I'm currently trying to get a list of all opened windows and storing them inside a vector. I've been looking at the code so much that the solution could be very easy but I don't seem to get it done without a global variable (which I want to avoid).
Here is the code:
#include "stdafx.h"
#include "json.h"
#include <algorithm>
using namespace std;
vector<string> vec;
BOOL CALLBACK speichereFenster(HWND hwnd, LPARAM substring){
const DWORD TITLE_SIZE = 1024;
TCHAR windowTitle[TITLE_SIZE];
GetWindowText(hwnd, windowTitle, TITLE_SIZE);
int length = ::GetWindowTextLength(hwnd);
wstring temp(&windowTitle[0]);
string title(temp.begin(), temp.end());
if (!IsWindowVisible(hwnd) || length == 0 || title == "Program Manager") {
return TRUE;
}
vec.push_back(title);
return TRUE;
}
int main() {
EnumWindows(speichereFenster, NULL);
cin.get();
return 0;
}
I want to store all titles in the vector but I don't know how as I can't pass the vector into the function...
Thanks!!!

The second parameter (lParam) to EnumWindows is documented as:
An application-defined value to be passed to the callback function.
Just pass your container to the API call:
int main() {
std::vector<std::wstring> titles;
EnumWindows(speichereFenster, reinterpret_cast<LPARAM>(&titles));
// At this point, titles if fully populated and could be displayed, e.g.:
for ( const auto& title : titles )
std::wcout << L"Title: " << title << std::endl;
cin.get();
return 0;
}
And use it in your callback:
BOOL CALLBACK speichereFenster(HWND hwnd, LPARAM lParam){
const DWORD TITLE_SIZE = 1024;
WCHAR windowTitle[TITLE_SIZE];
GetWindowTextW(hwnd, windowTitle, TITLE_SIZE);
int length = ::GetWindowTextLength(hwnd);
wstring title(&windowTitle[0]);
if (!IsWindowVisible(hwnd) || length == 0 || title == L"Program Manager") {
return TRUE;
}
// Retrieve the pointer passed into this callback, and re-'type' it.
// The only way for a C API to pass arbitrary data is by means of a void*.
std::vector<std::wstring>& titles =
*reinterpret_cast<std::vector<std::wstring>*>(lParam);
titles.push_back(title);
return TRUE;
}
Notes:
The code presented uses a std::wstring in place of std::string. This is necessary so that the entire character set can be represented.
As written, the code isn't correct. There are (invisible) code paths, that have no well-defined meaning. The Windows API is strictly exposed as a C interface. As such, it doesn't understand C++ exceptions. Particularly with callbacks it is vital to never let C++ exceptions travel across unknown stack frames. To fix the code apply the following changes:
[C++11 only] Mark the callback noexcept.
Wrap the entire callback inside a try-catch block, and handle any exceptions appropriately.
[C++11 only] With C++11 you can pass C++ exceptions across unknown stack frames, by passing a std::exception_ptr, and calling std::rethrow_exception at the call site.

Simple code to get all visible windows with non empty title
for (HWND hwnd = GetTopWindow(NULL); hwnd != NULL; hwnd = GetNextWindow(hwnd, GW_HWNDNEXT))
{
if (!IsWindowVisible(hwnd))
continue;
int length = GetWindowTextLength(hwnd);
if (length == 0)
continue;
char* title = new char[length+1];
GetWindowText(hwnd, title, length+1);
if (title == "Program Manager")
continue;
std::cout << "HWND: " << hwnd << " Title: " << title << std::endl;
}

Related

C++ Can someone show me how to create a select folder dialog with the Windows API GUi [duplicate]

How to select an existing folder (or create new) from a native Win32 application?
Here is a similar question. It has a good answer for C#/.NET. But I want the same thing for native Win32.
Anybody knows a solution, free code, etc?
Update:
I tried the function from the answer. Everything worked as expected, except it is necessary to call the SHGetPathFromIDList function to retrieve the name of selected directory. Here is a sample screen shot:
SHBrowseForFolder
Do your users a favor, and set at least the BIF_NEWDIALOGSTYLE flag.
To set the initial folder, add the following code:
static int CALLBACK BrowseFolderCallback(
HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
if (uMsg == BFFM_INITIALIZED) {
LPCTSTR path = reinterpret_cast<LPCTSTR>(lpData);
::SendMessage(hwnd, BFFM_SETSELECTION, true, (LPARAM) path);
}
return 0;
}
// ...
BROWSEINFO binf = { 0 };
...
binf.lParam = reinterpret_cast<LPARAM>(initial_path_as_lpctstr);
binf.lpfn = BrowseFolderCallback;
...
and provide a suitable path (such as remembering the last selection, your applications data folder, or similar)
Just as a go to for future users, this article helped me a lot with getting a directory dialog in C++
http://www.codeproject.com/Articles/2604/Browse-Folder-dialog-search-folder-and-all-sub-fol
Here is my code (heavily based/taken on the article)
NOTE: You should be able to copy/paste this into a file / compile it (g++, see VS in ninja edit below) and it'll work.
#include <windows.h>
#include <string>
#include <shlobj.h>
#include <iostream>
#include <sstream>
static int CALLBACK BrowseCallbackProc(HWND hwnd,UINT uMsg, LPARAM lParam, LPARAM lpData)
{
if(uMsg == BFFM_INITIALIZED)
{
std::string tmp = (const char *) lpData;
std::cout << "path: " << tmp << std::endl;
SendMessage(hwnd, BFFM_SETSELECTION, TRUE, lpData);
}
return 0;
}
std::string BrowseFolder(std::string saved_path)
{
TCHAR path[MAX_PATH];
const char * path_param = saved_path.c_str();
BROWSEINFO bi = { 0 };
bi.lpszTitle = ("Browse for folder...");
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;
bi.lpfn = BrowseCallbackProc;
bi.lParam = (LPARAM) path_param;
LPITEMIDLIST pidl = SHBrowseForFolder ( &bi );
if ( pidl != 0 )
{
//get the name of the folder and put it in path
SHGetPathFromIDList ( pidl, path );
//free memory used
IMalloc * imalloc = 0;
if ( SUCCEEDED( SHGetMalloc ( &imalloc )) )
{
imalloc->Free ( pidl );
imalloc->Release ( );
}
return path;
}
return "";
}
int main(int argc, const char *argv[])
{
std::string path = BrowseFolder(argv[1]);
std::cout << path << std::endl;
return 0;
}
EDIT: I've updated the code to show people how to remember the last selected path and use that.
Also, for VS, using Unicode character set. replace this line:
const char * path_param = saved_path.c_str();
With this:
std::wstring wsaved_path(saved_path.begin(),saved_path.end());
const wchar_t * path_param = wsaved_path.c_str();
My Test code above is compiled with g++, but doing this fixed it in VS for me.
For Windows Vista and above, it's best to use IFileOpenDialog with the FOS_PICKFOLDERS option for a proper open dialog rather than this tree dialog. See Common Item Dialog on MSDN for more details.

How can I show a window with the a name given as parameter in Windows in C++

I would like to make my software usable for Linux and Windows (Linux already works). Now I still need some functions so I can run the software on Windows, too.
I am currently trying to use the EnumWindows() function to get the window names and then show the window in the foreground (which matches the parameter).
static BOOL CALLBACK setWindowFocus(HWND hWnd, LPARAM lparam) {
int length = GetWindowTextLength(hWnd);
char* buffer = new char[length + 1];
GetWindowText(hWnd, buffer, length + 1);
std::string windowTitle(buffer);
// List visible windows with a non-empty title
if (IsWindowVisible(hWnd) && length != 0) {
// Check if it is the right Windowshandle
if ( windowTitle.compare(programname) == 0 ) <-- programname is a static variable
{
// Set application to the foreground
SetForegroundWindow(hWnd);
}
}
return TRUE;
}
Additionally, I used this to create the variable:
std::string programname;
And this to call it:
static void setWindowFocus(std::string programname)
{
std::cout << "Setting focus to window." << std::endl;
tempsavedProgramname=programname;
EnumWindows(setWindowFocus, NULL);
}
That is working, as long as it is in main(). But, I would like to have it in an extra class with some other functions (I would like to remove the static variable too, if possible).
Is there a way I can use the EnumWindows() function with an anonymous function, or something?
Can I use something like this to pass a string to the function:
EnumWindows(setWindowFocus, reinterpret_cast<LPARAM>(stringvariable));
Or, are there other ways which I can try to reach my goal?
Includes which I used for the code:
Windows.h
winuser.h
string
iostream
I hope that I did not forgot one.
Yes, you can use the LPARAM to pass a string variable into your callback, eg:
static BOOL CALLBACK setWindowFocus(HWND hWnd, LPARAM lparam) {
std::string &programname = *reinterpret_cast<std::string*>(lparam);
int length = GetWindowTextLength(hWnd);
char* buffer = new char[length + 1];
GetWindowText(hWnd, buffer, length + 1);
std::string windowTitle(buffer);
delete[] buffer; // <-- ADD THIS!
/* I would use this instead:
int length = GetWindowTextLength(hWnd);
std::string windowTitle(length+1, '\0');
windowTitle.resize(GetWindowText(hWnd, &windowTitle[0], length + 1));
*/
// List visible windows with a non-empty title
if (IsWindowVisible(hWnd) && (length != 0)) {
// Check if it is the right Windowshandle
if (windowTitle == programname)
{
// Set application to the foreground
SetForegroundWindow(hWnd);
return FALSE;
}
}
return TRUE;
}
static void setWindowFocus(std::string programname)
{
std::cout << "Setting focus to window." << std::endl;
EnumWindows(setWindowFocus, reinterpret_cast<LPARAM>(&programname));
}
And yes, you can use a C++11 lambda for the callback, rather than a static class method, but only if you use a non-capturing lambda, which is implicitly convertible to a function pointer (capturing lambdas are not). Fortunately, the LPARAM makes that a possibility, eg:
static void setWindowFocus(std::string programname)
{
std::cout << "Setting focus to window." << std::endl;
EnumWindows(
[](HWND hWnd, LPARAM lparam) -> BOOL {
std::string &programname = *reinterpret_cast<std::string*>(lparam);
// ...
},
reinterpret_cast<LPARAM>(&programname)
);
}
Now, that being said, there is a much simpler solution - since you already know the exact window text you are looking for, you can use FindWindow() instead of EnumWindows(), eg:
static void setWindowFocus(std::string programname)
{
std::cout << "Setting focus to window." << std::endl;
HWND hWnd = FindWindowA(NULL, programname.c_str());
if (hWnd != NULL) {
// Set application to the foreground
SetForegroundWindow(hWnd);
}
}
Here's the callback wrapped up in a class
class enum_windows {
protected:
virtual BOOL call_back(HWND hwnd) {
// Your code here
return TRUE;
}
public:
void start() {
EnumWindows([](HWND hwnd, LPARAM lParam) -> BOOL {
enum_windows * obj = reinterpret_cast<enum_windows *>(lParam);
return obj->call_back(hwnd);
}, reinterpret_cast<LPARAM>(this));
}
};
(You've already accepted an answer - I'm a slow typer 😄). I'll leave this here anyway.

C++ Get Another Windows Hwnd In Same Process

I've made a dll that you can inject into applications. I want that dll to find another window in the same process.
Here you can see a example. The console host (KonsolfönstervÀrd) is the dll I injected and I want that to find the lockdown-example window hwnd.
It is very simple to get the handle with the window title. But I don't want to hardcode it for just that application.
HWND parentHandle = FindWindowA(NULL, "Lockdown Example");
I have also tried some stuff with EnumWindows which basically prints all visible windows and their window title. And in the output of that you can see the "Lockdown Example" window.
10828: TheDll
388: Lockdown Example
13380: Microsoft Store
6664: Program Manager
How would I make it only do something when it found the "Lockdown Example" without just giving it a hardcoded title. Instead giving it something like GetCurrentProcessId()
static BOOL CALLBACK enumWindowCallback(HWND hWnd, LPARAM lparam) {
//GetWindowThreadProcessId();
int length = GetWindowTextLength(hWnd);
char* buffer = new char[length + 1];
GetWindowText(hWnd, buffer, length + 1);
std::string windowTitle(buffer);
// List visible windows with a non-empty title
if (IsWindowVisible(hWnd) && length != 0) {
cout << GetWindowThreadProcessId(hWnd, NULL) << ": " << windowTitle << endl;
}
return TRUE;
}
EnumWindows(enumWindowCallback, NULL)
So I came up to a answer with modifying the enumWindowCallback a little bit.
BOOL CALLBACK enumWindowCallback(HWND hwnd, LPARAM lParam)
{
int length = GetWindowTextLength(hwnd);
char* buffer = new char[length + 1];
GetWindowText(hwnd, buffer, length + 1);
std::string windowTitle(buffer);
DWORD lpdwProcessId;
GetWindowThreadProcessId(hwnd, &lpdwProcessId);
// 1. Checks if the current process is not equals to the value given in the lParam
// 2. Check if the current window is visible
// 3. Check if its not a empty name
// 4. Check so it is not the console window
// All these filters result in to only show all visible windows in the process
// Which in my case is "17260 | 15800: Lockdown Example"
if (lpdwProcessId == lParam && IsWindowVisible(hwnd) && length != 0
&& GetWindowThreadProcessId(GetConsoleWindow(), NULL) != GetWindowThreadProcessId(hwnd, NULL))
{
// Here you can do anything with the window handle (hwnd)
cout << lpdwProcessId << " | "<< GetWindowThreadProcessId(hwnd, NULL) << ": " << windowTitle << endl;
}
return TRUE;
}
Called it with:
EnumWindows(enumWindowCallback, GetCurrentProcessId()); // iterate over all windows

WinAPI GetWindowText as string

I was wondering if it were possible to take text input from a text box (CreateWindowEx "EDIT") and store it as a string or even better a vector
I need to have a textbox that the user can enter or paste text and when I click a button it will alphabetize the words and count unique words etc...
so far I have it reading it in as characters (i dont know how to make it a string) and alphabetizes the characters
so if I type in: how now brown cow
it will output: bchnnoooorwwww
instead of: brown cow how now
my code under the WM_COMMAND case is
int length;
length = GetWindowTextLength(textbox) + 1;
vector<wchar_t> list(length);
GetWindowText(textbox, &list[0], length);
wstring stxt = &list[0];
wstring str(stxt);
sort(str.begin(), str.end());
SetWindowText(sortedOutput, &str[0]);
This answer may be of use to you in devising a solution. I don't really know of one that is not hacky, but it can be done casting of the constness of c_string() from std::string.
https://stackoverflow.com/a/1986974/128581
A std::string's allocation is not guaranteed to be contiguous under the C++98/03 standard, but C++11 forces it to be. In practice, neither I nor Herb Sutter know of an implementation that does not use contiguous storage.
Notice that the &s[0] thing is always guaranteed to work by the C++11 standard, even in the 0-length string case. It would not be guaranteed if you did str.begin() or &*str.begin(), but for &s[0] the standard defines operator[] as:
Returns: *(begin() + pos) if pos < size(), otherwise a reference to an object of type T with value charT(); the referenced value shall not be modified
Continuing on, data() is defined as:
Returns: A pointer p such that p + i == &operator for each i in [0,size()].
(notice the square brackets at both ends of the range)
Thus it follows you can do something like this:
int len = GetWindowTextLength(hwnd) + 1;
std::string s;
s.reserve(len);
GetWindowText(hwnd, const_cast<char*>(s.c_str()), len - 1);
Which is pretty ugly. Welcome any more "correct" answers, though.
Regarding when unicode is enabled on your build, you have to use a wstring or equivalent. Testing that out just a moment ago, this works:
std::wstring title;
title.reserve(GetWindowTextLength(m_window_handle) + 1);
GetWindowText(m_window_handle, const_cast<WCHAR *>(title.c_str()), title.capacity());
In general, regarding the windows api its useful to google their all caps typedefs and figure out what they really are.
Regarding splitting strings, std::string isn't particular good at this kind of manipulation. This is where std::stringstream (or wstringstream for unicode) comes in handy. I am fairly certain stringstream is not guaranteed to be contiguous in memory, so you can't really go around writing directly into its buffer.
// Initialize a stringstream so we can extract input using >> operator
std::wstringstream ss;
ss.str(title);
// Make a vector, so we can store our words as we're extracting them
// and so we can use sort later, which works on many stl containers
std::vector<std::wstring> words;
std::wstring word;
// This will evaluate to false and thus end the loop when its done
// reading the string word by word
while(ss >> word)
{
words.push_back(word);
}
Then proceed with your sorting, but on the new vector words.
Your problem is not a winapi problem. While not the only way, you found a solution to transfer a string back and forth to your edit box.
How to turn that string into a list/vector of strings, with words being the elements of that list/vector is in fact an STL problem.
Basically, you are looking for the C++ equivalent of the C# function String.Split().
And there is already a good question and answer for that, here:
Most elegant way to split a string?
So, all you have to do is a sort of round trip:
Get string from TextBox
Convert string to a std::vector<string>, using your split function (see the other question for how to do that).
Sort the vector in the usual way.
Convert the vector back to a string, which is the inverse of your split function (Hint: std::ostringstream).
Set the resulting string as the text of your TextBox.
Depending on your preferences regarding multi character strings and globalization, you might decide to stick to the ASCII versions at first. On windows, you can compile to MBCS or ASCII. The respective string types are then respectively (TCHAR and LPCTSTR or WCHAR and LPCWSTR or CHAR and LPCSTR). All win32 functions come in two flavors, distinguished by a trailing A or W at the end of the function name, respectively.
AFAIK, while there is std::string and std::wstring, there is no standard implementation for std::basic_string<TCHAR>, which would work along with your compile options.
As for the window handling, here some code example (snippets):
In InitInstance(), I created the dialog (IDD_FORMVIEW) with my input edit box, my button and my static output area as a child window of the main application window:
//
// FUNCTION: InitInstance(HINSTANCE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
HWND hWndChild = CreateDialogW(hInstance, MAKEINTRESOURCE(IDD_FORMVIEW), hWnd, dlgProc);
if (NULL == hWndChild)
{
DWORD lastError = GetLastError();
wchar_t msg[100];
swprintf_s(msg, _countof(msg), L"error code: 0x%0X\n", lastError);
OutputDebugString(msg);
}
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
ShowWindow(hWndChild, SW_SHOW);
UpdateWindow(hWnd);
return TRUE;
}
CreateDialogW() takes as the last parameter a pointer to the dialog handler function, which is called dlgProc() in this example.
This is how that dlgProc() function looks like:
// Message handler for our menu which is a child window of the main window, here.
static INT_PTR CALLBACK dlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
BOOL processed = false;
switch (uMsg)
{
case WM_INITDIALOG:
break;
case WM_NOTIFY:
break;
case WM_COMMAND:
{
auto sourceId = LOWORD(wParam);
auto code = HIWORD(wParam);
if (IDC_BUTTON1 == sourceId)
{
if (BN_CLICKED == code)
{
wchar_t text[1024];
GetDlgItemText(hwndDlg, IDC_EDIT1, text, _countof(text));
// TODO: do your string processing here (string -> string)
SetDlgItemText(hwndDlg, IDC_STATIC, text);
processed = TRUE;
}
}
}
break;
default:
break;
}
return processed;
}
I have just mixed a few lines of code to convert wchar_t to wstring to std::string. Here you go!
string GetWindowStringText(HWND hwnd)
{
int len = GetWindowTextLength(hwnd) + 1;
vector<wchar_t> buf(len);
GetWindowText(hwnd, &buf[0], len);
wstring wide = &buf[0];
string s(wide.begin(), wide.end());
return s;
}
This has a vector so you'll need to include it.

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

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.
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.
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/
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.
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.
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.
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.
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");
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;
}