in my application i have timer, in TimerProc i want to get handles of all windows(main and child) of the another application that has focus. I have no idea how to do that because i don't understand functions like GetNextWindow or GetParent and Z-oder of windows and i can't find anywhere very detailed explanation of how this functions works(i dont understand explanation on msdn). Please can you give me some advice or block of code which do that? Many thanks for answer.
Use GetForegroundWindow() function - it returns the HWND of the window the user currently is working with.
Then having this handle you can retrieve childs in such a way:
HWND a_hWnd = (HWND)hParent;
HWND a_FirstChild = NULL;
a_FirstChild = ::GetWindow(a_hWnd, GW_CHILD);
if (a_FirstChild != NULL)
{
HWND a_NextChild = NULL;
do
{
a_NextChild = ::GetWindow(a_FirstChild, GW_HWNDNEXT);
if (a_NextChild != NULL)
{
a_FirstChild = a_NextChild;
}
}
while (a_NextChild != NULL);
}
GetForeGroundWindow to get the current foreground window/dialog
GetParent until you get NULL (that gets you to the top level window)**
EnumChildWindows to get to all the dependent windows
** Note that an application can have more than one top level window, though this isn't usual.
Code:
void Ccpp_testDlg::DoWalk ()
{
HWND hCurrent;
HWND hNew;
hCurrent = ::GetForegroundWindow ();
hNew = hCurrent;
while (hNew != NULL)
{
hNew = ::GetParent (hCurrent);
if (hNew != NULL)
{
hCurrent = hNew;
}
}
EnumChildWindows (hCurrent, EnumProc, 0);
}
BOOL CALLBACK EnumProc (HWND hwnd,LPARAM lParam)
{
TCHAR szText [MAX_PATH];
GetWindowText (hwnd, szText, sizeof(szText));
// do something with text
return TRUE;
}
Related
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);
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.
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.
I want to detect all the top-level windows in order to send messages to it's Descendants.
How can I do that?
The following code seems to not be detecting Qt top level window, I don't know why.
static BOOL CALLBACK EnumWindowsProc(_In_ HWND hwnd, _In_ LPARAM lParam) {
WORD far wndProcessID;
WORD currentProcessID = GetCurrentProcessId();
std::vector<HWND> *topWindowList = (std::vector<HWND> *)lParam;
if (topWindowList != NULL &&
GetWindowThreadProcessId(hwnd, NULL) == currentProcessID) {
printf("Found a top level window");
fflush(stdout);
topWindowList->push_back(hwnd);
}
return TRUE;
}
void enumAllDesktopChildWindow() {
std::vector<HWND> topWindowList;
EnumChildWindows(GetDesktopWindow(), EnumWindowsProc, LPARAM(&topWindowList));
}
First, the GetWindowThreadProcessId API returns a Thread ID (TID) not a Process ID (PID)
Second, if you want to enumerate all top-level Windows, you should use EnumWindows, not EnumChildWindows. If you want to use EnumChildWindows, pass NULL as the first parameter.
When I try to instantiate a CFileDialog object it shows both the folders and files. How do you create a CFileDialog that browses for folders alone?
It is very simple, really.
Use CFolderPickerDialog which is derived from the class CFileDialog!
You can't do it with CFileDialog.
Either you will use SHBrowseForFolder Function or a wrapper for it, like CFolderDialog - Selecting Folders.
Starting from Vista it's recommended to use IFileDialog with the FOS_PICKFOLDERS option (see msdn):
CFileDialog od(TRUE/*bOpenFileDialog*/, NULL, NULL,
OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT , NULL, NULL, 0,
TRUE/*bVistaStyle*/);
IFileOpenDialog * openDlgPtr = od.GetIFileOpenDialog();
if ( openDlgPtr != NULL )
{
openDlgPtr->SetOptions(FOS_PICKFOLDERS);
openDlgPtr->Release();
}
od.DoModal();
Like someone mentioned, use CFolderPickerDialog which works great. I would like to give you example how to use it especially when using the multi select flag:
CFolderPickerDialog folderPickerDialog(initialFolder, OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT | OFN_ENABLESIZING, this,
sizeof(OPENFILENAME));
CString folderPath;
if (folderPickerDialog.DoModal() == IDOK)
{
POSITION pos = folderPickerDialog.GetStartPosition();
while (pos)
{
folderPath = folderPickerDialog.GetNextPathName(pos);
}
}
starting from windows vista,you can use the Common Item Dialog .
void CQiliRegrvDlg::OnBnClickedSelectDir()
{
HRESULT hr = S_OK;
// Create a new common open file dialog.
IFileOpenDialog *pfd = NULL;
hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pfd));
if (SUCCEEDED(hr))
{
// Set the dialog as a folder picker.
DWORD dwOptions;
hr = pfd->GetOptions(&dwOptions);
if (SUCCEEDED(hr))
{
hr = pfd->SetOptions(dwOptions | FOS_PICKFOLDERS);
}
// Set the title of the dialog.
if (SUCCEEDED(hr))
{
hr = pfd->SetTitle(L"Folder");
}
// Show the open file dialog.
if (SUCCEEDED(hr))
{
hr = pfd->Show(m_hWnd);
if (SUCCEEDED(hr))
{
// Get the selection from the user.
IShellItem *psiResult = NULL;
hr = pfd->GetResult(&psiResult);
if (SUCCEEDED(hr))
{
PWSTR pszPath = NULL;
hr = psiResult->GetDisplayName(SIGDN_FILESYSPATH, &pszPath);
if (SUCCEEDED(hr))
{
m_appDir = pszPath;
SetDlgItemText(IDC_STATIC, m_appDir);
CoTaskMemFree(pszPath);
}
psiResult->Release();
}
}
}
pfd->Release();
}
}
Seems to me the answer you are asking for is inside the code of
CMFCPropertyGridFileProperty::OnClickButton(CPoint /*point*/)
of the
<Your Visual Studio installation folder>\VC\atlmfc\src\mfc\afxpropertygridctrl.cpp
file.
If you do not have access to the code, I will post the essential part of it:
CString strPath = m_varValue.bstrVal;
BOOL bUpdate = FALSE;
if (m_bIsFolder)
{
if (afxShellManager == NULL)
{
CWinAppEx* pApp = DYNAMIC_DOWNCAST(CWinAppEx, AfxGetApp());
if (pApp != NULL)
{
pApp->InitShellManager();
}
}
if (afxShellManager == NULL)
{
ASSERT(FALSE);
}
else
{
bUpdate = afxShellManager->BrowseForFolder(strPath, m_pWndList, strPath);
}
}
else
{
CFileDialog dlg(m_bOpenFileDialog, m_strDefExt, strPath, m_dwFileOpenFlags, m_strFilter, m_pWndList);
if (dlg.DoModal() == IDOK)
{
bUpdate = TRUE;
strPath = dlg.GetPathName();
}
}
As you see, Microsoft itself does not use the Cfiledialog class when wants to open a dialog for picking folders.
For using code like that, your application class MUST be derived from CWinAppEx, not CWinApp
Actually there is a way to do this - I found it in codeguru: "Selected files and folders in CFileDialog"
If you are willing to make your own implementation of CFileDialog such as:
class CMyFileDialog : public CFileDialog
You can add the following code and it should work (It is slightly different from the codeguru example):
// This code overrides the OnNotify message of the CFileDialog
// and catches the CDN_SELCHANGE, this way you can also do
// something with the selected folders.
BOOL CMyFileDialog::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
NMHDR* pNotificationParam = (NMHDR*)lParam;
// Check that we got to the selection change notification.
int code = pNotificationParam->code;
if (code == CDN_SELCHANGE)
{
CStringArray theSelection;
GetListControllSelectedItems(theSelection);
// Do as you want with theSelection.
}
return CFileDialog::OnNotify(wParam, lParam, pResult);
}
// The following Code is accessing the selection in the CFileDialog
// and filling a string array with the selected names
BOOL CMyFileDialog::GetListControllSelectedItems(CStringArray& selectedItemNames)
{
BOOL rc = FALSE;
// Get the list control of the file dialog.
CWnd* pParentWnd = GetParent();
CWnd* pListControlWnd = pParentWnd->GetDlgItem(lst2);
if (pListControlWnd) {
// Get the selection from the list control.
CListCtrl* pListCtrl = (CListCtrl*)(pListControlWnd->GetDlgItem(1));
UINT selectionCount = pListCtrl->GetSelectedCount();
// When there are items selected.
if (selectionCount) {
rc = TRUE;
selectedItemNames.RemoveAll();
POSITION itemPos = pListCtrl->GetFirstSelectedItemPosition();
while (itemPos != NULL)
{
int itemNum = pListCtrl->GetNextSelectedItem(itemPos);
CString currentItemName = pListCtrl->GetItemText(itemNum, 0);
selectedItemNames.Add(currentItemName);
}
}
}
return rc;
}
Note: In CFileDialog::OnFileNameChange of the Microsoft MFC documentation they actually do hint toward this solution, but without elaborating too much.
I had a problem in my very old, legacy code, where I have a customized file dialog that actually needs to save a folder!!!
After twenty two years of hardship and pain, my code is now complete...