EnumChildWindows never calls its callback - c++

I'm trying to manipulate a specific Internet Explorer 11 window. Using WinSpy++ I find that
The top level window's class is an IEFrame with the title of the document as the text (as returned by GetWindowText)
The actual web view class is called "Internet Explorer_Server" and is a child of the former.
I wrote a simple test case for finding the web view of IE11 opened on "https://encrypted.google.com/" in three different ways:
HWND FindIE_A()
{
// Use FindWindow, works!
HWND hWndTop = ::FindWindowA( NULL, "Google - Internet Explorer" );
// Find the web view window, the callback (FindIEServer) is NEVER called!
HWND hWnd = NULL;
::EnumChildWindows( hWndTop, &FindIEServer, (LPARAM)&hWnd );
return hWnd;
}
HWND FindIE_B()
{
// Use EnumChildWindows with NULL as parent, works!
HWND hWndTop = NULL;
::EnumChildWindows( NULL, &FindIEMain, (LPARAM)&hWndTop );
// Find the web view window, the callback (FindIEServer) is NEVER called!
HWND hWnd = NULL;
::EnumChildWindows( hWndTop, &FindIEServer, (LPARAM)&hWnd );
return hWnd;
}
HWND FindIE_C()
{
// Simple EnumWindows, works!
HWND hWndTop = NULL;
::EnumWindows( &FindIEMain, (LPARAM)&hWndTop );
// Find the web view window, the callback (FindIEServer) is NEVER called!
HWND hWnd = NULL;
::EnumChildWindows( hWndTop, &FindIEServer, (LPARAM)&hWnd );
return hWnd;
}
The callbacks that are very simple; get a property from the window and compare against a hard-coded value:
BOOL CALLBACK FindIEServer( HWND hWnd, LPARAM lParam )
{
char className[64];
::GetClassNameA( hWnd, className, sizeof(className) );
if ( !strcmp( className, "Internet Explorer_Server" ) )
{
*(HWND*)lParam = hWnd;
return FALSE;
}
return TRUE;
}
BOOL CALLBACK FindIEMain( HWND hWnd, LPARAM lParam )
{
char text[128];
::GetWindowTextA( hWnd, text, sizeof(text) );
if ( !strcmp( text, "Google - Internet Explorer" ) )
{
*(HWND*)lParam = hWnd;
return FALSE;
}
return TRUE;
}
EnumChildWindows failed (by not calling the callback AT ALL!) every time when provided with a parent window. Why?

The problem is that when I look for the window title, I was assuming there was only one window with that title. However Internet Explorer does some shenanigans and creates multiple windows with the same title however only one of them has the class IEFrame.
It just so happens that the first window found was the wrong one, it didn't have any children (and thus EnumChildWindows doesn't have anything to iterate over). Just adding an extra check for title + class works.
However as #wakjah suggested, it is better integrate IE (or any other browser) directly into your code. With google I found lots of documentation on how to do this with both IE and Chrome.

Related

MFC: OnNcCreate() not called; need to set BS_OWNERDRAW flag for CButton subclass

I'm not an expert on MFC, but I've made a dozen or so custom controls over the last 15+ years. I've just made a CButton with custom graphics.
Here's the problem: Of course I need BS_OWNERDRAW to be set. I have an OnNcCreate() method I've cut and pasted for 15+ years that's always worked in my custom controls, but for some reason it's not now being called.
I have no interest or even understanding of OnNcCreate() except for the fact that it was the place I ended up getting BS_OWNERDRAW set the first time I tried writing a custom control.
Question: What would keep OnNcCreate() from being called? Or, is there another place I could or should be setting BS_OWNERDRAW ?
A working class is defined with:
class CGood : public CStatic {
The non-working one is defined with:
class CBad : public CButton {
They both prototype the method thusly:
afx_msg int OnNcCreate( LPCREATESTRUCT lpCreateStruct );
The method is written identically (except the class name):
int CGood::OnNcCreate( LPCREATESTRUCT pcrs ) {
wStyleBits = LOWORD( pcrs->style );
DWORD dwStyleSuperclass = MAKELONG( ES_LEFT, HIWORD( pcrs->style ) );
dwStyleSuperclass |= BS_OWNERDRAW;
::SetWindowLong( m_hWnd, GWL_STYLE, dwStyleSuperclass );
pcrs->style = dwStyleSuperclass;
return CStatic::OnNcCreate( pcrs );
}
The dialog is using both the good and bad classes utilizing the Visual Studio 2017 dialog editor, as CustomControl. Both the good and bad classes have Extended Style 0x0 and Style 0x50010000. Both er Disabled=False, Help ID=False, Visible=True, Class=Good or Bad as needed.
When run the application calls each class's RegisterControlClass() method:
static WNDPROC pfnWndProc = NULL;
BOOL CBad::RegisterControlClass() {
WNDCLASS wcls;
static const TCHAR szClass[] = _T( "CBad" );
if ( ::GetClassInfo( AfxGetInstanceHandle(), szClass, &wcls ) )
return wcls.lpfnWndProc == ( WNDPROC ) CBad::WndProcHook;
VERIFY( ::GetClassInfo( NULL, _T( "button" ), &wcls ) );
pfnWndProc = wcls.lpfnWndProc;
wcls.lpfnWndProc = CBad::WndProcHook;
wcls.hInstance = AfxGetInstanceHandle();
wcls.lpszClassName = szClass;
return RegisterClass( &wcls ) != 0;
}
LRESULT CALLBACK EXPORT CBad::WndProcHook( HWND hWnd, UINT msg,
WPARAM wParam, LPARAM lParam ) {
CBad* pthis = new CBad();
pthis->Attach( hWnd );
pthis->m_pfnSuper = pfnWndProc;
::SetWindowLong( hWnd, GWL_WNDPROC, ( DWORD )AfxWndProc );
#ifdef STRICT
return ::CallWindowProc( AfxWndProc, hWnd, msg, wParam, lParam );
#else
return ::CallWindowProc( ( FARPROC )AfxWndProc, hWnd, msg, wParam, lParam );
#endif
}
(Funny: I note in CGood, derived from CStatic, mistakenly has "button" there and works fine; it stops working if I change to "static" . I'll look into that later. The bad class has "button" and indeed is a CButton subclass.)
Things I've done to confirm the issue: When I remove the BS_OWNERDRAW from the CGood::OnNcCreate(), it stops calling DrawItem(). So I'm sure that's the place this flag is being set.
Putting a breakpoint in CGood::OnNcCreate() and CBad::OnNcCreate(), the CBad one simply isn't called.
I've found examples on the internet of setting BS_OWNERDRAW in PreSubclassWindow(), OnCreate(), and CreateEx(), none of which work, in some cases for reasons I think I understand. (EG you can't just set flags in OnCreate() and call the superclass method, because it is ultimately getting the flags not from the param you give it but from some other source.)
I've tried changing CBad's superclass to CStatic and still the OnNcCreate() isn't being called.
I've tried setting BS_OWNERDRAW in the dialog's OnInitDialog() and that does get the CBad button subclass painting correctly. So I know I'm not accidently creating them with some other subclass, etc.
CBad* pcbut = (CBad*) GetDlgItem( IDC_MARKCHECK );
pcbut->ModifyStyle(0, BS_OWNERDRAW);
Ahh! I simply forgot to add it to the message map!
BEGIN_MESSAGE_MAP( CBad, CButton )
//{{AFX_MSG_MAP( CBad )
ON_WM_NCCREATE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

Display formatted text on selecting item in the Combobox

I have a combobox in that I want to display different string on selecting an item in Combo.
My combo box is a dropdown combobox.
For eg: I have following in my combobox.
Alex - Manager
Rain - Project Lead
Shiney - Engineer
Meera - Senior Engineer
OnSelecting an item in combobox I want to diaply only name i.e. Alex.
I tried below code
struct details{
CString name;
CString des;
};
BOOL CComboTestDlg::OnInitDialog()
{
CDialog::OnInitDialog();
details d1;
d1.name = _T("alex");
d1.des =_T("manager");
m_vec.push_back(d1);
details d2;
d2.name = _T("Rain");
d2.des =_T("Engineer");
m_vec.push_back(d2);
// TODO: Add extra initialization here
for(int i=0;i<m_vec.size();i++)
{
m_ctrlCombo.AddString(m_vec[i].name+m_vec[i].des);
m_ctrlCombo.SetItemData(i,(DWORD_PTR)&m_vec[i]);
}
m_ctrlCombo.SelectString(-1,m_vec[0].name);
m_ctrlCombo.SetWindowText(m_vec[0].name);
return TRUE; // return TRUE unless you set the focus to a control
}
void CComboTestDlg::OnCbnSelchangeCombo1()
{
int nItem = m_ctrlCombo.GetCurSel();
details* det = (details*)m_ctrlCombo.GetItemData(nItem);
PostMessage(SETCOMBOTEXT,IDC_COMBO1,(LPARAM)(LPCTSTR)det->name);
}
BOOL CComboTestDlg::PreTranslateMessage(MSG* pMsg)
{
MSG msg1=*pMsg;//I am loosing the value after checking ..so storing temp.
MSG msg;
CopyMemory(&msg, pMsg, sizeof(MSG));
HWND hWndParent = ::GetParent(msg.hwnd);
while (hWndParent && hWndParent != this->m_hWnd)
{
msg.hwnd = hWndParent;
hWndParent = ::GetParent(hWndParent);
}
if (pMsg->message==SETCOMBOTEXT && (pMsg->wParam == IDC_COMBO1))
SetDlgItemText(IDC_COMBO1, (LPCTSTR)pMsg->lParam);
if(pMsg->message==WM_KEYDOWN)
{
if(pMsg->wParam==VK_RETURN && msg.hwnd ==m_ctrlCombo.m_hWnd )
{
OnCbnSelchangeCombo1();
}
}
return CDialog::PreTranslateMessage(pMsg);
}
I am able to achieve my requirement OnComboSelChange() and Arrow Keys event but on pressing enter key after using arrow keys in combo box, it is not showing formatted text in combo box.
I think the most reliable and easy to implement solution is to subclass the edit control of the combobox. Intercept the WM_SETTEXT message and change the text as you like before forwarding it to the rest of the chain (finally the original window proc).
Install the sub class proc in OnInitDialog():
COMBOBOXINFO cbi{ sizeof(cbi) };
if( m_ctrlCombo.GetComboBoxInfo( &cbi ) )
{
SetWindowSubclass( cbi.hwndItem, ComboEditSubClassProc, 0, 0 );
}
ComboEditSubClassProc() could look like this:
LRESULT CALLBACK ComboEditSubClassProc( HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
switch( uMsg )
{
case WM_SETTEXT:
{
CString text = reinterpret_cast<LPCTSTR>( lParam );
// Extract the name (everything before "-").
CString name = text.SpanExcluding( _T("-") );
name.TrimRight();
// Forward the modified text to any other sub class procs, aswell
// as the original window proc at the end of the chain.
return DefSubclassProc( hWnd, uMsg, 0, reinterpret_cast<LPARAM>( name.GetString() ) );
}
case WM_NCDESTROY:
{
// We must remove our subclass before the subclassed window gets destroyed.
// This message is our last chance to do that.
RemoveWindowSubclass( hWnd, ComboEditSubClassProc, uIdSubclass );
break;
}
}
return DefSubclassProc( hWnd, uMsg, wParam, lParam );
}
Notes:
Contrary to my original solution of processing CBN_SELCHANGE, the current solution also works correctly if the combobox drop-down list is closed by pressing Return or is dismissed.
I think it is in general more reliable because we don't have to rely on the order of the notifications. The combobox has to finally call WM_SETTEXT to change the content of the edit control so this message will always be received.
There will also be no flickering as in the original solution where the text was first changed by the combobox and then modified by our code only after the fact.

WinApi - How to obtain SHELLDLL_DefView

I am trying to obtain handle to SHELLDLL_DefView.
So, I have this code.
HWND hProgman = FindWindow(L"Progman", NULL);
HWND hWnd = FindWindowEx(hProgman, 0, L"SHELLDLL_DefView", NULL);
Eveyrtihing works OK, until I change in Windows desktop brackground to slideshow. Then when I search with spy++ hierarchy of the windows, than SHELLDLL_DefView has another parent. Now it is #32769 (Desktop) -> WorkerW -> SHELLDLL_DefView. So I can't find it. Problem is that when I try
HWND desktop = GetDesktopWindow();
HWND hWnd = FindWindowEx(desktop , 0, L"WorkerW", NULL);
HWND hWnd = FindWindowEx(hWnd, 0, L"SHELLDLL_DefView", NULL);
Than SHELLDLL_DefView is not found. WorkerW yes.
Can anybody help?
Your code only works on some Windows versions as "SHELLDLL_DefView" can be found under "WorkerW" or "Progman" and as you discovered there can be many windows under the "WorkerW" class (normal in Win7).
Microsoft Docs report EnumWindows() is more reliable than calling GetWindow()/FindWindowEx() functions in loops, so more universal code (tested on Windows 98/Windows 7) would look like this (say you want to refresh the desktop):
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) {
HWND hNextWin;
hNextWin = FindWindowExA(hwnd, 0, "SHELLDLL_DefView", 0);
if ( hNextWin ) {
// The correct desktop shell window under Progman/WorkerW will have only 1 child window!
if ( GetNextWindow(hNextWin, GW_HWNDNEXT) || GetNextWindow(hNextWin, GW_HWNDPREV) )
return true;
// We found correct handle
PostMessageA(hNextWin, WM_KEYDOWN, VK_F5, 0);
return false;
}
return true;
}
void main() {
EnumWindows(&EnumWindowsProc, 0);
}
I found the answer. Need to iterate through all WorkerW.
HWND destop = GetDesktopWindow();
HWND hWorkerW = NULL;
HWND hShellViewWin = NULL;
do
{
hWorkerW = FindWindowEx(destop, hWorkerW, L"WorkerW", NULL);
hShellViewWin = FindWindowEx(hWorkerW, 0, L"SHELLDLL_DefView", 0);
} while (hShellViewWin == NULL && hWorkerW != NULL);

How to Get Tooltip of a Window From Window Handle? [duplicate]

I'm looking for a way to get the tooltip control (if any) which is associated with a given HWND. The text of the tooltip control would be sufficient, too. The closest thing I found is the TTM_GETTEXT message, but it's meant to be sent to the tooltip control itself instead of the tool it's associated with. I don't have a handle to the tooltip control though. Does anybody know how to do this?
All this is done using plain Windows API in C++.
There doesn't seem to be a specific message to get the tip or its text from the control, but this is how MFC's CWnd class implements OnToolHitTest(), which you should be able to adapt to Win32:
INT_PTR SomeFunction(HWND hWndChild, TOOLINFO *pTI)
{
if (hWndChild != NULL) // Your HWND being tested
{
// return positive hit if control ID isn't -1
INT_PTR nHit = _AfxGetDlgCtrlID(hWndChild);
// Replace with GetDlgCtrlID().
// hits against child windows always center the tip
if (pTI != NULL && pTI->cbSize >= sizeof(AFX_OLDTOOLINFO))
{
// setup the TOOLINFO structure
pTI->hwnd = m_hWnd;
pTI->uId = (UINT_PTR)hWndChild;
pTI->uFlags |= TTF_IDISHWND;
pTI->lpszText = LPSTR_TEXTCALLBACK;
// set TTF_NOTBUTTON and TTF_CENTERTIP if it isn't a button
if (!(::SendMessage(hWndChild, WM_GETDLGCODE, 0, 0) & DLGC_BUTTON))
pTI->uFlags |= TTF_NOTBUTTON|TTF_CENTERTIP;
}
return nHit;
}
return -1; // not found
}
Hopefully this will be useful.
To get tooltip text from some control you could use TTN_NEEDTEXT message. It was designed to be used by the ToolTip control, but I cannot see any reason why you could not send it from other place.
You could enumerate the windows looking for a tooltip control that has a parent of the required window. You'll need to supply the window together with the tool id (normally from GetDlgCtrlID)...:
HWND hToolTipWnd = NULL;
BOOL GetToolTipText(HWND hWnd, UINT nId, std::wstring& strTooltip)
{
hToolTipWnd = NULL;
EnumWindows(FindToolTip, (LPARAM)hWnd);
if (hToolTipWnd == NULL)
return FALSE;
WCHAR szToolText[256];
TOOLINFO ti;
ti.cbSize = sizeof(ti);
ti.hwnd = hWnd;
ti.uId = nId;
ti.lpszText = szToolText;
SendMessage(hToolTipWnd, TTM_GETTEXT, 256, (LPARAM)&ti);
strTooltip = szToolText;
return TRUE;
}
BOOL CALLBACK FindToolTip(HWND hWnd, LPARAM lParam)
{
WCHAR szClassName[256];
if (GetClassName(hWnd, szClassName, 256) == 0)
return TRUE;
if (wcscmp(szClassName, L"tooltips_class32") != 0)
return TRUE;
if (GetParent(hWnd) != (HWND)lParam)
return TRUE;
hToolTipWnd = hWnd;
return FALSE;
}
I don't know if the window whose tooltip you want to retrieve is a child of a window you have created.
If this is the case, you can handle the NM_TOOLTIPSCREATED notification, which is sent by a child window to its parent when it creates a tooltip (or should be sent: it is true for common controls but I don't know for other kinds of windows). This notification includes a handle to the tooltip window.

Activate Window from code problem

I have a window on my desktop called: "Kaspersky Anti-Virus Configuration Wizard"
Here is some info about the window:
>>>> Window <<<<
Title: Kaspersky Anti-Virus Configuration Wizard
Class: AVP.ConfigureWizard
Position: 612, 247
Size: 499, 388
Style: 0x94CA0044
ExStyle: 0x00010100
Handle: 0x00081308
The window does not appear in the Windows Task Manager Tasks list (only it's process exists in processes list as "avp.exe" what, as far as i think, making it hard for me to acheive my goal. First of all i would appriciate that someone will explain how can Kaspersky Programmed i window that does not exists in "Application" Tab in "Windows Task Manager". Secondly I would be very thankful if you can help me solve my problem which is detailed here:
I want to make the window activeate (Set Focus on the window) from code (C++ \ Autoit).
I tried to use the FindWindow function of WinAPI but couldn't get the handle to this window.
I got the handle with GetForegroundWindow function and I've found out that when i use EnumWindows function the handle to kaspersky configuration window was not in the list..
this was my code:
BOOL CALLBACK EnumWindowsProc(__in HWND hwnd, __in LPARAM lParam)
{
if(g_hWnd == hwnd)
{
cout << "Found window";
return FALSE;
}
return TRUE;
}
BOOL CALLBACK EnumDesktopProc(
__in LPTSTR lpszDesktop,
__in LPARAM lParam
)
{
EnumDesktopWindows(OpenDesktop(lpszDesktop,DF_ALLOWOTHERACCOUNTHOOK,FALSE,DESKTOP_ALL),EnumWindowsProc, NULL);
return true;
}
BOOL CALLBACK EnumWindowStationProc(
__in LPTSTR lpszWindowStation,
__in LPARAM lParam
)
{
EnumDesktops(OpenWindowStation(lpszWindowStation,FALSE, WINSTA_ALL_ACCESS),EnumDesktopProc, NULL );
return true;
}
int main()
{
Sleep(3000);
g_hWnd = GetForegroundWindow(); //Here i switch to kaspersky window to get it's handle
EnumWindowStations(EnumWindowStationProc, NULL); //I call EnumDesktopWindows in EnumDesktops in EnumWindowStations to search in all HWND of my Operation System.
}
the cout << "Found Window" statement never executed.
I would be very grateful if you could help me slove this and show me the ability to make this window active.