This is my attempt using a hook function to get the dialog window handle. Both SetWindowPos() and GetLastError() return correct values, but the dialog window is unaffected and shown at position 0,0.
#include <windows.h>
#include <iostream>
static UINT_PTR CALLBACK OFNHookProc (HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) {
using namespace std;
switch (uiMsg) {
case WM_INITDIALOG: {
// SetWindowPos returns 1
cout << SetWindowPos(hdlg, HWND_TOPMOST, 200, 200, 0, 0, SWP_NOSIZE ) << endl;
// GetLastError returns 0
cout << GetLastError() << endl;
break;
}
}
return 0;
}
int main() {
OPENFILENAMEW ofn;
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(OPENFILENAMEW);
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_ENABLEHOOK;
ofn.lpfnHook = OFNHookProc;
GetOpenFileNameW(&ofn);
return 0;
}
When using OFN_EXPLORER, you have to move hdlg's parent window, as the HWND passed to your callback is not the actual dialog window. This is clearly stated in the documentation:
OFNHookProc callback function
hdlg [in]
A handle to the child dialog box of the Open or Save As dialog box. Use the GetParent function to get the handle to the Open or Save As dialog box.
Also, you should wait for the callback to receive the CDN_INITDONE notification, instead of the WM_INITDIALOG message.
Try this:
static UINT_PTR CALLBACK OFNHookProc (HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
if ((uiMsg == WM_NOTIFY) &&
(reinterpret_cast<OFNOTIFY*>(lParam)->hdr.code == CDN_INITDONE))
{
SetWindowPos(GetParent(hdlg), HWND_TOPMOST, 200, 200, 0, 0, SWP_NOSIZE);
}
return 0;
}
Related
I am working on an ImGui project, and am trying to find a way to create custom key binds. I thought of just listing every single VK key code in a massive,
if
statement, but not only would that be bulky, but I would also not take certain keys such as some unique mouse buttons or any other key I may end up missing. I want a function that will store the next mouse or keyboard input into an integer, without the use of a predefined set of available inputs. I want to dynamically recognize any input key.
Minimal example:
const char* cbind0 = "none";
static bool bbind0 = false;
static int ibind0;
if (ImGui::Button(cbind0))
bbind0 = true
if (bbind0 == true)
{
cbind0 = "press any key...";
CopyNextInputTo(ibind0); // Function to copy pressed key to our integer
}
This code would show up as a box in the GUI, and then the integer ibind0 which is containing our now determined keybind, will be used like so:
static bool option = false;
if (GetAsyncKeyState(ibind0) & 1)
{
option =! option;
}
And now we can toggle our option on and off either using a GUI checkbox or by pressing the user-determined key.
The only problem now being I have no clue how to dynamically record all possible inputs! Does anyone know any possible functions or methods? Thanks!
Assuming you are the one creating the window you're using ImGui on, you can handle WM_KEYDOWN events in your WNDPROC callback. Here's a basic example of this in action that will create a pop-up message box whenever you press a key:
#ifndef UNICODE
#define UNICODE
#endif
#include <Windows.h>
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {
const wchar_t WINDOW_CLASS_NAME[] = L"Keyboard Listener Class";
const wchar_t WINDOW_TITLE[] = L"Keyboard Listener";
WNDCLASS windowClass{};
windowClass.lpfnWndProc = WindowProc;
windowClass.hInstance = hInstance;
windowClass.lpszClassName = WINDOW_CLASS_NAME;
RegisterClass(&windowClass);
HWND hWnd = CreateWindowEx(
0,
WINDOW_CLASS_NAME,
WINDOW_TITLE,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
if (hWnd == NULL) {
return EXIT_FAILURE;
}
ShowWindow(hWnd, nCmdShow);
MSG msg{};
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return EXIT_SUCCESS;
}
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_DESTROY:
PostQuitMessage(0);
return EXIT_SUCCESS;
case WM_KEYDOWN:
wchar_t keyName[64];
GetKeyNameText(lParam, keyName, sizeof(keyName));
MessageBox(hWnd, keyName, L"Key Pressed!", MB_OK);
return EXIT_SUCCESS;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return EXIT_SUCCESS;
}
Or, if you aren't the one actually creating the Window, you can hook into another Window's keyboard events using SetWindowsHookEx with the idHook parameter set to WH_KEYBOARD and passing in a KeyboardProc callback to the lpfn parameter.
So I'm working on a program that would detect the window name of a title that constantly changes 6 characters in the title Gamebar-592d22(master) I currently use:
Hwnd hwnd = FindWindowA(NULL, WindowTitle);
and I send mouse input via hwnd.
You won't be able to use FindWindow() for this. Use EnumWindows() instead. Inside the enum callback, use GetWindowText() to get the title of the provided HWND, check if it matches the pattern you are interested in, and if so then use the HWND as needed, eg:
BOOL CALLBACK MyEnumWindowsProc(HWND hwnd, LPARAM lParam)
{
char title[24] = {0};
GetWindowTextA(hwnd, title, 23);
int num;
if (sscanf(title, "Gamebar-%6x(master)", &num) == 1)
{
// use hwnd and lParam as needed...
}
return TRUE;
}
EnumWindows(&MyEnumWindowsProc, ...);
UPDATE: For example, given your comment about sending a mouse message to coordinates within the found window, you can use the callback's LPARAM to pass information into the callback. For instance:
You can pass the address of an HWND variable in the LPARAM, and if the matching window is found then assign its HWND to that variable, then you can send the message when EnumWindows() exits:
BOOL CALLBACK FindGamebarWnd(HWND hwnd, LPARAM lParam)
{
char title[24] = {0};
GetWindowTextA(hwnd, title, 23);
int num;
if (sscanf(title, "Gamebar-%6x(master)", &num) == 1)
{
*reinterpret_cast<HWND*>(lParam) = hwnd;
return FALSE;
}
return TRUE;
}
HWND hwnd = NULL;
EnumWindows(&FindGamebarWnd, reinterpret_cast<LPARAM>(&hwnd));
if (hwnd)
SendMessage(hwnd, WM_LBUTTONUP, 0, MAKELPARAM(pt.x, pt.y));
You can pass the mouse coordinates in the LPARAM, then send the message from in the callback when it finds the matching window:
BOOL CALLBACK ClickGamebarWnd(HWND hwnd, LPARAM lParam)
{
char title[24] = {0};
GetWindowTextA(hwnd, title, 23);
int num;
if (sscanf(title, "Gamebar-%6x(master)", &num) == 1)
{
SendMessage(hwnd, WM_LBUTTONUP, 0, lParam);
return FALSE;
}
return TRUE;
}
EnumWindows(&ClickGamebarWnd, MAKELPARAM(pt.x, pt.y));
I am trying to change a Wndproc for a listview control so the first parameter in wndproc return the control handle that receives the message, the problem is when I change it other functions stop working (I cannot insert columns or items anymore), is there something I need to change or return in order to keep using the same wndproc for all the controls
WNDPROC same for all controls:
LRESULT CALLBACK staticWndProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam){
std::cout << (int)hwnd << " control received msg:" << uMsg << std::endl; //This must work
//event = { msg:uMsg, target:(int)hwnd, x:0, y:0, button:0, key:0 };
switch (uMsg){
case WM_DESTROY:
std::cout << "window says bye " << std::endl;
PostQuitMessage(WM_QUIT);
break;
default:
//msghandlercall(event); //this is handled not in c++
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
after calling to SetWindowLongPtr(handle, GWLP_WNDPROC, (LONG_PTR)staticWndProc); insert messages are not working as default
int createColumn(HWND listhandle, int indexCol, char *Text, int width){
LVCOLUMN lvc={0};
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvc.fmt = LVCFMT_LEFT;
lvc.cx = width;
lvc.pszText = Text;
lvc.iSubItem = indexCol;
//return ListView_InsertColumn(listhandle, indexCol, &lvc);
SendMessage(listhandle,LVM_INSERTCOLUMN,indexCol,(LPARAM)&lvc);
return 1;
}
void createColumns(HWND listhandle, std::vector<LPSTR> columns){
for(int i=0; i<columns.size(); i++) createColumn(listhandle, i, columns[i], 50);
}
int createItem(HWND listhandle, const std::vector<LPSTR>& row){
LVITEM lvi = {0};
//lvi.mask = LVIF_TEXT;
// lvi.pszText = row[0];
int ret = ListView_InsertItem(listhandle, &lvi);
if(ret>-1) for(unsigned i=0; i<row.size(); i++)
ListView_SetItemText(listhandle, ret, i, row[i]);
return ret;
}
HWND createList(int parenthandle=0){
if(parenthandle==0) parenthandle=(int)GetDesktopWindow();
HWND handle = CreateWindow(WC_LISTVIEW, "",WS_VISIBLE|WS_BORDER|WS_CHILD | LVS_REPORT | LVS_EDITLABELS,
10, 10, 300, 100, (HWND)parenthandle, /*(HMENU)ID_LIST*/NULL, GetModuleHandle(NULL), 0);
if(!handle){ std::cerr << "Failed to create list\n"; return 0; }
SetWindowLongPtr(handle, GWLP_WNDPROC, (LONG_PTR)staticWndProc);
ListView_SetExtendedListViewStyle(handle, LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_DOUBLEBUFFER);
createColumns(handle,{"col1","col2","col3"});
createItem(handle,{"item1.1","item1.2","item1.3","item1.4"});
createItem(handle,{"item2.1","item2.2","item2.3","item2.4"});
return handle;
}
NOTE I tried adding wndproc with SetWindowSubclass() but an error occurs:
error C2664: 'BOOL IsolationAwareSetWindowSubclass(HWND,SUBCLASSPROC,UINT_PTR,DWORD_PTR)': cannot convert argument 2 from 'LRESULT (__cdecl *)(HWND,UINT,WPARAM,LPARAM)' to 'SUBCLASSPROC' [build\binding.vcxproj]
When you subclass a window procedure using SetWindowLongPtr(GWLP_WNDPROC), it returns the previous window procedure that is being replaced. Your subclass procedure must pass unhandled messages to that previous procedure using CallWindowProc() instead of DefWindowProc(). This is stated as much in the documentation:
Calling SetWindowLongPtr with the GWLP_WNDPROC index creates a subclass of the window class used to create the window. An application can subclass a system class, but should not subclass a window class created by another process. The SetWindowLongPtr function creates the window subclass by changing the window procedure associated with a particular window class, causing the system to call the new window procedure instead of the previous one. An application must pass any messages not processed by the new window procedure to the previous window procedure by calling CallWindowProc. This allows the application to create a chain of window procedures.
You are not doing that, which is why your code is failing.
Try this instead:
WNDPROC prevWndProc;
LRESULT CALLBACK staticWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
//...
return CallWindowProc(prevWndProc, hwnd, uMsg, wParam, lParam);
}
HWND createList(HWND parenthandle = NULL)
{
if (!parenthandle) parenthandle = GetDesktopWindow();
HWND handle = CreateWindow(..., parenthandle, ...);
if (!handle) {
std::cerr << "Failed to create list\n";
return NULL;
}
prevWndProc = (WNDPROC) SetWindowLongPtr(handle, GWLP_WNDPROC, (LONG_PTR)staticWndProc);
if (!prevWndProc) {
std::cerr << "Failed to subclass list\n";
DestroyWindow(handle);
return NULL;
}
...
return handle;
}
That being said, you really should not be using SetWindowLongPtr(GWLP_WNDPROC) at all. It is unsafe for exact this reason (amongst others). You should be using SetWindowSubclass() instead:
LRESULT CALLBACK staticSubClass(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
//...
if (uMsg == WM_NCDESTROY) {
// NOTE: this requirement is NOT stated in the documentation,
// but it is stated in Raymond Chen's blog article...
RemoveWindowSubclass(hWnd, staticSubClass, uIdSubclass);
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
HWND createList(HWND parenthandle = NULL)
{
if (!parenthandle) parenthandle = GetDesktopWindow();
HWND handle = CreateWindow(..., parenthandle, ...);
if (!handle) {
std::cerr << "Failed to create list\n";
return NULL;
}
if (!SetWindowSubclass(handle, staticSubClass, 1, 0)){
std::cerr << "Failed to subclass list\n";
DestroyWindow(handle);
return NULL;
}
...
return handle;
}
See MSDN for more details:
Subclassing Controls
Safer subclassing
I have an MFC application and I want all pop-up generated by this application as dialog box or using AfxMessageBox should be positioned to some location given in config file.
Is there a way in MFC to set the default position for any pop-up window ?
Thanks in advance
Easily done with a window hook procedure.
Consult this SO post: Hooking window creation in an MFC program
Sample code:
static HHOOK g_myHook = NULL;
LRESULT CALLBACK MyCbtHook(int nCode, WPARAM wParam, LPARAM lParam)
{
switch (nCode)
{
case HCBT_ACTIVATE:
{
CWnd* wnd = CWnd::FromHandle((HWND)wParam);
WINDOWINFO wi;
wi.cbSize = sizeof(wi);
wnd->GetWindowInfo(&wi);
if ((wi.dwStyle & WS_POPUPWINDOW) == WS_POPUPWINDOW)
{
wnd->SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}
break;
}
}
return CallNextHookEx(0, nCode, wParam, lParam);
}
static void InstallHook()
{
g_myHook = SetWindowsHookEx(WH_CBT, MyCbtHook, 0, GetCurrentThreadId());
}
static void UninstallHook()
{
if (g_myHook)
{
UnhookWindowsHookEx(g_myHook);
g_myHook = NULL;
}
}
Call InstallHook in the InitInstance, then UninstallHook in the ExitInstance (not required really).
This sample hook procedure moves all popup window to the top left corner.
It's easy to get system menu on console application (GetSystemMenu) and add some own entries (AppendMenu). But then these menu items are useless for the app. Is there any way to get into message stream that would identify what menu item was clicked ?
I've tried to hook to console window but without any result, I mean the WH_SYSMSGFILTER, all is compiling ok but there are no messages shown the hook function is not run by the system.
Next thing was ReadConsoleInput and this works partially, that is it shows mouse events on the system menu, but there is no information in MENU_EVENT_RECORD structure about what menu item was clicked.
These are my attempts all in one snippet, here the console should be flooded with messages, but only those from ReadConsoleInput appear, but these doesn't contain any useful information. No matter if user clicks on first or second added menu item there are only two codes shown 278 (0x116) WM_INITMENU and 287 (0x11F) WM_MENUSELECT, but there is no way I know to get to the wParam of WM_MENUSELECT message.
#include <windows.h>
#include <stdio.h>
HHOOK sysMsgFilterHook;
LRESULT CALLBACK SysMsgFilterCallback(int nCode, WPARAM wParam, LPARAM lParam) {
printf("%i\n", nCode);
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
static LRESULT CALLBACK consoleWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
printf("%u\n", uMsg);
WNDPROC origProc = (WNDPROC) GetProp(hWnd, "origProc");
return CallWindowProc(origProc, hWnd, uMsg, wParam, lParam );
}
int main() {
SetLastError(0);
HWND console_hwnd = GetConsoleWindow();
HMENU console_hMenu = GetSystemMenu(console_hwnd, FALSE);
HINSTANCE console_hinstance = (HINSTANCE)GetWindowLong(console_hwnd, GWL_HINSTANCE);
DWORD console_processid = GetWindowThreadProcessId(console_hwnd, NULL);
HANDLE console_input_handle = GetStdHandle(STD_INPUT_HANDLE);
AppendMenu(console_hMenu, MF_STRING | MF_CHECKED, NULL, "test menu item");
AppendMenu(console_hMenu, MF_STRING | MF_CHECKED, NULL, "yet another menu item");
WNDPROC origProc = (WNDPROC)SetWindowLongPtr(console_hwnd, GWL_WNDPROC, (LONG_PTR)&consoleWndProc);
SetProp(console_hwnd, "origProc", (HANDLE)origProc);
sysMsgFilterHook = SetWindowsHookEx(
WH_SYSMSGFILTER,
(HOOKPROC)SysMsgFilterCallback,
console_hinstance,
console_processid
);
DWORD numEvents = 0;
INPUT_RECORD input;
while(ReadConsoleInput(console_input_handle, &input, 1, &numEvents)) {
//printf("input.EventType: %i\n", input.EventType);
if (input.EventType == MENU_EVENT) {
printf("input.Event.MenuEvent.dwCommandId %i\n", input.Event.MenuEvent.dwCommandId);
}
}
//printf("GetLastError: %lu\n", GetLastError());
UnhookWindowsHookEx(sysMsgFilterHook);
system("pause");
return 0;
}
I've succeeded with creating hook for mouse events, that is WH_MOUSE_LL. But all other hooks do not work.
What I intend to accomplish is to get some sort of WM_MENUCOMMAND message and then get rest with GetMenuItemInfo.
I've heard that the hooking procedure should be in another dll, but how to do that ? are there any working snippets ?