I wrote some code to watch for window title changes. It works fine with different windows in my Windows 7. I use SetWinEventHook like that:
SetWinEventHook(EVENT_OBJECT_NAMECHANGE,
EVENT_OBJECT_NAMECHANGE,
0,
WinEventCallback,
processId,
threadId,
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS | WINEVENT_SKIPOWNTHREAD);
Callback:
void CALLBACK WinEventCallback(HWINEVENTHOOK hWinEventHook,
DWORD dwEvent,
HWND hwnd,
LONG idObject,
LONG idChild,
DWORD dwEventThread,
DWORD dwmsEventTime)
{
qDebug("Window %p", hwnd);
...
GetWindowText(hwnd, ...);
}
For one specific window I see the debug message "Window 0x0" all the time, e.g. I get the window handle set to zero in the callback. In this case GetWindowText fails. All other windows work fine. The question is why? I don't see anything extraordinary in Spy++:
Not all events generated may be associated with a window, especially for something as generic as a name change. The hook documentation specifically states that NULL windows are possible, so simply ignore them if your hook logic is window-oriented. If you are seeing a window change its title but you are getting a NULL window in your callback, then either it is not a real window, or there was an issue marshaling the window to your callback, or something like that.
The problem comes for the WinEventCallback's signature you are using.
Fix it by using this one: WinEventCallback(IntPtr hWinEventHook, uint iEvent, IntPtr hWnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
Related
In my app, I catch all event via SetWindowsHookEx and when a user clicks on a button I retrieve an hwnd that I guess is the handle of the Tbutton.
typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
DWORD lPrivate;
} MSG, *P
Now how can I from this hwnd retrieve the button name (or better the Delphi object representing the button?).
Maybe I can also retrieve the component via the POINT pt; ?
You can use FindControl, which will retrieve the object instance if the window is created by a control that belongs to the same instance of the VCL that calls the function. Since Name is published in TComponent, you can access the property regardless of the actual class type.
Every windowed VCL control has its object instance address stored in the API window's property list, along with properties containing information of module address, process id, thread id. This makes it possible for the VCL to backtrack a control from the window it created.
I decided to create and use windows using a base class like this:
class Window
{
private:
static LRESULT WINAPI WindowProc(HWND _hWnd, UINT _uMsg, WPARAM _wParam, LPARAM _lParam);
protected:
virtual LRESULT onMessageNameGoesHere(/*parameters*/)
{
// Default handling
};
public:
// Other functions
};
So all windows are inherited from this class. This is WindowProc.
LRESULT WINAPI Window::WindowProc(HWND _hWnd, UINT _uMsg, WPARAM _wParam, LPARAM _lParam)
{
Window *window = (Window*)GetWindowLongPtr(_hWnd, GWLP_USERDATA);
switch (_uMsg)
{
case WM_SOMEMESSAGE:
return window->onMessageNameGoesHere();
case WM_NCCREATE:
SetWindowLongPtr(_hWnd, GWLP_USERDATA, LONG(LPCREATESTRUCT(_lParam)->lpCreateParams));
return true;
case WM_GETMINMAXINFO:
return window->onGetMinMaxInfo((LPMINMAXINFO)_lParam);
default:
return DefWindowProc(_hWnd, _uMsg, _wParam, _lParam);
}
}
WM_NCCREATE stores a pointer to the class associated to the window in GWLP_USERDATA. The problem is that WM_GETMINMAXINFO is received before WM_NCCREATE does, making it crash. (Yes, I know I need some code to avoid that)
So is there a way to set the minimum and maximum window size without WM_GETMINMAXINFO or maybe make that message be sent before WM_NCCREATE does?
Feel free to ask for more detail or for some more explanation.
I believe the answer is "no". This is one of the many places where the Windows OOP model has an impedance mismatch with that of C++.
The only solution I can think of is to use RAII to save/restore the context pointer in thread-local storage (make sure to save what was already there to avoid reentrancy isues) and to retrieve it as soon as your message handler is called. On Windows XP you can try __declspec(thread) or explicitly do this via TlsAlloc/TlsSetValue; on Windows Vista or later you might want to use FlsAlloc/FlsSetValue although I'm not sure if fibers can mess with things here...
MSDN -> "GetDlgCtrlID function" -> Remarks:
"... Although GetDlgCtrlID may return a value if hwndCtl is a handle to a top-level window, top-level windows cannot have identifiers and such a return value is never valid."
It seems it is wrong information - "never valid".
At least for Win2k...Win8 this return value is just kernel pointer to hmenu(bar).
And my question is (primarily to MS insiders): why MSDN so inaccurate here?
(Screenshot: http://files.rsdn.ru/42164/gwl(-1)_tagwnd.png)
Upd (tagWND):
Also demo: http://files.rsdn.ru/42164/gwl(-1)_tagwnd.zip
It is not inaccurate. You create a top-level window with CreateWindowEx(). Which looks like this:
HWND WINAPI CreateWindowEx(
_In_ DWORD dwExStyle,
_In_opt_ LPCTSTR lpClassName,
_In_opt_ LPCTSTR lpWindowName,
_In_ DWORD dwStyle,
_In_ int x,
_In_ int y,
_In_ int nWidth,
_In_ int nHeight,
_In_opt_ HWND hWndParent,
_In_opt_ HMENU hMenu,
_In_opt_ HINSTANCE hInstance,
_In_opt_ LPVOID lpParam
);
Note how you don't specify the ID anywhere. But the fine print is in the description for the hMenu argument:
A handle to a menu, or specifies a child-window identifier, depending on the window style. For an overlapped or pop-up window, hMenu identifies the menu to be used with the window; it can be NULL if the class menu is to be used. For a child window, hMenu specifies the child-window identifier, an integer value used by a dialog box control to notify its parent about events. The application determines the child-window identifier; it must be unique for all child windows with the same parent window.
So you can have a menu OR a child ID. Overloaded, pretty common in the winapi, a child control can't have a menu and a toplevel window can't have a child ID. If you forge ahead and ignore this and call GetDlgCtrlID() on a toplevel window anyway then you will get back the value of the hMenu argument you specified in the create call. Well, today, always follow the api or you might get a rude surprise some day, you'd of course use GetMenu() instead.
Is it possible to find out to which process a MessageBox belongs? If yes, how?
You want to use GetWindowThreadProcessId. Here is an example.
#include <windows.h>
static const TCHAR g_cszClass = TEXT("#32770"); // dialog box class
// returned handle must be closed with CloseHandle() when no longer used
HANDLE GetMessageBoxProcess(__in_z LPCTSTR lpcszTitle, __in DWORD dwAccess)
{
HWND hWnd;
DWORD dwProcessId = 0;
HANDLE hRET;
hWnd = FindWindow(g_cszClass, lpcszTitle);
if (hWnd != NULL)
{
GetWindowThreadProcessId(hWnd, &dwProcessId);
if (dwProcessId != 0)
hRET = OpenProcess(dwAccess, FALSE, dwProcessId);
}
return hRET;
}
Not sure why you'd want the process though. Reasons I can think of:
terminating the message box completely
detecting a process
detecting a certain message box
all of which have superior & more optimal alternative solutions.
Another answer gives the programmatic solution. If this is a one-off thing for debugging, you can choose the window with Spy++, get the process ID from the properties window, and then look up the process in Task Manager.
Good day,
I have been using JNA for a while to interact with the Windows API and now I am stuck when creating a window. As far as I have done the following:
1. Have created a child window of an existing window and obtained a valid handler to it.
2. Understood that every window in Windows has a non-stop message-dispatch loop.
3. Understood that the best way to include my window in the message-dispatch loop is to use something like the following code (not mine, but that is what I would do as well):
final LONG_PTR prevWndProc = new LONG_PTR(User32.INSTANCE.GetWindowLong(hwnd, User32.GWL_WNDPROC)); //this is to obtain a pointer to the WNDPROC of the parent window, which we are going to need later
wndProcCallbackListener = new WndProcCallbackListener()
{
public LRESULT callback(HWND hWnd, int uMsg, WPARAM uParam, LPARAM lParam)
{
if (uMsg == WTSAPI.WM_POWERBROADCAST)
{
System.out.println("WM_POWERBROADCAST Event: hWnd="+hwnd+", uMsg="+uMsg+", uParam="+uParam+", lParam="+lParam);
}
else if (uMsg == WTSAPI.WTS_SESSION_CHANGE)
{
System.out.println("WTS_SESSION_CHANGE Event: hWnd="+hwnd+", uMsg="+uMsg+", uParam="+uParam+", lParam="+lParam);
}
//Call the window's actual WndProc so the events get processed.
return User32.INSTANCE.CallWindowProc(prevWndProc, hWnd, uMsg, uParam, lParam);
}
};
//Set the WndProc function to use our callback listener instead of the window's one.
int result = User32.INSTANCE.SetWindowLong(hwnd, User32.GWL_WNDPROC, wndProcCallbackListener);
However, my problem is when I call the GetWindowLong() for the parent window (my first line of code) I get a 0 for the pointer which indicated the function did not complete successfully. A subsequent call to GetLastError() and a quick check in the error codes give me an 'Access is denied' error. This, of course, is logical, since I am trying from my own thread to access the address of the WNDPROC of another, but I was wondering if there is any way (there should be, of course) to circumvent that.
Any pointers? (pun intended)
Do not use GetLastError() after a JNA call. JNA & JNI may call other APIs that may change the last error. Declare SetWindowLong with the clause throws LastErrorException, like this:
int SetWindowLongA(int hWnd, int nIndex, WndProcCallbackListener dwNewLong)
throws LastErrorException;
Notice the 'A' after the name. It makes explicit use of ANSI version. You could use SetWindowLongW as well.
Make sure your callback implements both Callback and StdCall. I prefer using primitive types as much as possible, because this makes mapping fast and obvious to JNA:
public interface WndProcCallbackListener extends Callback, StdCall {
int callback(int hWnd, int Msg, int wParam, int lParam);
}