How to check if a button is checkbox on 64 bit windows? - winapi

I'm checking if a button is checkbox of 32bit process on 64bit windows10.
The problem is that I can not distingush checkbox from normal button.
The buttons are different in Window-Detective:
(After I restart the application, even Window-Detective shows it is a button now!)
But the checkbox can't be recognized as checkbox in Spy++
BS_CHECKBOX is not listed.
Code (compiled as 32bit):
TEST_METHOD(ShouldCheckStyle) {
auto styleOfButton = ::GetWindowLongPtr((HWND)0x003F06E8, GWL_STYLE);
auto styleOfCheckbox = ::GetWindowLongPtr((HWND)0x01101642, GWL_STYLE);
auto bsOfButton = styleOfButton & BS_TYPEMASK;
auto bsOfCheckbox = styleOfCheckbox & BS_TYPEMASK;
auto resultOfButton = (bsOfButton == BS_CHECKBOX);
auto resultOfCheckbox = (bsOfCheckbox == BS_CHECKBOX);
auto debugger = 0;
}
Debug output
The code indicates they both have BS_OWNERDRAW. The above behaves the same for the button and the checkbox.
The weird thing is Window-Detective can recognize the style of checkbox. The code is same as I used above. Here's a piece of code:
Window* WindowManager::createWindow(HWND handle) {
WindowClass* windowClass = getWindowClassFor(handle);
String className = windowClass->getName().toLower();
if (className == "button") {
LONG typeStyle = GetWindowLong(handle, GWL_STYLE) & BS_TYPEMASK;
switch (typeStyle) {
case BS_CHECKBOX:
case BS_AUTOCHECKBOX:
case BS_3STATE:
case BS_AUTO3STATE: {
return new CheckBox(handle, windowClass);
}
case BS_RADIOBUTTON:
case BS_AUTORADIOBUTTON: {
return new RadioButton(handle, windowClass);
}
case BS_GROUPBOX: {
return new GroupBox(handle, windowClass);
}
default: {
// If none of the above is true, then the control is just a Button
return new Button(handle, windowClass);
}
}
}

After some discussion, you can use GetWindowText to get the text from each control and compare the specific text.
BS_CHECKBOX cannot be detected from the properties of the "checkbox" control beacuse of BS_OWNERDRAW.
Creates an owner-drawn button. The owner window receives a WM_DRAWITEM
message when a visual aspect of the button has changed. Do not combine
the BS_OWNERDRAW style with any other button styles.
Try the below code:
WCHAR str1[20];
WCHAR str2[] = L"Agree me";
GetWindowText(hwnd_checkbox, str1, 256);
if (_tcscmp(str1, str2) == 0)
{
//it is checkbox
}
else
{
//it isn't checkbox
}
After you get the correct control handle of the checkbox, you can use SendDlgItemMessage or SendMessage to send BM_SETCHECK check message.
SendMessage(hwnd_checkbox, BM_SETCHECK, BST_CHECKED, 0);

Related

Minimize window to system tray

I have created a SDL2 application, and would like it to minimize to the system tray, rather than appearing in the task bar.
SDL_MinimizeWindow doesn't do what I want, it leaves the task bar icon. Is there a way to achieve this with SDL?
There is no purely SDL2 way to do this, as Cody said, Shell_NotifyIcon is the function needed to create a notification area (system tray) icon.
The code I used to get the icon is
SDL_Window *window = SDL_CreateWindow("Test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 200, 200, SDL_WINDOW_HIDDEN);
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
NOTIFYICONDATA icon;
if (SDL_GetWindowWMInfo(window, &info))
{
icon.uCallbackMessage = WM_USER + 1;
icon.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
icon.hIcon = LoadIcon(NULL, IDI_INFORMATION);
icon.cbSize = sizeof(icon);
icon.hWnd = info.info.win.window;
strcpy_s(icon.szTip, "Test tip");
bool success = Shell_NotifyIcon(NIM_ADD, &icon);
}
This creates a hidden window, and an icon (using the default information icon).
To interact with this from SDL, you need to enable platform specific window management events, this is done as follows SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
After this, in the main event loop you must test for SDL_SYSWMEVENT, which contains information about how the user has interacted with the notification area icon. This also looks for the minimize event and hides the window which removes it from the task bar. This is achieved in the following snippet
SDL_Event e;
while (SDL_PollEvent(&e) != 0)
{
switch (e.type)
{
case SDL_SYSWMEVENT:
if (e.syswm.msg->msg.win.msg == WM_USER + 1)
{
if (LOWORD(e.syswm.msg->msg.win.lParam) == WM_LBUTTONDBLCLK)
{
SDL_ShowWindow(window);
SDL_RestoreWindow(window);
}
}
break;
case SDL_QUIT:
running = false;
break;
case SDL_WINDOWEVENT:
if (e.window.event == SDL_WINDOWEVENT_MINIMIZED)
SDL_HideWindow(window);
break;
}
}

Add custom action to System Menu in a QDialog

I have a need to add a custom action (say ‘About’ clicking which a QMessageBox needs to be displayed) in the system menu shown when the icon on the title bar of a QDialog is clicked. How do I achieve this?
Regards,
Bharath
You cannot do it with Qt because it's OS specific. But you can use GetSystemMenu and AppendMenu functions in Windows to modify the menu and then catch events that then item is clicked.
Here is a simple example from here. It appends a separator and an about item to the menu:
#include "windows.h"
// IDM_ABOUTBOX must be in the system command range
// (IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX)
// and (IDM_ABOUTBOX < 0xF000)
#define IDM_ABOUTBOX 0x0010
MyWidget::MyWidget() : QMainWindow()
{
...
HMENU hMenu = ::GetSystemMenu(winId(), FALSE);
if (hMenu != NULL)
{
::AppendMenuA(hMenu, MF_SEPARATOR, 0, 0);
::AppendMenuA(hMenu, MF_STRING, IDM_ABOUTBOX, "About MyApp...");
}
...
}
bool MyWidget::winEvent(MSG *m, long *result)
{
if (m->message == WM_SYSCOMMAND)
{
if ((m->wParam & 0xfff0) == IDM_ABOUTBOX)
{
*result = 0;
// open About dialog
about();
return (true);
}
}
return (false);
}
PRO-file:
LIBS += -lUser32

Two dialogs in one MFC application

I am trying to make a dialog based MFC application , where two dialogs needs to be shown sequentially.
What that means is , once the first dialog(modal) is shown and dismissed (by pressing OK), the second dialog needs to be brought up.My requirement is the second dialog should be modeless.
But what I observe is the second dialog is shown but none of the message handling function are being called in response of user messages.I think the message map itself is not working, while the overridden functions(like OnInitdialog) are being called. I tried replacing this modeless dialog with a modal one , and alas, the doModal() itself fails.
Here is the little code:
CFirstDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
// dismissed with OK
// TODO: Place code here to handle when the dialog is
// dismissed with Cancel
CSecondDlg *dlgModeLess = new CSecondDlg();
dlgModeLess->Create(CSecondDlg::IDD,NULL);
m_pMainWnd = dlgModeLess;
dlgModeLess->ShowWindow(SW_SHOW);
dlgModeLess->UpdateWindow();
}
Here is the message map of the second dialog:
BEGIN_MESSAGE_MAP(CSecondDlg, CDialog)
ON_MESSAGE(TRAY_MESSAGE,OnTrayMessage)
ON_BN_CLICKED(IDOK, &CSecongDlg::OnBnClickedOk)
ON_BN_CLICKED(IDC_RADIO1, &CSecondDlg::OnBnClickedRadio1)
END_MESSAGE_MAP()
I think I am doing something conceptually wrong. Kindly share your thoughts on what need to be done to tackle such a scenario.
As I mentioned in a previous post, it is not necessary that the second dialog be non modal.
Just do something like this:
BOOL CMyTestApp::InitInstance()
{
CMyTestDlg dlg;
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
COtherDlg Dlg ;
m_pMainWnd = &dlg;
if (Dlg.DoModal() == IDCANCEL)
{
...
}
}
else if (nResponse == IDCANCEL)
{
...
}
return FALSE;
}
When you create a modeless dialog box, control will return to your calling function right away, so you need to declare variable dlgModeLess at a global scope and make sure your program/scope is still active until the dialog box finishes
I have solved this problem and this turned out to be interesting.
It seems that Cdialog::Create() itself is not sufficient for creating a fully operative modeless dialog box. We have to supply a win32 style message loop to it.
So this effectively makes two message loops in the program, one provide my MFC framework and the one that I wrote after returning from IDOK. Here is the modified code.
CSecondDlg *dlgModeLess = new CSecondDlg();
dlgModeLess->Create(CSecondDlg::IDD,NULL);
CTrayIconDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
// dismissed with OK
// TODO: Place code here to handle when the dialog is
// dismissed with Cancel
MSG leftmsg;
PeekMessage(&leftmsg,m_pMainWnd->m_hWnd,0,0,PM_REMOVE);
MSG msg;
BOOL bRet;
while ((bRet = GetMessage(&msg, dlgModeLess->m_hWnd, 0, 0)) != 0)
{
if (bRet == -1)
{
// Handle the error and possibly exit
}
else if (!IsWindow(hWnd) || !IsDialogMessage(hWnd, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
This code works as expected, The interesting thing to note here is the PeekMessage function which removes a WM_QUIT message that is inserted in the thread's message queue once the first dialog is dismissed as we do not want to quite at that point of time. This i believe is done by the MFC framework.

How do I get MessageBox icon using WinAPI

I have a WH_CALLWNDPROC hook code which handles WM_INITDIALOG message to get information about message boxes. I could get "Message", "Title", "Buttons" but I couldnt get "icon" information. I'm trying to use a function like below:
long getIcon(HWND hwnd) { // handle of messagebox dialog
HWND hlbl = GetDlgItem(hwnd,20);
wcout << "LABEL HWND: " << hlbl << endl;
if (hlbl != NULL) {
LRESULT r = SendMessage(hlbl,WM_GETICON,0,0);
return (long)r;
}
return 0;
}
function always returns 0. I have checked by MS Spy++ and I saw that icon handle is 0.
What is the correct way to get icon?
The icon that is displayed on the message box dialog is implemented using a STATIC control with SS_ICON style. You can obtain the icon handle by sending that control the STM_GETICON message.
In the code in your question, the variable named hlbl is actually the window handle of the STATIC control that contains the icon. I'd name it hIconWnd. With that name change, the code to obtain the icon would look like this:
HICON getIcon(HWND hwnd) { // handle of messagebox dialog
HWND hIconWnd = GetDlgItem(hwnd, 20);
if (hIconWnd != NULL) {
return (HICON)SendMessage(hIconWnd, STM_GETICON, 0, 0);
}
return NULL;
}

how to make jqgrid advanced search dialog keyboard accessible

Answer in how to enable enter in jqgrid advanced search window describes how to enable enter and other keys in jqgrid advanced search dialog.
After clicking Add group, Add subgrup, Delete rule or Delete group button in advanced search dialog Enter and other keys are still ignored. How set focus to added element or after delete remaining element to enable Enter and other keys?
The current version of the Advanced Searching dialog (see definition of jqFilter in the grid.filter.js) recreate all controls of the dialog on change of someone. See the code of reDraw which looks
this.reDraw = function() {
$("table.group:first",this).remove();
var t = this.createTableForGroup(p.filter, null);
$(this).append(t);
if($.isFunction(this.p.afterRedraw) ) {
this.p.afterRedraw.call(this, this.p);
}
};
How one can see the first line $("table.group:first",this).remove(); delete the content of all filter. The current focus will be lost and one have the problems which you described.
I suggest to fix the code of reDraw using document.activeElement element which was introduced in Internet Explorer originally (at least in IE4) and which is supported now in all web browsers because it's part of HTML5 standard (see here). The element which has focus originally will be destroyed and one will unable to set focus on it later. So I suggest to save the element name of the element and it's classes (like input.add-group or input.add-rule.ui-add) and to find the position of the element on the searching dialog. Later, after the dialog element will be recreated we'll set focus on the element with the same index.
I suggest to change the code of reDraw to the following
this.reDraw = function() {
var activeElement = document.activeElement, selector, $dialog, activeIndex = -1, $newElem, $buttons,
buttonClass,
getButtonClass = function (classNames) {
var arClasses = ['add-group', 'add-rule', 'delete-group', 'delete-rule'], i, n, className;
for (i = 0, n = classNames.length; i < n; i++) {
className = classNames[i];
if ($.inArray(className, arClasses) >= 0) {
return className;
}
}
return null;
};
if (activeElement) {
selector = activeElement.nodeName.toLowerCase();
buttonClass = getButtonClass(activeElement.className.split(' '));
if (buttonClass !== null) {
selector += '.' + buttonClass;
if (selector === "input.delete-rule") {
$buttons = $(activeElement).closest('table.group')
.find('input.add-rule,input.delete-rule');
activeIndex = $buttons.index(activeElement);
if (activeIndex > 0) {
// find the previous "add-rule" button
while (activeIndex--) {
$newElem = $($buttons[activeIndex]);
if ($newElem.hasClass("add-rule")) {
activeElement = $newElem[0];
selector = activeElement.nodeName.toLowerCase() + "." +
getButtonClass(activeElement.className.split(' '));
break;
}
}
}
} else if (selector === "input.delete-group") {
// change focus to "Add Rule" of the parent group
$newElem = $(activeElement).closest('table.group')
.parent()
.closest('table.group')
.find('input.add-rule');
if ($newElem.length > 1) {
activeElement = $newElem[$newElem.length-2];
selector = activeElement.nodeName.toLowerCase() + "." +
getButtonClass(activeElement.className.split(' '));
}
}
$dialog = $(activeElement).closest(".ui-jqdialog");
activeIndex = $dialog.find(selector).index(activeElement);
}
}
$("table.group:first",this).remove();
$(this).append(this.createTableForGroup(this.p.filter, null));
if($.isFunction(this.p.afterRedraw) ) {
this.p.afterRedraw.call(this, this.p);
}
if (activeElement && activeIndex >=0) {
$newElem = $dialog.find(selector + ":eq(" + activeIndex + ")");
if ($newElem.length>0) {
$newElem.focus();
} else {
$dialog.find("input.add-rule:first").focus();
}
}
};
Like one can see on the next demo the focus in the Searching Dialog stay unchanged after pressing on the "Add subgroup" or "Add rule" buttons. I set it on the "Add rule" buttons of the previous row group in case of pressing "Delete group".
One more demo use jQuery UI style of the buttons and the texts in the buttons (see the answer). After clicking on the "Delete" (rule or group) button I tried to set the focus to the previous "Add Rule" button because setting of the focus on another "Delete" (rule or group) button I find dangerous.
Additionally in the demo I use
afterShowSearch: function ($form) {
var $lastInput = $form.find(".input-elm:last");
if ($lastInput.length > 0) {
$lastInput.focus();
}
}
because it seems me meaningful to set initial focus on the last input field at the dialog opening.
UPDATED: I find additionally meaningful to set focus on the current clicked buttons "Add subgroup", "Add rule" or "Delete group". The advantage one sees in the case it one first click some button with the mouse and then want to continue the work with keyboard. So I suggest to change the line
inputAddSubgroup.bind('click',function() {
to
inputAddSubgroup.bind('click',function(e) {
$(e.target).focus();
To change the line
inputAddRule.bind('click',function() {
to
inputAddRule.bind('click',function(e) {
$(e.target).focus();
and the line
inputDeleteGroup.bind('click',function() {
to
inputDeleteGroup.bind('click',function(e) {
$(e.target).focus();
and the line
ruleDeleteInput.bind('click',function() {
to
ruleDeleteInput.bind('click',function(e) {
$(e.target).focus();

Resources