Get the window handle of an externally running program through C++ - c++

There is an externally running program that i need the capability to resize. The kicker for me is that part of the title is the version and other specific information related to that instance. I know the substring that should be consistent across versions.
I have attempted the Findwindow() function, which works well if you have the exact wording of the title, but not when you only have a portion. I have also tried EnumWindows, but i believe that has the same limitations (i didn't have much luck with it).
I feel the simplest thing i could do (if possible) would be to get the window handle from the image name in order to do my resizing.
Ideas?

Here's a working piece of code I just tested on MSVS 2010 that works perfectly:
#include <stdlib.h>
#include <string.h>
#include <tchar.h>
#include <windows.h>
BOOL CALLBACK FindWindowBySubstr(HWND hwnd, LPARAM substring)
{
const DWORD TITLE_SIZE = 1024;
TCHAR windowTitle[TITLE_SIZE];
if (GetWindowText(hwnd, windowTitle, TITLE_SIZE))
{
//_tprintf(TEXT("%s\n"), windowTitle);
// Uncomment to print all windows being enumerated
if (_tcsstr(windowTitle, LPCTSTR(substring)) != NULL)
{
// We found the window! Stop enumerating.
return false;
}
}
return true; // Need to continue enumerating windows
}
int main()
{
const TCHAR substring[] = TEXT("Substring");
EnumWindows(FindWindowBySubstr, (LPARAM)substring);
}

EnumWindows was meant specifically for this. You create your own callback function to pass to EnumWindows, and it will call your callback function for each window it enumerates and pass it the hwnd of the window. You can call GetWindowText inside of your callback function to get the window title and search that text like any other. What problem did you have with that code? Why don't you post it?

Related

Port Win32/WinAPI application to wxWidgets

I'm currently using Windows 10, MSVC v142 (with VS2019) and wxWidgets 3.1.3. I have an old Windows C++ application that uses WinAPI for its GUI features, i.e Windows message loop, using "CreateWindow", and having to "manually" create all window procedures and event handling.
I want to improve this application by gradually replacing the UI using wxWidgets so I don't have to start over from scratch. I would implement new, independent UI features in wxWidgets (e.g specific dialogs), and then work my way back and replace all the old UI code with a wxWidgets implementation, without having to break the app along the way.
Below is a skeleton of how my app is currently set up:
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// Handling window messages, e.g menus, buttons, etc.
}
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, char* lpCmdLine, int nCmdShow)
{
// Initialize resources, register main window class using MainWndProc, etc.
// ...
HWND mainwnd = CreateWindow(/* CreateWindow args... */);
do
{
MSG msg = {};
while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Per-frame application logic
// ...
} while (msg.message != WM_QUIT);
// Clean up resources
// ...
return 0;
}
How would I need to modify this so that all the WinAPI objects continue to function, but I can now also create windows using wxWidgets? I previously tried replacing the message loop above by initializing wxWidgets through a custom class derived from wxApp, but my application kept crashing during the cleanup code (which it sometimes wouldn't even reach).
EDIT: I managed to make it work, updated skeleton can be found below.
// Main WinAPI window procedure
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
// Code to handle other messages
// ...
case WM_CLOSE:
// Closing this window should shut down the app
PostQuitMessage(0);
break;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
class MyApp : public wxApp
{
public:
virtual bool OnInit() override
{
if(!Old_Init())
{
// Perform cleanup in case something goes wrong
Old_Exit();
return false;
}
// Wrap the WinAPI window in a dummy wxWindow
m_dummyMainWindow = new wxWindow();
m_dummyMainWindow->SetHWND(m_mainWnd);
return true;
}
int OnExit() override
{
// Unset the dummy window HWND and delete it (is this necessary?)
m_dummyMainWindow->SetHWND(NULL);
delete m_dummyMainWindow;
// Clean up everything else
return Old_Exit();
}
private:
bool Old_Init()
{
// Perform the old initialization
// ...
m_mainWnd = CreateWindow(/* CreateWindow args... */);
if(m_mainWnd)
{
return true;
}
else
{
return false;
}
}
int Old_Exit()
{
// Perform the old cleanup (previously done after exiting the Windows message loop)
// ...
return 0;
}
HWND m_mainWnd;
wxWindow* m_dummyMainWindow;
};
wxIMPLEMENT_APP_NO_MAIN(MyApp);
// App entrypoint
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, char* lpCmdLine, int nCmdShow)
{
if (wxEntry())
{
// Exit if something goes wrong (this might not be the correct way to do it?)
wxExit();
}
return 0;
}
This does seem to function, all the old UI elements are working as before, but I am not 100% sure this is a stable solution. My debugger is warning me about memory leaks, which appear to increase in number when I activate UI elements (e.g open and close dialogs). I suspect WinAPI resources might not be cleaned up correctly. Am I missing anything?
EDIT2: I did some more debugging, and the code in my original app (i.e without wxWidgets) causes those "memory leaks" as well, and I can't replicate it in a minimal working example, so I suspect the issue is not related to wxWidgets at all. I am therefore confident that the approach described above should solve my problem, but I would not mind a second opinion.
I recommend looking at the MFC sample to see a working example of something close to what you want to do. MFC is, of course, not quite the same as Win32 API, but it should still be a good starting point.
Notably it shows how to pass the messages to wx event loop when you're running your own one (this part is in wxMFCApp class in include/wx/msw/mfc.h). Of course, if you can switch to running wx event loop, it would be even simpler.
You may also find it helpful to know that you can wrapp HWNDs you create into wxWindows using AssociateHandle() and handle their messages by overriding MSWHandleMessage() or even just using wxNativeWindow directly.
Good luck!

CFolderPickerDialog - No MFC

I'm trying to figure out if it is possible to use OR re-create the CFolderPickerDialog dialog without using MFC, or if there has been an attempt. So far I did not find a lot of hints. This old question does not seem to help me either.
I currently open the normal folder dialog with SHBrowseForFolder. But I need an Explorer-style dialog.
Here is the Explorer-style dialog (MFC) from another application:
#include <afxdlgs.h> requires MFC. I cannot use MFC in this specific project.
Is there a way to do this without using MFC ?
Honestly, I didn't even know that MFC had wrapped this. My class library has its own implementation. And, as Barmak points out, the MFC implementation may even be buggy, and certainly has usage caveats that would not be obvious had you failed to read the documentation carefully.
That said, in general, it is good advice to use functionality that is already wrapped up in a library because this makes your life easier. If you don't want to use the whole library, but still want to see how it implements a particular feature, you can check the library's source code. MFC is provided with reference source so that you can do this easily (also so you can debug it). Although it would probably be a violation of the license to copy and paste code directly out of MFC (it would also be nigh-impossible, since it uses so many MFC-specific idioms), you can look at the code to see what they're doing, then go back to the Windows SDK documentation to figure out how to write the code yourself.
In this case, the relevant SDK documentation is here. Modern versions of Windows (since Vista) use the Common Item Dialog API to display open/save file/folder dialogs. The API consists of a base IFileDialog interface, with two sub-interfaces, IFileOpenDialog and IFileSaveDialog. There is a lot of flexibility here; the details are in the documentation, along with sample code.
Note that the Common Item Dialog is only available on Windows Vista and later. If you need to support older operating systems (I still support Windows XP), you need a fallback. The SHBrowseForFolder dialog is that fallback. It certainly has its design flaws, but it is better than nothing.
If all you want is a simple folder-picker dialog, here is an approximation of the code that I use. It uses a couple of ATL/MFC types, like the CString and CComPtr wrapper classes, but you can translate that to alternate classes of your own choosing (such as std::wstring and _com_ptr_t). It displays a simple browse-for-folder dialog, appropriate for the current operating system, with a caller-specified title and starting path. If it succeeds, it returns a string containing the path to the folder selected by the user; otherwise, it returns an empty string.
namespace
{
HRESULT Downlevel_SHCreateItemFromParsingName(PCWSTR pszPath, IBindCtx* pbc, REFIID riid, void** ppv)
{
_ASSERTE(IsWinVistaOrLater());
HRESULT hResult = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
const HINSTANCE hinstLib = GetModuleHandle(TEXT("shell32"));
if (hinstLib)
{
typedef HRESULT (WINAPI * pfSHCreateItemFromParsingName)(PCWSTR, IBindCtx*, REFIID, void**);
const pfSHCreateItemFromParsingName pf = reinterpret_cast<pfSHCreateItemFromParsingName>(GetProcAddress(hinstLib, _CRT_STRINGIZE(SHCreateItemFromParsingName)));
if (pf)
{
hResult = pf(pszPath, pbc, riid, ppv);
}
}
return hResult;
}
int CALLBACK BrowseForFolderCallbackProc(HWND hWnd, UINT uMsg, LPARAM /* lParam */, LPARAM lData)
{
if (uMsg == BFFM_INITIALIZED)
{
// Start with BFFM_SETSELECTION, which is always available.
SendMessage(hWnd, BFFM_SETSELECTION, TRUE, lData);
#ifdef UNICODE
// If possible, also try to use BFFM_SETEXPANDED, which was introduced with
// version 6.0 of the shell (Windows XP).
SendMessage(hWnd, BFFM_SETEXPANDED, TRUE, lData);
// You can also set the caption for the dialog's "OK" button here, if you like
// (e.g., by loading a string from a resource).
//SendMessage(hWnd,
// BFFM_SETOKTEXT,
// 0,
// reinterpret_cast<LPARAM>(pszOKBtnCaption));
#endif // UNICODE
}
return 0;
}
}
CString ShowFolderBrowserDialog(HWND hwndOwner, const CString& strDlgTitle, const CString& strStartPath)
{
if (IsWinVistaOrLater())
{
CComPtr<IFileOpenDialog> pFileOpenDlg;
if (SUCCEEDED(pFileOpenDlg.CoCreateInstance(__uuidof(FileOpenDialog))))
{
if (SUCCEEDED(pFileOpenDlg->SetTitle(strDlgTitle)))
{
FILEOPENDIALOGOPTIONS options;
if (SUCCEEDED(pFileOpenDlg->GetOptions(&options)))
{
if (SUCCEEDED(pFileOpenDlg->SetOptions(options | FOS_PATHMUSTEXIST | FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM)))
{
CComPtr<IShellItem> psiStartPath;
if (SUCCEEDED(Downlevel_SHCreateItemFromParsingName(static_cast<const TCHAR*>(strStartPath),
NULL,
IID_PPV_ARGS(&psiStartPath))))
{
if (SUCCEEDED(pFileOpenDlg->SetFolder(psiStartPath)))
{
if (SUCCEEDED(pFileOpenDlg->Show(hwndOwner)))
{
CComPtr<IShellItem> pShellItemResult;
pFileOpenDlg->GetResult(&pShellItemResult);
CComHeapPtr<TCHAR> pszSelectedItem;
if (SUCCEEDED(pShellItemResult->GetDisplayName(SIGDN_FILESYSPATH, &pszSelectedItem)))
{
return pszSelectedItem;
}
}
}
}
}
}
}
}
}
else
{
TCHAR szBuffer[MAX_PATH + 1];
szBuffer[0] = TEXT('\0');
BROWSEINFO bi;
bi.hwndOwner = hwndOwner;
bi.pidlRoot = nullptr;
bi.pszDisplayName = szBuffer;
bi.lpszTitle = strDlgTitle;
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE | BIF_SHAREABLE | BIF_NONEWFOLDERBUTTON;
bi.lpfn = BrowseForFolderCallbackProc;
bi.lParam = reinterpret_cast<LPARAM>(static_cast<const TCHAR*>(strStartPath));
CComHeapPtr<ITEMIDLIST> pidl(SHBrowseForFolder(&bi));
if (pidl && SHGetPathFromIDList(pidl, szBuffer))
{
return pszSelectedItem;
}
}
return TEXT("");
}
The dialog only shows actual folders in the filesystem. Although the Common Item Dialog API supports other types of special folders and namespaces, I don't need that in my app, so my code doesn't deal with the complexity. Use this as a starting point, along with the documentation, if you need more features. The most notable aspect is probably the use of SHCreateItemFromParsingName (which I have wrapped up in a dynamic call so that the code continues to run on older operating systems) to translate the caller-specified starting path (which is a string) to a Shell item object (as required by the Common Item Dialog API).

C++ - WINAPI - Object-oriented approach to closing a window

While trying to create a nice wrapper around Win32 specific GUI components, I eventually ran into a problem. The problem is that I'm unable to close the application after the windows I created no longer exist.
My API works like this:
/// ----------------------------
/// #author God
/// #project Helixirr Widgets
/// ----------------------------
#include <helixirrwidgets/HelixirrWidgets.hpp>
int main(void){
HelixirrWidgets::Window __windows[2] = {HelixirrWidgets::Window("Big Window"), HelixirrWidgets::Window()};
__windows[0].position(200, 200);
__windows[0].size(800, 600);
__windows[0].visible(true);
__windows[0].save_changes();
__windows[1].name("Tiny Window");
__windows[1].position(10, 100);
__windows[1].size(400, 200);
__windows[1].visible(true);
__windows[1].save_changes();
while(__windows[0].active() || __windows[1].active()){
if(__windows[0].visible()){
__windows[0].show();
}
if(__windows[1].visible()){
__windows[1].show();
}
}
return 0;
}
In method of HelixirrWidgets::Window called "active", which is declared like this
inline bool active(void) const noexcept;
I can check, whether my window is active or not.
This method basically return a const reference to a boolean member variable of an instance. This member variable is modified in "show"-method of the same class. Here's the definition:
void Window::show(void){
if(GetMessage(&_m_opHelper->message, _m_opHelper->handle_window, 0, 0)){
if(_m_opHelper->message.message == WM_CLOSE){
_m_bActive = false;
return;
}
TranslateMessage(&_m_opHelper->message);
DispatchMessage(&_m_opHelper->message);
ShowWindow(_m_opHelper->handle_window, SW_SHOWDEFAULT);
UpdateWindow(_m_opHelper->handle_window);
_m_bActive = true;
return;
}
_m_bActive = false;
}
Do note I use pimpl-idiom to hide platform-specific structures ("_m_opHelper" is pointer to implementation).
It may look like it works, but it doesn't and I can't understand why. It all comes down to a simple question: how can I close my window implemented using WINAPI specific functions and structures to be closed appropriately by a user of my application?
I guess the cause of the issue is related to the fact WM_CLOSE simply is not last message HWND gets. Messages like WM_DESTROY, WM_NCDESTROY and possibly more (depending on the particlar window and its state) will come after WM_CLOSE, leading to the assignment _m_bActive = TRUE.
I.e. the window becomes inactive for very short time, and (likely) they will never be inactive at the same time, causing an endless loop in main().

Add visible window titles to combobox MFC

I want to add visible window titles to a combobox. Here is my source:
BOOL CALLBACK EnumWindowsProc(HWND hWnd, long lParam)
{
TCHAR buff[255];
CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_PROCESS);
if (IsWindowVisible(hWnd))
{
GetWindowText(hWnd, buff, 254);
pComboBox->AddString(buff);
}
return TRUE;
}
void CFindProcess::OnDropdownComboProcess()
{
EnumWindows(EnumWindowsProc, 0);
}
but I get error:
error C2660: 'GetDlgItem' : function does not take 1 arguments 60
How I can correctly add titles to combo?
MFC objects are thread-sensitive, GetDlgItem works well in the thread that created the object, probably the main UI thread. Function EnumWindows probably creates a worker thread to access the callback function, and that is why GetDlgItem failed to get a valid handle of the combobox.
To access the combobox properly in another thread, you have to use the static function: CWnd::FromHandle with the raw handle of the combobox object as follows:
BOOL CALLBACK EnumWindowsProc(HWND hWnd, long lParam)
{
if (IsWindowVisible(hWnd))
{ TCHAR szBuffer[255];
INT nLength = GetWindowText(hWnd, szBuffer, 254);
if (nLength>0)
{ // only add windows that has a caption
CComboBox *pComboBox = (CComboBox*)CWnd::FromHandle((HWND)lParam);
pComboBox->AddString(szBuffer);
}
}
return TRUE;
}
// call EnumWindows --------------------
CComboBox *pComboBox = (CComboBox *)GetDlgItem(IDC_COMBO1);
// passing the raw handle of the combobox as parameter
EnumWindows(EnumWindowsProc, (LPARAM)pComboBox->m_hWnd);
Firstly, your GetDlgItem has two parameters, and the first is a handle to the dialog box that contains the control.
So it expects a HWND parameter of the dialog that contains this control, I would presume that will be the HWND you pass as a parameter to your function.
CComboBox* pComboBox = (CComboBox*)GetDlgItem(hWnd,IDC_COMBO_PROCESS);
^^^^ added parameter
If you look at EnumWindows in MSDN, you'll see you have to pass a callback and it has a HWND parameter, if you look at what this parameter is for it says:
A handle to a top-level window.
This is exactly what you have to pass to GetDlgItem.
Also, you should check the return value of GetWindowText as this returns the number of characters written to the buff you passed it.
int ret = GetWindowText(hWnd, buff, 254);
if (ret > 0) pComboBox->AddString(buff); // only add non-empty strings.
In addition to what user #mfc has provided, I would not do UI update from a different thread. I believe EnumWindows does not create thread for enumeration. It would call the callbacks within the call-stack of current thread.
This, in turn, means that UI may freeze for a while. Thus, it is recommended to create a thread for enumeration. More over, I would not directly update UI from different thread. May be a vector of string, or a PostMessage (on each iteration) I would have used.
It is true that EnumWindows may perform quite fast. But when you move to enumerate other (kernel) objects like file, printers, users etc - the UI is definitely going to freeze. So, better practice writing multithreaded code. Initially writing MT-code would be a pain, but later you'd love it, appreciate it, and cannot live without it.

Notify CDialog-Window when selecting another window

I got a dialog-based MFC-Tool that is supposed to show the title of a window of another application in a messagebox when i click on it.
My Problem is that the WM_KILLFOCUS doesn't work here. Maybe I'm doing it wrong.
I do the following:
BEGIN_MESSAGE_MAP(CMyDlg, CDialog)
ON_WM_KILLFOCUS()
END_MESSAGE_MAP()
...
...
void CMyDlg::OnKillFocus( CWnd* pNewWnd )
{
CDialog::OnKillFocus(pNewWnd);
if(m_bSelectorModeActive)
{
HWND hwnd(GetForegroundWindow());
TCHAR buf[512];
::GetWindowText(hwnd, buf, 512);
MessageBox(buf);
}
}
Any idea what's wrong?
This is my guess
Replace HWND hwnd(GetForegroundWindow()); with GetActiveWindow(void) .
I solved it, thanks for your efforts.
Yes, i do use CStrings, this was just a little example of a bit more complex thing i do. My problem was not the function itself but the event WM_KILLFOCUS that didn't seem to work.
Maybe I was not clear enough here, sorry.
WM_ACTIVATE does what i need. It notifies my dialog when the focus is set and/or lost.
The code you've shown shouldn't even compile. The GetForegroundWindow function provided by MFC doesn't return an HWND, so you can't initialize the hwnd variable using its return value.
If you want to get an HWND, you need to call GetForegroundWindow from the Windows API by escaping the call with ::, just like you did for GetWindowText. So simply rewrite your code as follows:
void CMyDlg::OnKillFocus( CWnd* pNewWnd )
{
CDialog::OnKillFocus(pNewWnd);
if(m_bSelectorModeActive)
{
HWND hwnd(::GetForegroundWindow());
TCHAR buf[512];
::GetWindowText(hwnd, buf, 512);
MessageBox(buf);
}
}
Beyond that, in looking at your code, one wonders that you seem to be ignoring the object-orientation MFC so humbly attempts to bring to the Windows API. You shouldn't need to work directly with window handles. And one could argue that the most compelling reason to use MFC is its CString class. There's no reason you should have to deal with an array of TCHARs anymore. I might write this instead:
void CMyDlg::OnKillFocus( CWnd* pNewWnd )
{
CDialog::OnKillFocus(pNewWnd);
if(m_bSelectorModeActive)
{
CWnd* pForeWnd = GetForegroundWindow();
CString windowText;
pForeWnd->GetWindowText(windowText);
MessageBox(windowText);
}
}