So i am making a basic tic tac toe game as my first program in win32 (just for fun to learn more, no school assignment or anything). I have most of the UI done and the basic gameplay such as clicking squares and placing x's or o's appropriately. I have written it so that it recognizes who is the winner when the game is over and can display a little text window saying "PLAYER 1 WINS!" etc....
No my question is concerning how to display the score. My idea is to have a int variable called scoreplayer1, and when the player wins, i will increase it by 1 (scoreplayer1++). I then want to have the window that has the previous score change to the new score. This is what i have so far (I am going to take out all of the code that is not relevant to this question but if you need more let me know):
My Global Variables:
//Global Variables
HINSTANCE hInst; // current instance
TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
HWND hwnd1, hwnd2, hwnd3, hwnd4, hwnd5, hwnd6, hwnd7, hwnd8;
HWND hwnd9, hwndscore1, hwndscore2, hWnd;
// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int determinewinner();
int showscore(int win_value);
int scoreplayer1,scoreplayer2;
The CreateWindow function that originally creates the score windows (they start out blank):
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
case WM_CREATE:
hwndscore1 = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("STATIC"),TEXT(""), WS_CHILD|WS_VISIBLE|SS_CENTER, 20,285,100,20,hWnd,HMENU(NULL),GetModuleHandle(NULL),NULL);
hwndscore2 = CreateWindowEx(WS_EX_CLIENTEDGE,TEXT("STATIC"),TEXT(""), WS_CHILD|WS_VISIBLE|SS_CENTER, 130,285,100,20,hWnd,HMENU(NULL),GetModuleHandle(NULL),NULL);
Then the function that is trying to handle changing the score window:
int showscore(int win_value)
{
if(win_value==1)
{ scoreplayer1++;
DestroyWindow(hwndscore1);
hwndscore1 = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("STATIC"),TEXT(scoreplayer1), WS_CHILD|WS_VISIBLE|SS_CENTER, 20,285,100,20,hWnd,HMENU(NULL),GetModuleHandle(NULL),NULL);
return (scoreplayer1);} // This return part is just temporary because it wants
// the function to return a value, it doesn't come into play
}
The idea was to destroy the old window and simply recreate a new window with the new score (that seemed to be the easiest way to go about doing it). I know where the problem is, it says i can't put the int scoreplayer1 variable in that TEXT("scoreplayer1") part in the last CreateWindowEx function. The error is: argument of typ int is incompatible with parameter of type LPCSTR.
So how can change the creation of that last window so that it will display a int variable (such as scoreplayer1) that will be increasing as the game goes on? Thanks!
*EDIT***
In response to a comment i attempted to use itoa() to fix the problem, i did the following:
int showscore(int win_value)
{ if(win_value==1)
{ scoreplayer1++;
char score1[1];
itoa(scoreplayer1, score1, 1);
DestroyWindow(hwndscore1);
hwndscore1 = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("STATIC"),TEXT(score1), WS_CHILD|WS_VISIBLE|SS_CENTER, 20,285,100,20,hWnd,HMENU(NULL),GetModuleHandle(NULL),NULL);
return (scoreplayer1);}
Which forces the program to break as soon as i get to that point in the game... Any ideas on what i did wrong?
Converting an integer to a string can be made MANY different ways in C and C++.
Your edited code fails because the string is 1 character long, and ALL numbers are guaranteed to NOT fit in one character, since all numbers take one character for the string itself, and a further character for the zero byte marking the end of the string. If the number is greater than 9, it will take up three characters, greater than 99 will take 4, and so on.
In C++, I would suggest that, although a bit longer, usign a stringstream to output the number to is an easier/safer method that avoids the problems if figuring out how much space you need for the string (and allows output of more complex things than just one integer, since you can simply combine any output, just like you would for console output using cout).
Something like this:
std::stringstream ss;
ss << scoreplayer1;
hwndscore1 = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("STATIC"),ss.str().c_str(), WS_CHILD|WS_VISIBLE|SS_CENTER, 20,285,100,20,hWnd,HMENU(NULL),GetModuleHandle(NULL),NULL);
Related
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.
I'm writing some example programs for a programming class using WinAPI. I'm familiar with GUI programming, but not in C++. As part of a test I ran into an anomaly. I'm hoping someone will know what I'm doing wrong and be able to explain the fix. Below is the callback function. Most of the code was copied directly from an example on basic paint.
LRESULT CALLBACK WinProc(HWND hwnd, UINT msg, WPARAM w, LPARAM l)
{
PAINTSTRUCT ps;
HDC hdc;
std::string txt = "Hello\0";
switch(msg)
{
hdc = BeginPaint(hwnd, &ps);
TextOut(hdc, 0, 60, txt.c_str(), 45);
EndPain, h, &ps);
}
}
So, the function is supposed to simply print "Hello" on the GUI, but instead I was seeting "Hello xc". I expanded the length of the string to 45 and saw a lot more. It prints random bits of text. I've seen class names and string values which were going to cout.
I have no idea why this is happening, but it appears that txt is being read far past the end of the buffer. I added a \0. No difference. The value is different every time.
Any idea what is going on?
TextOut uses the final parameter cchString to determine how many characters to print. You must tell it the correct number.
https://msdn.microsoft.com/en-us/library/windows/desktop/dd145133.aspx
lpString [in]
A pointer to the string to be drawn. The string does not need to be zero-terminated, because cchString specifies the length of the string.
cchString [in]
The length of the string pointed to by lpString, in characters.
TextOut() does not stop writing text at the null terminator, it will just keep going for as long as you specified. The last parameter tells the function how many characters to write. Instead of giving it 45, give it something like strlen(txt.c_str()).
I recently started coding in C++ and I am very new to it. (I code in Javascript, PHP, Java and Obj-C more often)
I'm practicing how to hook a message box and change its position. This is what I have in my .cpp file (after reading this SO post).
#include <iostream>
#pragma comment(lib,"User32.lib")
#include <windows.h>
HHOOK hhookCBTProc = 0;
LRESULT CALLBACK pfnCBTMsgBoxHook(int nCode, WPARAM wParam, LPARAM lParam){
if (nCode == HCBT_CREATEWND)
{
CREATESTRUCT *pcs = ((CBT_CREATEWND *)lParam)->lpcs;
if ((pcs->style & WS_DLGFRAME) || (pcs->style & WS_POPUP))
{
HWND hwnd = (HWND)wParam;
SetWindowPos(hwnd, HWND_TOP,130,122, 0, 0,SWP_NOSIZE);
}
}
return (CallNextHookEx(hhookCBTProc, nCode, wParam, lParam));
}
int main(void)
{
hhookCBTProc = SetWindowsHookEx(WH_CBT,pfnCBTMsgBoxHook,
0, GetCurrentThreadId());
int sResult = MessageBox ( NULL, "Hooked!", "oh my", MB_OK );
UnhookWindowsHookEx(hhookCBTProc);
return 0;
}
For some reason the position of the message box isn't changing. Where did it go wrong?
(I know I can create a customized window or dialog. But I am doing it this way because I want to learn how to hook a message box and where I did wrong.)
Firstly you should check in the debugger that your hook is actually being called, if you haven't already.
Secondly, at the time the HCBT_CREATEWND hook event is triggered, the window has only just been created - the system has yet to size and position it. It will do this with the values in the CREATESTRUCT after the hook returns - overriding your SetWindowPos call.
See the docs from MSDN on the lParam value for this particular hook event:
Specifies a long pointer to a CBT_CREATEWND structure containing
initialization parameters for the window. The parameters include the
coordinates and dimensions of the window. By changing these
parameters, a CBTProc hook procedure can set the initial size and
position of the window.
Therefore, the correct way to use this hook to change a window's position is to modify the values in the CREATESTRUCT directly.
Also note that it's quite possible that the dialog manager sizes and positions the window after creation, so if you find that this still isn't working for you, you may need to try watching for the HCBT_MOVESIZE event instead.
From the docs
At the time of the HCBT_CREATEWND notification, the window has been
created, but its final size and position may not have been determined
and its parent window may not have been established.
Maybe try hooking into CBT_ACTIVATE instead.
I am writing my first simple program in C++/WINAPI, with a lot of check boxes and a few edit fields, which will set up some calculations on a button press. All of my check boxes work/store info through individual cases, ie
switch (msg)
{
...
case WM_COMMAND:
{
switch (wParam)
{
case IDBC_BoxCheck1:
{...}
case IDBC_BoxCheck2:
{...}
...
} ...
...but I assumed edit fields didn't work as a case statement like a button press, since the value has to be read at the end once it has been changed as many times as the user wants. I looked online and attempted to use the SendMessage(hwnd, ...) and GetWindowText(hwnd, ...) functions to send a WM_GETTEXT command to the edit field and store it to a lpstr string, but I ran into the same problem with both of them - the hwnd for the edit fields aren't declared in the scope where the WM_GETTEXT command is being sent from, and I'm not sure how to get it there. Here is an overview of the structure being used in my program, which comes from a mix of some tutorials I was working with:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
{
return OnCreate(hwnd, reinterpret_cast<CREATESTRUCT*>(lParam));
// OnCreate is a sub function that handles the creation of all the buttons/controls,
// since there are so many of them, with the format:
// HWND editControl1 = CreateControl(...); // CreateControl being another sub fnct
// that creates edit field ctrls
// editControl1 is the hwnd I'm trying
// to read the value from
// HWND checkControl1 = CreateButton(...); // Creates button ctrls, including ck box
...
}
...
case WM_COMMAND:
{
switch (wParam)
{
case IDBC_BoxCheck1: // These control IDs are defined at the top of the file
{
LPSTR Check1;
StoreInfo(Check1); // Just a sub fnct to store info for later calculations
}
case IDBC_BoxCheck2:
{
LPSTR Check2;
StoreInfo(Check2);
} // etc... there are 20 or so check boxes/buttons
case IDBC_Calculate:
{
LPSTR edit1;
GetWindowText(editControl1, edit1, 100); // or SendMessage(editControl1, ...)
// This kicks out the error of editControl1 not being declared in this scope
StoreInfo(edit1);
// Calculation function goes here
} ...
} ....
}
default: DefWindowProc(hwnd, msg, wParam, lParam);
}
}
IDBC_Calculate is the final button pressed before the calculations run. I figured the best place to read and store the values from the edit fields would be after this button is pressed, right before the calculation function is called, but tied to the same command. This is where the hwnd editControl1 is undefined, but I don't know how to send the definition to this scope, or where else I should be reading and storing the edit field values.
Any help or pointers on getting the values from these edit fields to my other functions would be appreciated! I've seen many different ways to check button states in various tutorials/lessons, so I'd love to know if there's a better way to do what I've written above in general.
Your edit fields have IDs right? Then you can use GetDlgItem.
editControl1 = GetDlgItem(hwnd, CONTROL_ID_1);
GetDlgItem is badly named, it doesn't just work in dialog boxes. It gets the handle of any child window from a parent window, using the ID of the child window.
And what Anders K says is correct. The way you are using GetWindowText will crash your program.
I am writing a MFC program in which I have a a lot of Editboxes and I want to get all their text values and put them in a container. How can I achieve this without writing a line for each ID. I'm using this code for each ID:
CEdit *edit;
edit = reinterpret_cast<CEdit *>(GetDlgItem(IDC_NAME1));
But if I use that method I would have to write it 45 times. That doesn't seem right.
Is there a way of getting all the Editboxes in a container so I can use them that way or something like that?
You can certainly create an array (or other container) or pointers to CEdit: CEdit edits[45]; If the values of IDC_NAME1 through IDC_NAME45 are contiguous, you can just do something like:
for (int i=0; i<45; i++)
names[i] = reinterpret_cast<CEdit *>(GetDlgItem(IDC_NAME1 + i));
If those identifiers may not be contiguous, then you can put them in an array, and just index into that array as needed.
One caution: unless they're something like a grid of otherwise nearly identical edit controls, 45 on a screen may well be a bit much. If they are like a grid, you might want to look at one of the many available grid controls instead.
You do not have to use controls IDs.
Use EnumChildWindows and get test only for edit controls. Snippet follows.
Add following in dialog’s header:
afx_msg LRESULT OnFoundEdit(WPARAM wParam, LPARAM lParam);
And this to cpp:
#define WM_U_FOUND_EDIT WM_APP + 0x100
BEGIN_MESSAGE_MAP(CEditCtrlFishingDlg, CDialog)
ON_MESSAGE(WM_U_FOUND_EDIT, OnFoundEdit)
.
.
.
.
END_MESSAGE_MAP()
Write this line in the place you want to start edit text collection:
EnumChildWindows(m_hWnd, EnumChildProc, (LPARAM)m_hWnd);
Enum child procedure:
BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam)
{
CString csBuffer;
LPTSTR pBuf = csBuffer.GetBufferSetLength(MAX_PATH);
GetClassName(hwnd, pBuf, MAX_PATH);
csBuffer.ReleaseBuffer();
if(!csBuffer.CompareNoCase(_T("edit")))
{
SendMessage((HWND)lParam, WM_U_FOUND_EDIT, 0, (LPARAM)hwnd);
}
return TRUE;
}
and the handler:
LRESULT YourDlg::OnFoundEdit(WPARAM wParam, LPARAM lParam)
{
CWnd *pWnd = FromHandle((HWND)lParam);
CString csTxt;
pWnd->GetWindowText(csTxt);
// do what you need with text here
return 0;
}