I am trying to write a program that can get the window title of a process.
Before I describe the problem, here is the code:
#include <Windows.h>
#include <string>
#include <Psapi.h>
#include <algorithm>
std::string window_title;
std::string search_for;
BOOL CALLBACK EnumWindowCallback(HWND hWindow, LPARAM param)
{
if ( IsWindow( hWindow ) == TRUE )
{
DWORD pid = 0;
if ( GetWindowThreadProcessId( hWindow, &pid ) != 0 )
{
HANDLE hProcess;
hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pid );
if ( hProcess != 0 )
{
std::string path;
CHAR name[MAX_PATH];
GetModuleFileNameExA( hProcess, NULL, name, sizeof(name) / sizeof(CHAR) );
path = name;
unsigned int slash = path.find_last_of('\\');
if ( slash != std::string::npos ){
std::string proc_name = path.substr( slash + 1, path.length() );
std::transform(proc_name.begin(), proc_name.end(), proc_name.begin(), ::tolower);
if ( proc_name == search_for )
{
CHAR finalTitle[MAX_PATH];
ZeroMemory( finalTitle, sizeof(finalTitle) );
SendMessageA( hWindow, WM_GETTEXT, (WPARAM)sizeof(CHAR)/sizeof(MAX_PATH), (LPARAM)finalTitle );
window_title = finalTitle;
return FALSE;
}
}
}
}
}
return TRUE;
};
const char* __stdcall GetWinTitleByProcessName( const char* title )
{
search_for = title;
std::transform(search_for.begin(), search_for.end(), search_for.begin(), ::tolower);
if ( EnumWindows( (WNDENUMPROC)EnumWindowCallback, 0 ) == FALSE )
{
return window_title.c_str();
}
return "NOTFOUND";
}
int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int )
{
MessageBoxA( NULL, GetWinTitleByProcessName("chrome.exe"), "Test", MB_OK);
}
The program works so far, until I want to get the actual title of the window.
I tried GetWindowText and SendMessage, as shown here.
Both methods return empty strings.
How can I get the window title?
The following code works for a similar problem. In my case I am looking for the windows handle of an application so that I can parent a dll. I identify the app by its caption. Its C++Builder code so some parts may be unfamiliar. I'll comment the differences I spot. The main one is the use of Application, I'm not sure what the non-Embarcadero equivalent is, but each running instance of code has an Application instance that manages the message loop and so on. I set my dll's Application->Handle to the calling apps hWnd to keep it off the taskbar, among other things. This code works on xp, vista 32 and win7 64.
void HideDLL() {
if (Application->Handle == 0) {
SearchObject *so = new SearchObject();
so->Caption = L"MyCallingApp";
so->Handle = 0;
EnumWindows((WNDENUMPROC)EnumWindowsProc, (long)so);
Application->Handle = so->Handle;
delete so;
}
}
BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lparam) {
bool result;
SearchObject *so = (SearchObject*)lparam;
wchar_t *caption = new wchar_t[STR_DEFAULT];
GetWindowTextW(hWnd, caption, STR_DEFAULT);
// String is an Embarcadero type representing UnicodeString
String Caption = caption;
// Pos is a wrapper around strstr I think
// the actual caption in my case is decorated with some other stuff
// I only know that it will start with the name of the app
if (Caption.Pos(so->Caption) > 0) {
so->Handle = hWnd;
result = false;
} else {
result = true;
}
delete caption;
return result;
}
Hope this helps.
It seems that (WPARAM)sizeof(CHAR)/sizeof(MAX_PATH) would return you zero, because sizeof(char) will be defenetly smaller then the max path, so you say to WinAPI that your variable has zero length, that's why it returns empty string to you.
Specify there MAX_PATH value instead.
Related
I am launching an windows desktop application by
CATStartProcess (const char *comPath,
char *const argv[],
int wait, int *pid,
int *exitStatus);
The arguments are passed to it.
If the application is already running I don't need to create a new instance for this. How can I check if this application is already running in background or not?
int wait = 0;
int pid;
int exitStatus;
char *CommandArgs[9] = { 0 };
CommandArgs[0] = (char*)usComposerExePath.ConvertToChar();
CommandArgs[1] = (char*)usOpen.ConvertToChar();
CommandArgs[2] = (char*)usComposerProjectDocPath.ConvertToChar();
CommandArgs[3] = (char*)strInfiniteTicket.ConvertToChar();
CommandArgs[4] = (char*)strDocName.ConvertToChar();
CommandArgs[5] = (char*)strSecurityContext.ConvertToChar();
CommandArgs[6] = (char*)usBusID.ConvertToChar();
CommandArgs[7] = (char*)usUserID.ConvertToChar();
CommandArgs[8] = NULL;
CATLibStatus startComposerBatchStatus = CATStartProcess((char*)usComposerExePath.ConvertToChar(), CommandArgs, wait, &pid, &exitStatus);
There's a few ways, but I'll admit, neither of my two solutions are portable/standard C++, but you tagged Windows, so I'll give a Windows method.
The below code actually performs both checks. The first method is to use a named mutex. Windows has a "Global" mutex, which checks for running processes by any user. If the mutex already exists, then its running. If it doesn't exist, then it's not running. There's some states where things can't be easily determined, so it checks the running process list. This part is less accurate, since different permissions affects the list.
The part with mutexes will only work if you can modify the application you are trying to launch so that it creates a mutex.
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <psapi.h>
#include <TlHelp32.h>
#include <shellapi.h>
#include <advpub.h>
enum class ProcessRunningState {
YES,
NO,
ERR
};
ProcessRunningState CheckIfProcessIsAlreadyRunning( DWORD currentProcessId, const wchar_t *programName, const wchar_t *programGUID, HANDLE *mutex_handle ) noexcept {
{ // Check the mutexes first
wchar_t global_prog_name[1024] = L"Global\\";
wcsncat_s( global_prog_name, programName, wcslen( programGUID ) );
if ( mutex_handle ) {
*mutex_handle = CreateMutex( NULL, TRUE, global_prog_name );
if ( !( *mutex_handle ) ) {
const DWORD dw = GetLastError();
if ( dw == ERROR_ALREADY_EXISTS )
return ProcessRunningState::YES;
} else {
return ProcessRunningState::NO;
}
} else {
HANDLE h = OpenMutex( SYNCHRONIZE, FALSE, global_prog_name );
if ( h ) {
CloseHandle( h );
return ProcessRunningState::YES;
} else if ( GetLastError() == ERROR_FILE_NOT_FOUND ) {
return ProcessRunningState::NO;
}
}
}
{ // At this point, the state is unknown, so try running through the process list
DWORD aProcesses[2048], cProcesses;
if ( !EnumProcesses( aProcesses, sizeof( aProcesses ), &cProcesses ) ) {
return ProcessRunningState::ERR;
}
// Calculate how many process identifiers were returned.
cProcesses = cProcesses / sizeof( DWORD );
for ( unsigned int i = 0; i < cProcesses; i++ ) {
if ( aProcesses[i] != 0 && aProcesses[i] != currentProcessId ) {
HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, aProcesses[i] );
WCHAR szProcessName[MAX_PATH] = { 0 };
if ( hProcess ) {
HMODULE hMod;
DWORD cbNeeded;
if ( EnumProcessModules( hProcess, &hMod, sizeof( hMod ), &cbNeeded ) ) {
GetModuleBaseName( hProcess, hMod, szProcessName, sizeof( szProcessName ) / sizeof( TCHAR ) ); // Can't error here, since this function "errors" on access
}/* else {
return ProcessRunningState::ERR;
}*/
CloseHandle( hProcess );
}
if ( _wcsicmp( szProcessName, programName ) == 0 ) {
return ProcessRunningState::YES;
}
}
}
}
return ProcessRunningState::NO;
}
Calling it like so will create the mutex if possible, and basically says that "I want to run, can I?"
HANDLE mutex_handle;
const ProcessRunningState cur_state = CheckIfProcessIsAlreadyRunning( GetCurrentProcessId(), L"PROGRAM_NAME", programGUID, &mutex_handle );
switch ( cur_state ) {
case ProcessRunningState::ERR:
case ProcessRunningState::YES:
return ERROR_ALREADY_EXISTS;
default:
break;
}
Calling it like so, simply checks if its already running, and launches the application if not.
if ( CheckIfProcessIsAlreadyRunning( GetCurrentProcessId(), L"PROGRAM_NAME", programGUID, nullptr ) == ProcessRunningState::NO ) {
std::wstring programInstallLocation = L"path";
std::wstring programName = programInstallLocation + L"\\PROGRAM_NAME";
ShellExecute( NULL, L"open", programName.c_str(), NULL, NULL, SW_SHOW );
}
And somewhere in your code, you would specify what programGUID is. For example:
WCHAR programGUID[] = L"ba2e95a0-9168-4b6e-bcd6-57309748df21";
edit: how can i get windows title using processname? for example get current title of chrome.exe
You can get title of specific windows using it's process ID.
If you know the name of executed file(ex: Chrome.exe), you can get Handle with FindWindowEX() or get PID "Chrome.exe" with CreateToolHelp32Snapshot.
Then use EnumWindows to get HWND using HANDLE.
struct param_enum
{
unsigned long ulPID;
HWND hWnd_out;
};
HWND find_specific_window(unsigned long process_id)
{
param_enum param_data;
param_data.ulPID = process_id;
param_data.hWnd_out = 0;
EnumWindows(enum_windows_callback, (LPARAM)¶m_data);
get_window_title(process_id, param_data.hWnd_out);
return param_data.hWnd_out;
}
BOOL CALLBACK enum_windows_callback(HWND handle, LPARAM lParam)
{
param_enum& param_data = *(param_enum*)lParam;
unsigned long process_id = 0;
GetWindowThreadProcessId(handle, &process_id);
if (param_data.ulPID != process_id)
{
return TRUE;
}
param_data.hWnd_out = handle;
return FALSE;
}
---------------------------Get Handle---------------------------
HANDLE GetHandleFromProcessPath(TCHAR* szExeName, DWORD& dwPID)
{
HANDLE hExeName = INVALID_HANDLE_VALUE;
HANDLE hSnap = INVALID_HANDLE_VALUE;
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE != hSnap)
{
if (Process32First(hSnap, &pe32))
{
do
{
//!!! Attention pe32.szExeFile always return exe file name. not window title.
if (NULL != _tcsstr(pe32.szExeFile, szExeName))
{
hExeName = OpenProcess(PROCESS_ALL_ACCESS, TRUE, pe32.th32ProcessID);
dwPID = pe32.th32ProcessID;
break;
}
} while (Process32Next(hSnap, &pe32));
}
}
return hExeName;
}
Completing the answer of "G.Alexander" and and the comment of Skewjo
the get_window_title code is incomplete. So, worked for me, by removing it and calling find_specific_window like below:
wchar_t* caption = new wchar_t[MAX_PATH*2];
HWND h = find_specific_window(processID);
GetWindowTextW(h, caption, MAX_PATH*2);
I would need to get all elements of webpage displayed in IE from a c++ program.
I tried to see with spy++ but there's only the IEFrame.
So I'm thinking about using the developement tool (F12 in IE), I heard there's a way to automat it, a good idea ?
Thanks
You can get an IHtmlDocument2 reference from an IE's window handle, even out-of-process. This is documented here https://support.microsoft.com/en-us/help/249232/how-to-get-ihtmldocument2-from-a-hwnd, but not really supported by Microsoft.
However it looks like it still works today, I've tested it with a Windows 10 box, and IE is now a frozen app, so not going to change any time soon.
Once you have the proper HWND for Internet Explorer, than you can get the DOM with a code like this. Make sure IE and your program run at the same security level
The DOM is the same as when you're coding IE inprocess (host, activex, etc.), however for security reasons, some things may not work :
void DoSomeDomOperations(HWND hwnd)
{
UINT msg = RegisterWindowMessage(L"WM_HTML_GETOBJECT");
LRESULT result = 0;
SendMessageTimeout(hwnd, msg, NULL, NULL, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR)&result);
if (!result)
return;
// get main document object
IHTMLDocument2 *doc = NULL;
ObjectFromLresult(result, IID_IHTMLDocument2, NULL, (void**)&doc);
if (!doc)
return;
// get document's url
BSTR url = NULL;
doc->get_URL(&url);
wprintf(L"url:%s\n", url);
SysFreeString(url);
// get body element
IHTMLElement *element = NULL;
doc->get_body(&element);
if (element)
{
BSTR text = NULL;
element->get_innerText(&text);
wprintf(L"text:%s\n", text);
SysFreeString(text);
element->Release();
}
// etc.
// etc.
doc->Release();
}
And here is a full sample console app that scans all current IE processes running:
BOOL CALLBACK GetIEServerWindowProc(HWND hwnd, LPARAM lParam)
{
// enumerate all child windows to find IE's COM server
wchar_t className[100];
GetClassName(hwnd, className, 100);
if (!wcscmp(className, L"Internet Explorer_Server"))
{
*((HWND*)lParam) = hwnd;
return FALSE;
}
return TRUE;
}
HWND GetIEServerWindow(HWND hwnd)
{
HWND serverHwnd = NULL;
EnumChildWindows(hwnd, GetIEServerWindowProc, (LPARAM)&serverHwnd);
return serverHwnd;
}
struct IEServer
{
DWORD processId;
HWND serverHwnd;
};
BOOL CALLBACK GetIEProcessServerWindowProc(HWND hwnd, LPARAM lParam)
{
DWORD processId = ((IEServer*)lParam)->processId;
DWORD pid;
GetWindowThreadProcessId(hwnd, &pid);
if (pid == processId)
{
HWND serverHwnd = GetIEServerWindow(hwnd);
if (serverHwnd)
{
((IEServer*)lParam)->serverHwnd = serverHwnd;
return FALSE;
}
}
return TRUE;
}
HWND GetIEProcessServerWindow(DWORD processId)
{
IEServer ie = { processId, NULL };
EnumWindows(GetIEProcessServerWindowProc, (LPARAM)&ie);
return ie.serverHwnd;
}
void EnumerateIEProcesses()
{
HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (h == INVALID_HANDLE_VALUE)
return;
PROCESSENTRY32 process;
process.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(h, &process))
{
do
{
// we only consider IE processes
if (!wcscmp(process.szExeFile, L"iexplore.exe"))
{
HWND serverHwnd = GetIEProcessServerWindow(process.th32ProcessID);
if (serverHwnd)
{
DoSomeDomOperations(serverHwnd);
}
}
} while (Process32Next(h, &process));
}
CloseHandle(h);
}
int main()
{
CoInitialize(NULL);
EnumerateIEProcesses();
CoUninitialize();
return 0;
}
This problem occurs only on Windows 10. Works fine on other versions such as Windows 7.
On user action, I have following code to find out another open application window as:
void zcTarget::LocateSecondAppWindow( void )
{
ghwndAppWindow = NULL;
CString csQuickenTitleSearch = "MySecondApp";
::EnumDesktopWindows( hDesktop, MyCallback, (LPARAM)(LPCTSTR)csTitleSearch );
}
With callback functions as:
BOOL CALLBACK MyCallback( HWND hwnd, LPARAM lParam)
{
if ( ::GetWindowTextLength( hwnd ) == 0 )
{
return TRUE;
}
CString strText;
GetWindowText( hwnd, strText.GetBuffer( 256 ), 256 );
strText.ReleaseBuffer();
if ( strText.Find( (LPCTSTR)lParam ) == 0 )
{
// We found the desired app HWND, so save it off, and return FALSE to
// tell EnumDesktopWindows to stopping iterating desktop HWNDs.
ghwndAppWindow = hwnd;
return FALSE;
}
return TRUE;
} // This is the line after which call is not returned for about 30 mins
This callback function mentioned above gets called for about 7 times, each time returning True. At this stage it finds own app window through which EnumDesktopWindows was invoked.
It returns True as expected, but then nothing happens for about 30 minutes. No debug points hit. The original running application is unresponsive at this point.
How to resolve this problem?
Found another path. Instead of going by Window name, looking for Process helps. Get process using process name, extract process id and get window handle.
void zcTarget::LocateSecondAppWindow( void )
{
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (Process32First(snapshot, &entry) == TRUE)
{
while (Process32Next(snapshot, &entry) == TRUE)
{
if (_stricmp(entry.szExeFile, "myApp.exe") == 0)
{
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID);
EnumData ed = { GetProcessId( hProcess ) };
if ( !EnumWindows( EnumProc, (LPARAM)&ed ) &&
( GetLastError() == ERROR_SUCCESS ) ) {
ghwndQuickenWindow = ed.hWnd;
}
CloseHandle(hProcess);
break;
}
}
}
CloseHandle(snapshot);
}
BOOL CALLBACK EnumProc( HWND hWnd, LPARAM lParam ) {
// Retrieve storage location for communication data
zcmTaxLinkProTarget::EnumData& ed = *(zcmTaxLinkProTarget::EnumData*)lParam;
DWORD dwProcessId = 0x0;
// Query process ID for hWnd
GetWindowThreadProcessId( hWnd, &dwProcessId );
// Apply filter - if you want to implement additional restrictions,
// this is the place to do so.
if ( ed.dwProcessId == dwProcessId ) {
// Found a window matching the process ID
ed.hWnd = hWnd;
// Report success
SetLastError( ERROR_SUCCESS );
// Stop enumeration
return FALSE;
}
// Continue enumeration
return TRUE;
}
I've written two COM classes in C++, contained in a single MFC DLL. They're being loaded as plugins by a 3rd party application.
How can I get the file name, and version number, of the DLL from within those classes?
The main dll entry gives you the handle of your dll.
extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
and
GetModuleFileName(hInstance, buffer, MAX_PATH);
can be used to get the filename of the dll.
GetFileVersionInfoSize
GetFileVersionInfo
will be used to get the file version.
TCHAR fileName[MAX_PATH + 1];
GetModuleFileName(hInstance, fileName, MAX_PATH);
Where hInstance is the one you get in the DllMain function. Don't use GetModuleHandle(0), because that returns the HINSTANCE of the host application.
CString GetCallingFilename(bool includePath)
{
CString filename;
GetModuleFileName(AfxGetInstanceHandle(), filename.GetBuffer(MAX_PATH), MAX_PATH);
filename.ReleaseBuffer();
if( !includePath )
{
int filenameStart = filename.ReverseFind('\\') + 1;
if( filenameStart > 0 )
{
filename = filename.Mid(filenameStart);
}
}
return filename;
}
CString GetCallingVersionNumber(const CString& filename)
{
DWORD fileHandle, fileVersionInfoSize;
UINT bufferLength;
LPTSTR lpData;
VS_FIXEDFILEINFO *pFileInfo;
fileVersionInfoSize = GetFileVersionInfoSize(filename, &fileHandle);
if( !fileVersionInfoSize )
{
return "";
}
lpData = new TCHAR[fileVersionInfoSize];
if( !lpData )
{
return "";
}
if( !GetFileVersionInfo(filename, fileHandle, fileVersionInfoSize, lpData) )
{
delete [] lpData;
return "";
}
if( VerQueryValue(lpData, "\\", (LPVOID*)&pFileInfo, (PUINT)&bufferLength) )
{
WORD majorVersion = HIWORD(pFileInfo->dwFileVersionMS);
WORD minorVersion = LOWORD(pFileInfo->dwFileVersionMS);
WORD buildNumber = HIWORD(pFileInfo->dwFileVersionLS);
WORD revisionNumber = LOWORD(pFileInfo->dwFileVersionLS);
CString fileVersion;
fileVersion.Format("%d.%d.%d.%d", majorVersion, minorVersion, buildNumber, revisionNumber);
delete [] lpData;
return fileVersion;
}
delete [] lpData;
return "";
}