Which function to use to detect if menu is present in the app? ...
Menu is present in a window.
Have a look at the GetMenu API or /and CWnd::GetMenu method in MSDN
If only the main window contains a menu - you could use AfxGetMainWnd()->GetMenu();
Originally Posted by crazy boy
... It's nonsense to recompile every time I change app. You also could help me if you would approve the code to support more titles. I am debugging 4 apps as target window: Learning WinAPI , Dialog App, AOK Trigger Studio and Map Viewer....
OK, So I paste here the code to detect window from 2kaud. However it looks like freezing. On attempt to close the console window manually (mouse click on close button) it will generate access error, some kind of violent access to memory.
This is shortened code. I want first to solve the Window detection and then I will continue to other codes. I meant if it were possible to detect one of those windows. I use at least 4 different application (titles of windows). Once I change destination app I need to recompile code. So if it were possible to simplify the job yet a bit more.
Code:
#define CODE 81
#include <windows.h>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <tchar.h>
#include <conio.h>
using namespace std;
const int maxstr = 200;
const char wndtitle[] = "AOK";
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lparam)
{
char title[maxstr + 1];
char *ch = title;
if (GetWindowTextA(hwnd, title, maxstr)) {
while (*ch = (char)tolower(*(ch++)));
// strstr - finds the first occurrence of a substring within a string. The comparison is case-sensitive.
if (strstr(title, wndtitle)) {
*(HWND*)lparam = hwnd;
SetLastError(ERROR_NO_MATCH); // Sets the last-error code for the calling thread.
return FALSE;
}
}
return TRUE;
}
int main()
{
HWND HCapture;
while (EnumWindows(EnumWindowsProc, (LPARAM)&HCapture));
if (GetLastError() != ERROR_NO_MATCH) {
cout << "Cannot find window\n";getch();return 1;
}
if (!IsWindow(HCapture)) {
cout << "Bad find! Cannot find window\n";getch();return 1;
}
// GetSystemMetrics(SM_CYMENU) +
#if CODE==81
const int fudgey = (GetMenu(HCapture)?GetSystemMetrics(SM_CYMENU):0)
+ GetSystemMetrics(SM_CYMENU) + GetSystemMetrics(SM_CYSIZE) + GetSystemMetrics(SM_CYSIZEFRAME);
const int fudgex = 0;
#endif
RECT rect;
if (CODE == 81){
GetClientRect(HCapture, &rect);
}
LONG dx = rect.right - rect.left;
LONG dy = rect.bottom - rect.top;
}
All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!
I've attached the zip of my test.exe version of the program (32bit) which works fine on several systems.
testcomp.zip added created using compressed folder in xp.
Last edited by 2kaud; May 14th, 2014 at 08:03 AM.
Reason: Added textcomp.zip
All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!
Arjay: There are maybe two bugs, even that 23kaud had tested it on his system and even XP SP3 without problems; #1 on my system XP SP3 the loop caused infinite loop. If I comment the loop it continues to debug successfully #2 on the end of enumerate callback function. This seems odd. It should be returned TRUE, but it did not returned even the debugger stepped on the return line.
Ad #1 - bug removed with for loop:
Code:
for (char *ch = title; *ch; ++ch) // update: ch++ to ++ch
*ch = (char)tolower(*ch);
Ad #2 - on stepping out of the callback - message is appeared:
No source available. Call stack location user32.dll!7e37a4e8()
Do you think the while loop above has anything to do with the freezing (or actually using 100% of the cpu)?
Yeah, that was changed on a later post #108.
I've also changed the case conversion code as OP had an issue with this in a PM. The code below has been compiled on 3 different version of MSVS and tested on several computers with no issues.
All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!
To make it clear. I don't say, that the window is not found, but that I cannot debug it because it will get error "no source available". My question here is: do you experience same problem? Is this normal? When you add break on the end of callback EnumWindowsProc(){ } do you got error?
Last edited by crazy boy; May 14th, 2014 at 06:30 AM.
To make it clear. I don't say, that the window is not found, but that I cannot debug it because it will get error "no source available". My question here is: do you experience same problem? Is this normal? When you add break on the end of callback EnumWindowsProc(){ } do you got error?
No, I have no problems. I can step though the code fine in the debugger and set break points etc which are triggered as expected. If you get errors whilst trying to debug it looks like you either haven't set the configuration to debug build or there is some issue with the build debug properties. Note that the file in post #109 has no debug info included with the .exe.
All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!
To make it clear. I don't say, that the window is not found, but that I cannot debug it because it will get error "no source available". My question here is: do you experience same problem? Is this normal?
Yes, it is normal. It is NOT an error. It only means that source code to the function form user32.dll that calls your callback is not available, so you can step in only in the assembly.
Yes, it is normal. It is NOT an error. It only means that source code to the function form user32.dll that calls your callback is not available, so you can step in only in the assembly.
Thanks for the info. But how I debug it if I would need to proceed the loop of existing windows? For example if I type I want to get handle of AOK window , but there is more windows and the AOK would not be the first window, but the third. So when I am in the callback, first handle is not what I search, but I cannot continue to the third call because it will stop on the end of first call.
Update #1:
I have made improvement to the callback function. It will skip windows which are not top level on platforms prior to Windows 8.
You can set minimum and maximum height of destination window.
You can set your own style depending on your theme.
These conditions saves processing of many program titles into for loop in versions prior Windows 8. In my case saved about 50 programs (but some of theme have no title, so may be irrelevant).
Code:
#define CODE 81
#define WINDOW_MIN_HEIGHT 200
#define WINDOW_MAX_HEIGHT 700
#define WINSTYLE 0x14FF0000
#include <windows.h>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <cstring>
#include <string>
using namespace std;
const int maxstr = 200;
// const char wndtitle[] = "AOK";
LONG t1; LONG t2;
struct windata {
string partitle;
HWND hfnd; // Found destination window handler
};
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lparam)
{
char title[maxstr + 1];
char *ch = title;
RECT wrect;
GetWindowRect(hwnd, &wrect);
int winHeight = wrect.bottom-wrect.top;
if ( (WINDOW_MIN_HEIGHT && winHeight<WINDOW_MIN_HEIGHT) ||
(WINDOW_MAX_HEIGHT && winHeight>WINDOW_MAX_HEIGHT))
return TRUE; // skip window
if ( GetWindowLong(hwnd, GWL_STYLE) & WINSTYLE != TRUE )
return TRUE;
if (GetWindowTextA(hwnd, title, maxstr)) {
for (char *ch = title; *ch; ++ch) // was ch++)
*ch = (char)tolower(*ch);
if (strstr(title, ((windata*)lparam)->partitle.c_str())) {
((windata*)lparam)->hfnd = hwnd;
SetLastError(ERROR_NO_MATCH);
cout << "Found matching window - " << title << endl;
return FALSE;
}
}
return TRUE;
} // Press F5 to continue debug, not F10. Otherwise "error" occurs.
int main()
{
windata wd;
cout << "Partial window title: ";
getline(cin, wd.partitle);
// ... rest of the code here
}
Notice that the struct does not contain integers initiation, but any string is assigned outside the struct class.
Notice two difference operators are used to access the wd members of instance of struct/class windata.
The difference in pointers is nicely described here
dot operator: In the first form, postfix-expression represents a value of struct, class, or union type, and name names a member of the specified structure, union, or class. The value of the operation is that of name and is an l-value if postfix-expression is an l-value. arrow operator: In the second form, postfix-expression represents a pointer to a structure, union, or class, and name names a member of the specified structure, union, or class. The value is that of name and is an l-value. The –> operator dereferences the pointer. Therefore, the expressions e–>member and (*e).member (where e represents a pointer) yield identical results (except when the operators –> or * are overloaded).
Update #2:
Here I add updated code. This will find one of four windows, starting on "learn", "dialog", "aok" or "map". Type lower case characters into window titles definition. If you open more than one window with the specified name, you can select what window should be selected otherwise you can press enter and default window will be selected.
Code:
#define CODE 81
#define WINDOW_MIN_HEIGHT 200
#define WINDOW_MAX_HEIGHT 700
#define WINSTYLE 0x14FF0000
#include <windows.h>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <cstring>
#include <string>
#include <tchar.h>
#include <conio.h>
using namespace std;
const int maxstr = 200;
LONG t1; LONG t2;
struct windata {
static const string titles[]; // only static const integral data members can be initialized within a class
static const string default;
string choose;
HWND hfnd; // Found destination window handler
};
const string windata::titles[] = {"learn", "dialog", "aok", "map" };
const string windata::default = "learn";
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lparam)
{
char title[maxstr + 1];
char *ch = title;
RECT wrect;
GetWindowTextA(hwnd, title, maxstr);
GetWindowRect(hwnd, &wrect); // Calculate window height
int winHeight = wrect.bottom-wrect.top;
if ( (WINDOW_MIN_HEIGHT && winHeight<WINDOW_MIN_HEIGHT) ||
(WINDOW_MAX_HEIGHT && winHeight>WINDOW_MAX_HEIGHT))
return TRUE; // skip window
if ( GetWindowLong(hwnd, GWL_STYLE) & WINSTYLE != TRUE )
return TRUE;
if (GetWindowTextA(hwnd, title, maxstr)) {
for (char *ch = title; *ch; ++ch) // was ch++)
*ch = (char)tolower(*ch);
if (strstr(title, ((windata*)lparam)->choose.c_str())) {
((windata*)lparam)->hfnd = hwnd;
SetLastError(ERROR_NO_MATCH);
cout << "Found matching window - " << title << endl;
return FALSE;
}
}
return TRUE;
} // Press F5 to continue debug, not F10. Otherwise "error" occurs.
int main()
{
windata wd;
cout << "Partial window title: ";
getline(cin, wd.choose);
wd.choose.erase(wd.choose.find_last_not_of(" \n\r\t")+1); // trim string
if ( wd.choose == "" ) // select default window
wd.choose = wd.default;
if (EnumWindows(EnumWindowsProc, (LPARAM)&wd) || (GetLastError() != ERROR_NO_MATCH)) {
cout << "Cannot find window\n";
return 1;
}
HWND HCapture = wd.hfnd;
if (!IsWindow(HCapture)) {
cout << "Bad find! Cannot find window\n";_getch();return 2;
}
Yes it was originally, but see my post #82 and #85. I moved away from any changes to the program producing the window to be captured. The code in #96 doesn't require any changes to the window producing program. The other program was simply a test program to produce a window containg an eclipse that could be captured This had a title of wintest - hence the use of wintest in the FindWindow().
// CODE 81 and 82 de facto no difference
#define CODE 85 // 81
#define WINDOW_MIN_HEIGHT 200
#define WINDOW_MAX_HEIGHT 700
#define WINSTYLE 0x14FF0000
// #define IS_CHILD TRUE // [TRUE | FALSE | 0] ... 0 - deactivates this detection
// GetWindowLong: GWL_HWNDPARENT
#include <windows.h>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <cstring>
#include <string>
#include <tchar.h>
#include <conio.h>
using namespace std;
const int maxstr = 200;
// const char wndtitle[] = "AOK";
LONG t1; LONG t2;
RECT rect, wrect;
struct windata {
static const string titles[]; // only static const integral data members can be initialized within a class
static const string default;
string choose;
HWND hfnd; // Found destination window handler
};
const string windata::titles[] = {"learn", "dialog", "aok", "map" };
const string windata::default = "learn"; // windata.default = ... will produce syntax error : missing ';' before '.'
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lparam)
{
char title[maxstr + 1];
char *ch = title;
GetWindowRect(hwnd, &wrect); // Calculate window height
int winHeight = wrect.bottom-wrect.top;
if ( (WINDOW_MIN_HEIGHT && winHeight<WINDOW_MIN_HEIGHT) ||
(WINDOW_MAX_HEIGHT && winHeight>WINDOW_MAX_HEIGHT))
return TRUE; // skip window
if ( GetWindowLong(hwnd, GWL_STYLE) & WINSTYLE != TRUE )
return TRUE;
/* GetWindowTextA - copies the text of the specified window's
title bar (if it has one) into a buffer. If the specified window is a
control, the text of the control is copied. However, it
can't retrieve the text of a control in another application. */
if (GetWindowTextA(hwnd, title, maxstr)) {
for (char *ch = title; *ch; ++ch) // was ch++)
*ch = (char)tolower(*ch);
// strstr - finds the first occurrence of a substring within a string. The comparison is case-sensitive.
// c_str - converts the contents of a string as a C-style, null-terminated string.
if (strstr(title, ((windata*)lparam)->choose.c_str())) {
((windata*)lparam)->hfnd = hwnd;
SetLastError(ERROR_NO_MATCH);
cout << "Found matching window - " << title << endl;
return FALSE;
}
}
return TRUE;
} // Press F5 to continue debug, not F10. Otherwise "error" occurs.
int main()
{
// HWND HCapture = GetForegroundWindow();
/* HWND HCapture = FindWindow(NULL, _T("Learning WInAPI")); // get window handle
*/
windata wd;
cout << "Partial window title: ";
getline(cin, wd.choose);
wd.choose.erase(wd.choose.find_last_not_of(" \n\r\t")+1); // trim string
if ( wd.choose == "" ) // select default window
wd.choose = wd.default;
/* EnumWindows(in,in) - Enumerates all top-level windows on the
screen by passing the handle to each window, in turn, to
callback function. EnumWindows continues until the last top-level
window is enumerated or the callback function returns FALSE.
The EnumWindows function does not enumerate child windows.
*/
if (EnumWindows(EnumWindowsProc, (LPARAM)&wd) || (GetLastError() != ERROR_NO_MATCH)) {
cout << "Cannot find window\n";
return 1;
}
HWND HCapture = wd.hfnd;
if (!IsWindow(HCapture)) {
cout << "Bad find! Cannot find window\n";_getch();return 2;
}
#if CODE<85
const int fudgey = (GetMenu(HCapture)?GetSystemMetrics(SM_CYMENU):0)
+ GetSystemMetrics(SM_CYSIZE) + GetSystemMetrics(SM_CYSIZEFRAME);
const int fudgex = 0;
#endif
GetClientRect(HCapture, &rect);
LONG dx = rect.right - rect.left;
LONG dy = rect.bottom - rect.top;
if (CODE>82) {
/*
const int fudgey = (GetMenu(HCapture)?GetSystemMetrics(SM_CYMENU):0)
+ GetSystemMetrics(SM_CYSIZE) + GetSystemMetrics(SM_CYSIZEFRAME);
*/
const int fudgey = (rectw.bottom - rectw.top) - dy - 2 * GetSystemMetrics(SM_CYSIZEFRAME);
const int fudgex = 0;
}
// create BITMAPINFO structure
// used by CreateDIBSection
BITMAPINFO info = {0};
info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
info.bmiHeader.biWidth = dx + fudgex;
info.bmiHeader.biHeight = dy + fudgey;
info.bmiHeader.biPlanes = 1;
info.bmiHeader.biBitCount = 24;
info.bmiHeader.biCompression = BI_RGB;
HDC HDevice = CreateCompatibleDC(NULL);
//HDC HDevice = GetDC(HCapture);
BYTE* memory = 0;
HBITMAP HBitmap = CreateDIBSection(HDevice, &info, DIB_RGB_COLORS, (void**)&memory, NULL, NULL);
SelectObject(HDevice, HBitmap);
SendMessage(HCapture, WM_PRINTCLIENT, 0, 0);
// SendMessage(HCapture, WM_PRINTCLIENT,(WPARAM) HDevice, 0);
if (!PrintWindow(HCapture, HDevice, /*PW_CLIENTONLY*/0))
return 2;
SendMessage(HCapture, WM_PRINTCLIENT, 0, 0);
ReleaseDC(HCapture, HDevice);
char *buffer = new char[50];
sprintf(buffer, "capture%d%d.bmp", dx, dy);
ofstream file(buffer, ios::binary);
if (!file) {
DeleteObject(HBitmap);
return 1;
}
// initialize bitmap file headers
BITMAPFILEHEADER fileHeader = {0};
BITMAPINFOHEADER infoHeader = {0};
fileHeader.bfType = 0x4d42;
fileHeader.bfSize = 0; //????
fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
infoHeader.biSize = sizeof(infoHeader);
infoHeader.biWidth = dx + fudgex;
infoHeader.biHeight = dy;
infoHeader.biPlanes = 1;
infoHeader.biBitCount = 24;
infoHeader.biCompression = BI_RGB;
// save file headers
file.write((char*)&fileHeader, sizeof(fileHeader));
file.write((char*)&infoHeader, sizeof(infoHeader));
// save 24-bit bitmap data
int wbytes = (((24 * (dx) + 31) & (~31)) / 8);
int tbytes = wbytes * dy;
file.write((char*)(memory), tbytes);
file.close();
// delete bitmap
DeleteObject(HBitmap);
return 0;
}
If I remove all from if (CODE>82) till the end except ending curly bracket it succeeds to compile otherwise
'&' : check operator precedence for possible error; use parentheses to clarify precedence
...
'&' : check operator precedence for possible error; use parentheses to clarify precedence
Something with rect, rectw declaration?
It's telling you what the error is:
"'&' : check operator precedence for possible error; use parentheses to clarify precedence"
It's always tough to interpret the compiler errors at first. When I run across an compiler error I haven't seen before, I find it helpful to paste the error string into a bing or google search.
You might consider adapting a more readable coding style that uses a bit of white space and better formatting. That way, these kinds of errors will jump right out at you.
Another thing to do is if coding in Visual Studio, just double-click on the error and it usually will take you to the offending line of code.
* The Best Reasons to Target Windows 8
Learn some of the best reasons why you should seriously consider bringing your Android mobile development expertise to bear on the Windows 8 platform.