Link to home
Create AccountLog in
Avatar of edvinson
edvinsonFlag for United States of America

asked on

Monitor EDIT control

I have an EDIT control, and during the entire execution of my program, I need to monitor this field to see if the number "7" was entered.

It doesn't matter what the final value of the EDIT control is, I just need to know if at ANY time, a 7 was entered.

Can someone show me a code snippet example?

Thank you
Avatar of jkr
jkr
Flag of Germany image

You could use a Windows hook (http://msdn.microsoft.com/en-us/library/windows/desktop/ms632589%28v=vs.85%29.aspx) for that which monitors keypress events and checks if they occurred in your edit control, e.g. like

#include <windows.h>

HHOOK g_hhk;
HWND g_hEdit;

LRESULT CALLBACK GetMsgProc(
    int code,
    WPARAM wParam,
    LPARAM lParam
)
{
    if (o > code || PM_NOREMOVE == wParam) return CallNextHookEx(g_hhk,code,wParam,lParam);

    MSG* pmsg = (MSG*) lParam;

    if (pmsg->message == WM_KEYDOWN) { // this one is for us

        if (pmsg->hwnd == g_hEdit && pmsg->wParam == VK_7) { // '7' has been pressed if the monitored edit control

            // ---> ACTION!
        }
    }

    return CallNextHookEx(g_hhk,code,wParam,lParam);
}

void InitMonitoring(HWND hEdit) {

    h_hEdit = hEdit;

    g_hhk = SetWindowsHookEx(WH_GETMESSAGE,GetMsgProc,NULL,GetCurrentThreadId());
}

void StopMonitoring() {

    UnhookWindowsHookEx(g_hhk);
}

Open in new window

Avatar of edvinson

ASKER

Wow thanks! I just don't know where to implement it, could you show me so I can test it?
Was your code intended to be used in a Dialog based app?


Here is my complete source code:

#include <windows.h>


#include "resource.h"


//---------------------------------------------------------------------------
HWND hWnd;
LRESULT CALLBACK DlgProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
//---------------------------------------------------------------------------

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
				   LPSTR lpCmdLine, int nCmdShow)
{
	DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1),
	          hWnd, reinterpret_cast<DLGPROC>(DlgProc));

	return FALSE;
}
//---------------------------------------------------------------------------
LRESULT CALLBACK DlgProc(HWND hWndDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
{
	switch(Msg)
	{
	case WM_INITDIALOG:
		return TRUE;

	case WM_COMMAND:
		switch(wParam)
		{
		case IDOK:
             
			EndDialog(hWndDlg, 0);
			
			
			
			
			return TRUE;
		}
		break;
	}

	return FALSE;
}
//---------------------------------------------------------------------------
                                  

Open in new window


Maybe we can just show a messagebox when the 7 is pressed so I can test.

Thanks
almost forgot, let's use IDC_EDIT1 as my textfield we are monitoring, ok?

Thank you. I am currently blindly trying to put your code into my project, but I am new to c++
You could do that in any kind of app, all you need the window handle of the control. E.g. like

InitMonitoring(GetDlgItem(IDC_EDIT1));

Open in new window

I feel stupid, you have clearly explained this, but I cannot get this to compile.

Could you show me exactly where to put your working code in the code sample I posted above, please?
What compier errors are you getting?
One thing, that should be a '0' instead of an 'o' in

    if (o > code || PM_NOREMOVE == wParam) return CallNextHookEx(g_hhk,code,wParam,lParam);

Open in new window


i.e.

    if (0 > code || PM_NOREMOVE == wParam) return CallNextHookEx(g_hhk,code,wParam,lParam);

Open in new window

OK, along with your code, that should be

#include <windows.h>


#include "resource.h"

HHOOK g_hhk;
HWND g_hEdit;

LRESULT CALLBACK GetMsgProc(
    int code,
    WPARAM wParam,
    LPARAM lParam
)
{
    if (0 > code || PM_NOREMOVE == wParam) return CallNextHookEx(g_hhk,code,wParam,lParam);

    MSG* pmsg = (MSG*) lParam;

    if (pmsg->message == WM_KEYDOWN) { // this one is for us

        if (pmsg->hwnd == g_hEdit && pmsg->wParam == VK_7) { // '7' has been pressed if the monitored edit control

            // ---> ACTION!
        }
    }

    return CallNextHookEx(g_hhk,code,wParam,lParam);
}

void InitMonitoring(HWND hEdit) {

    h_hEdit = hEdit;

    g_hhk = SetWindowsHookEx(WH_GETMESSAGE,GetMsgProc,NULL,GetCurrentThreadId());
}

void StopMonitoring() {

    UnhookWindowsHookEx(g_hhk);
}
                                            


//---------------------------------------------------------------------------
HWND hWnd;
LRESULT CALLBACK DlgProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
//---------------------------------------------------------------------------

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
				   LPSTR lpCmdLine, int nCmdShow)
{
	DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1),
	          hWnd, reinterpret_cast<DLGPROC>(DlgProc));

	return FALSE;
}
//---------------------------------------------------------------------------
LRESULT CALLBACK DlgProc(HWND hWndDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
{
	switch(Msg)
	{
	case WM_INITDIALOG:
                InitMonitoring(hwndDlg);
		return TRUE;

	case WM_COMMAND:
		switch(wParam)
		{
		case IDOK:
             
			EndDialog(hWndDlg, 0);
			
			
			
			
			return TRUE;
		}
		break;
	}

	return FALSE;
}
//---------------------------------------------------------------------------
                                  
                                            

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of jkr
jkr
Flag of Germany image

Link to home
membership
Create an account to see this answer
Signing up is free. No credit card required.
Create Account
That is great. Thank you. One final question, if I wanted to monitor multiple controls, should I simply use:

InitMonitoring(GetDlgItem(hwndDlg,IDC_EDIT1));
InitMonitoring(GetDlgItem(hwndDlg,IDC_EDIT2));
InitMonitoring(GetDlgItem(hwndDlg,IDC_EDIT3));

etc? Or would you suggest I rewrite your code to handle multiple controls?
Yes, multiple controls would require that to be rewritte, albeit not that differently - you'd basically make that

void InitMonitoring(HWND* phEdit, size_t count);

Open in new window


and store the handles in an array that you later evaluate inside 'GetMsgProc()' (also changing 'g_hEdit' to an array/pointer).
if you have a dialog form where the edit control is part of, it is easier to handle the EN_CHANGE notification for the edit control rather than hooking. hooking is the way if you don't own the control or don't have access to the dialog (sources).

WM_COMMAND 
wNotifyCode = HIWORD(wParam); // notification code 
wID = LOWORD(wParam);         // item, control, or accelerator identifier 
hwndCtl = (HWND) lParam;      // handle of control

Open in new window


EN_CHANGE 
idEditCtrl = (int) LOWORD(wParam); // identifier of edit control 
hwndEditCtrl = (HWND) lParam;      // handle of edit control

Open in new window



when using mfc you simply could add an event handler in the resource editor. select the edit control and choose 'add event handler' from right-click menu. or you override the PreTranslateMessage function of your mfc dialog class and check for WM_KEYDOWN message (similar as done in the hook).

when not using mfc go to the windows proc of your dialog and handle WM_COMMAND message.

case WM_COMMAND:
{
     WORD wNotifyCode = HIWORD(wParam); // notification code 
     WORD idEdit = LOWORD(wParam); 
     if (wNotifyCode == EN_CHANGE && idEdit == IDC_EDIT1)
     {
          char szEdit[256] = { '\0' };
          HWND hwndEdit = (HWND) lParam;
          GetWindowText( hwndEdit, szEdit, sizeof(szEdit));
          if (strchr(szEdit, '7')) 
          {
               // string contains '7'
               DWORD dwStart;
               DWORD dwEnd;
               wParam = (WPARAM) (LPDWORD) &dwStart; // receives starting position 
               lParam = (LPARAM) (LPDWORD) &dwEnd;   // receives ending position 
               SendMessage(hwndEdit, EM_GETSEL, wParam, lParam);
               if (dwStart == dwEnd && dwStart >= 0 && dwStart <= (DWORD)strlen(szEdit))
               { 
                    if (szEdit[dwStart] == '7')
                    {
                         // 7 was entered at caret (cursor) position
                    }
               }
         }
}

Open in new window



note, the code checking for the actual change would become easier if you save old value of edit control. then you only have to compare old value with new value to find out whether a '7' was entered.

note, if you use EN_UPDATE instead of EN_CHANGE the notification was sent before the edit control was displayed. you then can prevent the '7' to be displayed by calling SetWindowText.

Sara
Thank you both so much! Sara, as always, I appreciate your thorough explanations and code examples.

JKR, I am attempting to re-write to accommodate multiple controls. Thanks again.