I'm trying to change text of a Static control in another application.
I'm not able to change its text by using WM_SETTEXT from my program. (The target app has some mechanisms to prevent this)
So I inject a library to the target app. In the library I subclass the Static control.
WNDPROC wndProcOrig;
LRESULT CALLBACK SubclassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_SETTEXT:
DefWindowProc(hWnd, uMsg, wParam, (LPARAM)"1234");
return TRUE;
case WM_USER + 5003:
return TRUE;
}
return CallWindowProc(wndProcOrig, hWnd, uMsg, wParam, lParam);
}
wndProcOrig = (WNDPROC)SetWindowLongPtr(textHandle, GWLP_WNDPROC, (LONG_PTR)SubclassWndProc);
I use the Window Detective to set the text to the Static, when I'm debugging I can the program breaks in SubclassWndProc.
What I have observed:
The SubclassWndProc get called.
The string of lParam is always empty (the value is modified by target app)
After the function called, the text is still empty (not '1234')
Questions:
How does the target app modify the message value?
Is 'DefWindowProc' the most underlying API programmer can use in order to change text of a Static control? If not, what's the best way to change text of a Static control? (Tried SetWindowText, SetDlgItemText, SendMessage with WM_SETTEXT, neither works)
Yes DefWindowProc is basically the lowest level but a window does not have to use it, it can answer WM_GETTEXT directly instead.
Try passing WM_GETTEXT and WM_SETTEXT directly to DefWindowProc.
Related
I viewed many tutorials and posts on this subject, and they all say I pass the class instance pointer (this) in my CreateWindowEx() function, and then store it in the window procedure function, when the WM_NCCREATE message is sent. I guess this is because WM_NCCREATE is supposedly the first message that gets sent to the window procedure since a window is created.
A few questions/notes:
From debugging I came to know that actually WM_GETMINMAXINFO is the first sent message, before WM_NCCREATE (at least on my machine). Does this mean I should listen for this message instead of WM_NCCREATE?
According to this popular article, the reason everyone calls SetWindowLongPtr() after the message WM_NCCREATE is received is because
If the value does not exist by the time WM_NCCREATE is called then,
by some mysterious Windows behavior that I still don't understand, the
value never gets inserted.
I tried to do exactly that (that is, call SetWindowLongPtr() after CreateWindowEx()). It turns out to be just fine. The application runs okay. Below is my code, please tell me if there's something wrong with this approach.
void GLWin32::CreateWindow(...)
{
...
_hwnd = CreateWindowEx(NULL, _wndclassex.lpszClassName, title.c_str(), WS_OVERLAPPEDWINDOW, x, y, width, height, NULL, NULL, _hinstance, NULL);
SetWindowLongPtr(_hwnd, GWL_USERDATA, reinterpret_cast<LONG_PTR>(this));
...
}
//static window procedure for all instances of this class
LRESULT CALLBACK GLWin32::_msgRouter(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
LONG l = GetWindowLongPtr(hwnd, GWLP_USERDATA);
GLWin32* thisPtr = reinterpret_cast<GLWin32*>(l);
if (thisPtr)
return thisPtr->_winProc(msg, wparam, lparam);
else
return DefWindowProc(hwnd, msg, wparam, lparam);
}
LRESULT GLWin32::_winProc(UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg)
{
case WM_CLOSE:
{
PostQuitMessage(0);
return 0;
}
}
return DefWindowProc(_hwnd, msg, wparam, lparam);
}
Why is my approach not used instead of the popular approach?
The problem with this approach is that if you want to use the instance when processing any of the window creation messages (including "ordinary" messages that are sent as part of the creation process), you won't have access to it.
Suppose you want to create a button when processing WM_CREATE (typical scenario) and you want to set the button text to some instance member value. You want something like:
LRESULT GLWin32::_winProc(UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg)
{
case WM_CREATE:
{
CreateWindow("BUTTON", this->buttonText, WS_VISIBLE | WS_CHILD,
10, 10, 50, 30, this->hwnd, NULL, this->hInstance, NULL);
return 0;
}
}
return DefWindowProc(_hwnd, msg, wparam, lparam);
}
Problem is, when WM_CREATE is processed (before CreateWindowEx returns), SetWindowLongPtr wasn't called yet and the instance pointer is missing, so _winProc isn't being called at all.
I regularly use a certain application (which is closed-sourced, single-threaded, 32-bit, native) that predates such niceties as configurable key bindings. It does support plug-ins, though, so I'm making one to provide the missing feature.
I'm using SetWindowsHookEx to add a WH_KEYBOARD hook. Some key presses go through unchanged, and others get suppressed and handled manually. For the latter case I'm (as the documentation seems to suggest) just returning 1 and ignoring the output of CallNextHookEx. That works in most cases, but there's at least one case--Ctrl+D--that the application is still handling even though I told it not to.
Is there something more/different I should be doing to squash that keystroke?
Maybe relevant: normally I get both an HC_NOREMOVE and an HC_ACTION version of each message, but for Ctrl+D I only get the HC_ACTION version.
C# source:
private IntPtr ProcessWindowsMessage(int code, IntPtr wParam, IntPtr lParam)
{
if (code < 0 || code == HC_NOREMOVE)
{
return Win32.CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
}
Keys key = GetKeyFromWParam(wParam);
Keys keyCombo = key | GetCurrentModifiers();
var isKeyDownEvent = IsKeyDownEvent(lParam);
var handled = stateMachine.Add(keyCombo, isKeyDownEvent);
if (handled)
{
Win32.CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
return LResultHandled;
}
else
{
return Win32.CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
}
}
private static readonly IntPtr LResultHandled = new IntPtr(1);
Anyboby knows how to get text of a created window? I set hook on CreateWindow, but GetWindowText returns empty string.
hCBTHook = SetWindowsHookEx(WH_CBT, (HOOKPROC) &CBTHook, g_appInstance, 0);
LRESULT CALLBACK CBTHook(int nCode, WPARAM wParam, LPARAM lParam)
{
if(nCode == HCBT_CREATEWND)
{
HWND hwnd = (HWND)wParam;
CHAR buf[256];
GetWindowText(hwnd, buf, 256);
}
return CallNextHookEx(hCBTHook, nCode, wParam, lParam);
}
"The system calls the hook procedure before sending the WM_CREATE or WM_NCCREATE message to the window."
The window may not have text yet, your callback is invoked very early in the process of window creation. The name of the window gets passed in as part of the CREATESTRUCT message sent with WM_CREATE which would be the earliest the window could do something about having text. Even to hard code something, it would have to be in WM_NCCREATE, which still hasn't happened yet when your callback is called.
However, the callback itself also gets passed the the CREATESTRUCT. If the name that was passed to CreateWindow is what you are after, that's available to you as well.
I'm working on a windows application where I'm implementing the whole event loop and everything like that myself (there's a reason for that). In one place, I need to execute some code AFTER a window is shown. Normally, when the window is created, I do some initialisation when WM_CREATE message is received. WM_SHOWWINDOW is sent just BEFORE the window is displayed. However I need to get some code executed right AFTER the window is displayed for the first time. I can't seem to find a notification message sent AFTER the window is shown. Could it be that there isn't one?
Of course, I can keep a boolean - FirstRun - indicating whether or not I had performed my logic, and then execute the code when WM_ACTIVATE is received, provided the boolean is TRUE, then set FirstRun to FALSE so that the code is not execute the next time I am receive WM_ACTIVATE, but this seems somewhat unnatural to me.
It's been ages since I did win32 programming on this level, so can't remember much of it. What is the best approach here?
There is no special notification, but in many cases you can use this trick:
LRESULT CALLBACK MainWndProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam) // second message parameter
{
switch (uMsg)
{
case WM_USER + 100:
//window is just displayed, do some actions
return DefWindowProc(hwnd, uMsg, wParam, lParam);
case WM_CREATE:
PostMessage(hwnd, WM_USER + 100, 0, 0);
return DefWindowProc(hwnd, uMsg, wParam, lParam);
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
There isn't one because that's what WM_SHOWWINDOW is for. Once that mesage is passed to the default message handling procedure, the window will be shown. The best thing you could do is poll with IsWindowVisible via some sort of timer.
Your program design seems flawed though to have to rely on something like this. What are you trying to do?
As long as you are implementing the whole event loop and everything like that yourself, you can handle this directly in WinMain() like this:
HWND hWnd = CreateWindow(...);
if (!hWnd) return 0;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
OnWindowJustDisplayed(); // here
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
I'm trying to set the text on the "save" button of the Windows "Save file as..." dialog.
I've set up the hook, received the message, found the button (nb. If I call "GetWindowText()" I see "&Save" so I know it's the right button).
Next I changed the text using "SetWindowText()" (and called "GetWindowText()" to check it - the text is correct).
But ... the button still says "Save".
I can change the "Cancel" button using the exact same code - no problem. What's so special about the "Save" button? How can I change it.
Code (for what it's worth):
static UINT_PTR CALLBACK myHook(HWND hwnd, UINT msg, WPARAM, LPARAM)
{
if (msg == WM_INITDIALOG) {
wchar_t temp[100];
HWND h = GetDlgItem(GetParent(hwnd),IDOK);
GetWindowTextW(h,temp,100); // temp=="&Save"
SetWindowTextW(h,L"Testing");
GetWindowTextW(h,temp,100); // temp=="Testing"
}
}
I finally made it work....
I'm pretty sure there's something funny going on with the "Save" button but this code will wrestle it into submission:
// I replace the dialog's WindowProc with this
static WNDPROC oldProc = NULL;
static BOOL CALLBACK buttonSetter(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// Set the button text on every window redraw....
if (msg == WM_ERASEBKGND) {
SetDlgItemTextW(hwnd,IDOK,L"OK");
}
return oldProc(hwnd, msg, wParam, lParam);
};
// This is the callback for the GetWriteName hook
static UINT_PTR CALLBACK GWNcallback(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HWND dlg = GetParent(hwnd);
if (msg == WM_INITDIALOG) {
oldProc = (WNDPROC)GetWindowLongPtr(dlg, GWL_WNDPROC);
if (oldProc !=0) {
SetWindowLongPtr(dlg, GWL_WNDPROC, (LONG)buttonSetter);
}
}
// We need extra redraws to make our text appear...
InvalidateRect(dlg,0,1);
}
You probably need to redraw the window after setting the text.
Try calling UpdateWindow() after setting the text.
Use CDM_SETCONTROLTEXT message to set the text rather than mess with SetWindowText directly, i.e.
SendMessage(hwnd, CDM_SETCONTROLTEXT, IDOK, L"Testing");
http://msdn.microsoft.com/en-us/library/ms646960(VS.85).aspx has more on customizing open/save dialogs