Concatenation of pressed WM_Char key values win32API - c++

I am trying to capture the values of WM_CHAR keys, and then putting all the captured the values into a single string. I have tried to concatenate the pressed key value 2, 3, 4 and 5 with _tcscat, the resulting TCHAR string looks like this "22232323423423452345" I would like to know how to make TCHAR string looks like 2345. The following is the code that I have.
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static PMSG pmsg ;
int i, iType ;
int StrLen;
TCHAR StrBuf[9];
static TCHAR tBuf[32];
TCHAR MyTchar[8] = TEXT ("A");
WORD wCharCode;
switch (message)
{
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
GetClientRect(hwnd, &rect);
SelectObject (hdc, GetStockObject (SYSTEM_FONT)) ;
SetBkMode (hdc, TRANSPARENT) ;
for (i = min (cLines, cLinesMax), cScreenLine=1; i>0 ; i--, cScreenLine++)
{
iType = pmsg[i-1].message == WM_CHAR ;
if (!iType)
{
StrLen= wsprintf(StrBuf, TEXT("%s"), TEXT(" "));
}
else
{
wCharCode = (WORD)(pmsg[i-1].wParam & 0xffff);
memcpy(&MyTchar, &wCharCode, 2);
StrLen = wsprintf(StrBuf[2], TEXT("%s"), &MyTchar);
_tcscat(tBuf, MyTchar);
}
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_DESTROY:
PostQuitMessage (0) ;
return 0 ;
}
}

I don't understand the message processing you have during the WM_PAINT message. You probably want to handle WM_CHAR as a separate message altogether where you can keep track of a string.
Outside of your WndProc, you will need #include <string>; and std::wstring keyPresses;
WM_CHAR can then be handled like any other event inside the WndProc.
case WM_CHAR:
switch (wParam)
{
// First, handle non-displayable characters by beeping.
case 0x08: // backspace.
case 0x09: // tab.
case 0x0A: // linefeed.
case 0x0D: // carriage return.
case 0x1B: // escape.
case 0x20: // space.
MessageBeep((UINT) -1);
break;
// Next, handle displayable characters by appending them to our string.
default:
keyPresses += (wchar_t) wParam;
}
break;
Then, you can do whatever manipulations you would like on this string, including displaying it during the WM_PAINT message.

Since you are using C++, use std::string or std::wstring. It will be much simpler and safer (no buffer overflows)

Before you use string buffer, you should clear them first.
You can use
1. ZeroMemery
2. memset
And or
TCHAR StrBuf[9];
====>
TCHAR StrBuf[9] = {0};
finally, why u use tBuf as a static var?

Related

Windows API: Guess the next caret position

I'm currently writing a function to get the current line/column of an EDIT control, and I'm stuck on a problem:
If I use WM_KEYUP to handle the caret position, the coordinates are valid but it can't be updated every "frame" since it waits for the user to release the pressed key
If I use WM_KEYDOWN, GetCaretPos returns the "previous" position of the caret (well, it's an obvious issue since it hasn't moved yet.)
Is there anything I can do to guess the next position of a caret? is it efficient if I just use EM_GETSEL?
LRESULT Edit::HandleMessage(UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_LBUTTONDOWN:
{
// Get character position from the mouse cursor's position (currently, there's no conversion of the coordinates if the mouse cursor is out of bound)
POINT pntMousePos{ 0 };
pntMousePos.x = GET_X_LPARAM(lParam);
pntMousePos.y = GET_Y_LPARAM(lParam);
LRESULT notifyValue = this->Notify(EM_CHARFROMPOS, 0, MAKELPARAM(pntMousePos.x, pntMousePos.y));
int lineIdx = static_cast<int>(this->Notify<int, LPARAM>(EM_LINEINDEX, -1));
if (lineIdx == NULL) {
DWORD beg, end;
lineIdx = Notify(EM_GETSEL, &beg, &end);
}
m_caretPos.line = HIWORD(notifyValue) + 1;
m_caretPos.column = (LOWORD(notifyValue) - lineIdx) + 1;
// Send a custom message to the main window
SendMessage(GetParent(m_parent) /*The EDIT control is actually a child of a tab control that is itself a child of the main window, ignore this */, CEM_GETLINEINFO, MAKEWPARAM(m_caretPos.line, m_caretPos.column), 0);
}
break;
case WM_KEYDOWN:
{
// Get Character position from the carret's position
// Get text metric (doesn't work so I removed some lines)
TEXTMETRIC tm{0};
HDC hdc = GetDC(m_self);
SelectObject(hdc, this->m_fnt);
GetTextMetricsW(hdc, &tm);
ReleaseDC(m_self, hdc);
POINT caretPos{ 0 };
GetCaretPos(&caretPos);
LRESULT notifyValue = this->Notify(EM_CHARFROMPOS, 0, MAKELPARAM(caretPos.x, caretPos.y));
int lineIdx = static_cast<int>(this->Notify<int, LPARAM>(EM_LINEINDEX, -1));
m_caretPos.line = HIWORD(notifyValue) + 1;
m_caretPos.column = (LOWORD(notifyValue) - lineIdx) + 1;
SendMessage(GetParent(m_parent), CEM_GETLINEINFO, MAKEWPARAM(m_caretPos.line, m_caretPos.column), 0);
}
break;
}
return DefSubclassProc(m_self, msg, wParam, lParam);
}

GetWindowText() doesn't work

Initially I have to say that I know nothing about WinAPI. I'm learning from quite old tutorial, which seems to be a little bit outdated. I'm trying to make a dialog box where user would type in size of a next window. I've made it in Visual Studio using Resource Editor (or whatever it is called). I'm trying to retrieve data from Edit Controls, but GetWindowText doesn't work well.
So I made global LPTSTR named SizeX and SizeY (I know I could made them local and later pass them to a function that creates the second window, but I've got then problems with hInstance... nevermind).
BOOL CALLBACK SettingsProcedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_INITDIALOG:
{
SetWindowTextA(GetDlgItem(hwnd, IDC_EDIT1), "20"); //I'm setting default input in case the user doesn't want to write anything
SetWindowTextA(GetDlgItem(hwnd, IDC_EDIT2), "20");
}
break;
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDC_BUTTON1:
{
GetWindowText(GetDlgItem(hwnd, IDC_EDIT1), sizeX, GetWindowTextLength(GetDlgItem(hwnd, IDC_EDIT1)) + 1);
if (sizeX == NULL)
break; //breaks every time
GetWindowText(GetDlgItem(hwnd, IDC_EDIT2), sizeY, 10);
EndDialog(hwnd, IDC_BUTTON1);
}
break;
}
}
break;
default: return FALSE;
}
return TRUE;
}
I'm sure I have a lot of basic mistakes in this code, so please don't blame me :P
I have no idea how to make it work. The fantastic tutorial I use tells nothing about Edit Controls, it even has an information that it might be too old. Unfortunately that is the only WinAPI tutorial I've found in my language, if you know any good one in English I'd be glad.
the thing that you should do is use directly GetDlgItemInt to retrieve sizeX and sizeY otherwise you should get text as a string then convert it into int:
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDC_BUTTON1:
{
BOOL bCheck = FALSE;
sizeX = GetDlgItemInt(hwnd, IDC_EDIT1, &bCheck, false);
sizeY = GetDlgItemInt(hwnd, IDC_EDIT2, &bCheck, false);
// or text then convert:
int textLengthX = SendDlgItemMessage(hwnd, IDC_EDIT1, WM_GETTEXTLENGTH, 0, 0);
int textLengthY = SendDlgItemMessage(hwnd, IDC_EDIT2, WM_GETTEXTLENGTH, 0, 0);
LPSTR lpTextX = (LPSTR)GlobalAlloc(GPTR, textLengthX + 1);
LPSTR lpTextY = (LPSTR)GlobalAlloc(GPTR, textLengthY + 1);
SendDlgItemMessage(hwnd, IDC_EDIT1, WM_GETTEXT, (WPARAM)textLengthX + 1, (LPARAM)lpTextX);
SendDlgItemMessage(hwnd, IDC_EDIT1, WM_GETTEXT, (WPARAM)textLengthY + 1, (LPARAM)lpTextY);
// now you have sizeX and sizeY as strings so convert them to int:
int sizeX = atoi(lpTextX);
int sizeY = atoi(lpTextY);
GlobalFree(lpTextX);
GlobalFree(lpTextY);
}
break;
}
break;
}

Properly handle WM_PASTE in subclass procedure

RELEVANT INFORMATION:
I have a subclass procedure that needs to validate the content of the clipboard before it gets pasted.
I have managed to get the content of the clipboard successfully, at least I think so.
QUESTION:
I do not know how to construct the following if statement ( the following is pseudo code ):
if( clipboard content is OK )
defaul handler;
else
discard message;
MY EFFORTS TO SOLVE THIS:
So far this is what I have in mind:
LRESULT CALLBACK Decimalni( HWND hwnd,
UINT message, WPARAM wParam, LPARAM lParam,
UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
switch (message)
{
case WM_PASTE:
{
bool IsTextValid = true; // indicates validity of text
if( OpenClipboard(hwnd) ) // open clipboard
{
HANDLE hClipboardData;
// get clipboard data
if( hClipboardData = GetClipboardData(CF_UNICODETEXT) )
{
// Call GlobalLock so that to retrieve a pointer
// to the data associated with the handle returned
// from GetClipboardData.
wchar_t *pchData = (wchar_t*)GlobalLock(hClipboardData);
// copy clipboard data so we can free clipboard
wchar_t result[10]; // I just need first 9 characters
memset( result, L'0', sizeof(result) );
// copy clipboard data WITH TRUNCATION!!!
wcsncpy_s( result, 10, pchData, _TRUNCATE );
// Unlock the global memory.
GlobalUnlock(hClipboardData);
/*** parse the text for validity ****/
// code for parsing text
// update IsTextValid to indicate success or fail
/*** end of parsing *******/
}
// Finally, when finished I simply close the Clipboard
// which has the effect of unlocking it so that other
// applications can examine or modify its contents.
CloseClipboard();
}
// here should be the problematic if statement
if( IsTextValid )
return ::DefSubclassProc( hwnd, message, wParam, lParam);
else
return FALSE;
}
break;
case WM_CHAR:
{
// filter out some invalid keys
}
break;
case WM_NCDESTROY:
::RemoveWindowSubclass( hwnd, Decimalni, 0 ); // remove subclassing
break;
}
return ::DefSubclassProc( hwnd, message, wParam, lParam);
}
Is my idea correct or is there another way to form my if statement?
Thank you.
Best regards.
The code seems plausible, down to the action taken. Bit clunky, but that's Windows API. There may be better ways, but this should work.
One mistake: if the text is OK, you should call DefSubclassProc, not the default window procedure.
If the text is not OK you could consider emptying the clipboard. There is not enough here about your other requirements to talk about that.

reading from ComboBox

how can i read the text of a selected value of a comboBox in windows aplication(borland C++) for example:
i have combobox which contains 2 values (sum and mult) i want to see if it is sum i have to add the numbers and if it mult i have to multiplicate the numbers so how can i read the value of combobox in this case.
For Windows:
In your window procedure use the WM_COMMAND message and then check for a CBN_SELCHANGE notification. Then use WM_GETTEXT along with WM_GETTEXTLENGTH to receive the selected text like Mark Ingram says. Or you can also use CB_GETCURSEL to receive the identifier of the selected item.
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_COMMAND:
switch(LOWORD(wParam)) {
case IDC_COMBO:
if (HIWORD(wParam) == CBN_SELCHANGE) {
HWND hCtl = GetDlgItem(hWnd, IDC_COMBO);//Get handle for HMENU item
if (SendMessage(hCtl, CB_GETCURSEL, 0, 0) == compareValue) {
//...
}
}
break;
}
break;
//...
}
}
Assuming that you are using Windows, you can use the following messages:
WM_GETTEXTLENGTH and WM_GETTEXT.
Firstly, get the length of the selected text, then allocate your buffer to ensure it's large enough, then retrieve the actual text. Easy.
Example:
const UINT length = ::SendMessage(hWnd, WM_GETTEXTLENGTH, 0, 0);
LPTSTR pszText = new TCHAR[length + 1];
::SendMessage(hWnd, WM_GETTEXT, length + 1, pszText);
// pszText will now contain the text you want, do what you want with it
delete[] pszText; // Remember to delete else you will leak.
I never work with c++ with winapplication but i tried it with the c# and hopefully that you want the desired output as i got through your question if it is not right then you should edit your question.
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboBox1.Text == "ADD")
{
int a = 12, b = 13, c;
c = a + b;
MessageBox.Show("Result of adding= " + c);
}
else if (comboBox1.Text == "Multiple")
{
int x = 3, y = 5, z;
z = x * y;
MessageBox.Show("Result of multiplication= " + z);
}
}

Keyboard Hook... not getting Lower or Upper case characters

The function below is logging the "0", "z" and the "1" ok... but its not capturing the "Z" (shift-z)... any help would be appreciated...
__declspec(dllexport)
LRESULT CALLBACK HookProc (UINT nCode, WPARAM wParam, LPARAM lParam)
{
if ((nCode == HC_ACTION) && (wParam == WM_KEYUP))
{
// This Struct gets infos on typed key
KBDLLHOOKSTRUCT hookstruct = *((KBDLLHOOKSTRUCT*)lParam);
// Bytes written counter for WriteFile()
DWORD Counter;
wchar_t Logger[1];
switch (hookstruct.vkCode)
{
case 060: Logger[0] = L'0'; break;
case 061: Logger[0] = L'1'; break;
case 90: Logger[0] = L'z'; break;
case 116: Logger[0] = L'Z'; break;
}
// Opening of a logfile. Creating it if it does not exists
HANDLE hFile = CreateFile(L"C:\\logfile.txt", GENERIC_WRITE,
FILE_SHARE_READ, NULL,OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
// put the file pointer to the end
SetFilePointer(hFile,NULL,NULL,FILE_END);
// Write the hFile typed in logfile
WriteFile(hFile,&Logger,sizeof(Logger),&Counter,NULL);
//WriteFile(hFile,&hookstruct.vkCode,sizeof(hookstruct.vkCode),&Counter,NULL);
// Close the file
CloseHandle(hFile);
}
}
The keyboard does not send characters. It sends keys. Whether you're typing z or Z, you're still pressing the same key, and that key has the same VK code both times.
You should also get notification when the Shift key is pressed or released. You can use those notifications to translate the keystrokes into characters. The caps-lock state will also be relevant for that. You may also be concerned about dead keys.
You can check whether the Shift key is pressed. GetAsyncKeyState will tell you the state of the key right now, and GetKeyState will tell you the state of the key as of the last message removed from the message queue.
There's no virtual key code for Z.
Try something like this:
case 90:
if(GetKeyState(VK_LSHIFT|VK_RSHIFT)
Logger[0] = L'Z'; break;
else
Logger[0] = L'z'; break;