SetWindowLongPtr doesnt work properly - c++

I want to subclass RichEdit in my program (here is c++ code: http://dumpz.org/46182/). _native_log is a hwnd of richedit. At first all works fine and LogWindow::wndProc callback called normal, but if i set some text in RichEdit or click on them LogWindow::wndProc stops work (there no any further calls of it). Is there any thoughts what's i do wrong?
void LogWindow::replaceNativeLog(HWND native_log_handle) {
_native_log = native_log_handle;
SendMessage(_native_log, EM_GETOLEINTERFACE, 0, (LPARAM) &_rich_edit_ole);
_old_wnd_proc = (WNDPROC) SetWindowLongPtr(_native_log, GWLP_WNDPROC, (LONG) &wndProc);
}
LRESULT LogWindow::wndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
switch (Msg) {
case EM_STREAMIN:
break;
case WM_SETTEXT:
break;
};
return CallWindowProc(_old_wnd_proc, _native_log, Msg, wParam, lParam);
}

Starting with Common Controls version 6 the procedure of subclassing windows has been revised to eliminate the issues with previous versions. In particular it is no longer a problem if a control is subclassed more than once.
A comparison between subclassing pre-v6 Common Controls and the v6 way of doing things can be found at "Subclassing Controls". Instead of calling SetWindowLongPtr to replace the window procedure there is SetWindowSubclass which in addition to replacing the window procedure does all the internal bookkeeping. A consequence of the redesign is that you do not have to store a pointer to the previous window procedure either; if you need to call into the original window procedure there is DefSubclassProc at your disposal.
This of course will only help if all competing clients trying to subclass the a control all agree on using the v6 style subclassing.

Finally, I found the problem. I actually develop a plugin for Miranda IM, and there was another function trying to subclass richedit i want. So there is a kind of conflict between my and that functions. Thanks all for trying to help.

Related

Is there a solution for this case?

This is a Windows Desktop Application project created by Visual Studio.
I have a Dialog resource created from the Resource View that has a Static Text.
I'm using this dialog in order to show errors to the user:
DialogBox(hInst, MAKEINTRESOURCE(IDD_MY_MESSAGE_BOX), hWnd, MyMessageBoxProc);
The reason I'm using a DialogBox is that I need it to stop the code execution, because the next line of code will close the application I mean the user should be aware of the error message before application exits. I know a way to change the Static Text:
HWND myMessageBox = CreateDialog(hInst, MAKEINTRESOURCE(IDD_MY_MESSAGE_BOX), nullptr, MyMessageBoxProc);
HWND staticText = GetDlgItem(myMessageBox, IDC_STATIC);
SetWindowText(staticText, L"Text changed.");
But that approach doesn't stop code execution.
Since it's a Windows Desktop Application project I cannot create MFC classes and try the following approach:
// Find the Static Text.
// If called from within MyMessageBox class.
CWnd *staticText = GetDlgItem(IDC_STATIC);
staticText->SetWindowText("Text changed.");
// If called from elsewhere.
MyMessageBox myMessageBox;
CWnd *staticText = myMessageBox.GetDlgItem(IDC_STATIC);
staticText->SetWindowText("Text changed.");
So what would be a workaround in order to change the Static Text using a DialogBox without the need of MFC classes or even another approach that allows me to change the Static Text and still stop code execution like a DialogBox.
Just change the text in your window procedure (MyMessageBoxProc) by handling WM_INITDIALOG message. If you wish to supply the text to the dialog, then create it using DialogBoxParam instead, which is then accessible via the lParam parameter.
e.g.
INT_PTR MyMessageBoxProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_INITDIALOG) {
HWND hCtrl = GetDlgItem(hWnd, IDC_STATIC);
SetWindowText(hCtrl, reinterpret_cast<LPCTSTR>(lParam));
}
return FALSE;
}
The creation would be something like:
LPCTSTR text = _T("Text changed.");
DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_MY_MESSAGE_BOX), hWnd, MyMessageBoxProc,
reinterpret_cast<LPARAM>(text));
Note that there is a standard message box that ships with windows, which you may want to use instead of writing your own. That's available via the function MessageBox

How to pass class instance to LowLevelMouseProc?

I am implementing logic to handle global mouse clicks and I stuck at moment where I need to get data from LowLevelMouseProc. Currently, I am setting global variable at point when I am creating instance of MyClass and I able to access it from LowLevelMouseProc, but I suppose that isn't right approach.
Setting hook:
g_myClass = this;
SetWindowsHookEx(WH_MOUSE_LL, MyClass::MouseHookProc, LoadLibraryA("user32.dll"), 0);
LowLevelMouseProc:
LRESULT CALLBACK MyClass::MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
MOUSEHOOKSTRUCT * pMouseStruct = (MOUSEHOOKSTRUCT *)lParam;
g_myClass.onClick(wParam, pMouseStruct->pt.x, pMouseStruct->pt.y); //isn't good
}
The low level mouse hook offers no mechanism by which you can retrieve an instance pointer. Using a global variable is often the best option.
If you cannot make your program work that way you will need to use a thunk. That technique has been discussed in many places and a web search will provide more detail. For instance a cursory search on my part yielded this: http://zabkat.com/blog/hook-callback-thunk-x64.htm

Opening a dialog box

I don't quite understand how this works. So I've made my dialog box.. or boxes. And I don't know how to make them appear in my code. Right now I'm trying to just get them to pop up right when I start my program so I can get a basic understanding of how this works.
switch (message)
{
case WM_CREATE:
HINSTANCE hInstance = ((LPCREATESTRUCT) lParam)->hInstance;
CreateDialog(hInstance, "Whatever", hwnd, ABOUT_DIALOG);
That gives me an error in CreateDialog saying a parameter of type int is incompatible with DLGPROC. I'm assuming that I need to declare my dialog box somewhere?
And If I had a button on my very first start up window, how would I know that the user pressed the button? I'm going to once again assume and say that I need to catch it somewhere in the WM_COMMAND command?
The final parameter, the thing that you pass ABOUT_DIALOG to, needs to be a DLGPROC. That is a function of this form:
INT_PTR CALLBACK DialogProc(
HWND hwndDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
);
The compiler is telling you that ABOUT_DIALOG is not a function of that form. In fact the compiler tells you that ABOUT_DIALOG is an int which is definitely not the right thing!
To get it up and running with a default do-nothing dialog procedure implement it like this:
INT_PTR CALLBACK DialogProc(
HWND hwndDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
return FALSE;
}
The documentation says this:
Typically, the dialog box procedure should return TRUE if it processed the message, and FALSE if it did not. If the dialog box procedure returns FALSE, the dialog manager performs the default dialog operation in response to the message.
So by returning FALSE we are asking for default processing.
Once you have the dialog up and running, you can then fill out the dialog procedure with any functionality that you need.

Window Maximum Maximise

I am looking to create a program where I can set the maximum maximize size (as in the size the window maximises to when you hit the maximise button) and maximize position (X/Y coordinated for the maximised window) for all of the windows that are running. This is so that I can have my Rainmeter visible at all times on my secondary monitor without having to manually resize every window fit inside of it.
I have managed to do this for a simple program I wrote using MINMAXSIZE from the WinAPI. This method seems to work perfectly for my little program, but there is very little documentation on it beside 1 or 2 articles on the internet. I was wondering if this would be the best way to set the maximum maximise size, or if there is another way to do this.
They way I planned to implement this into all of the applications was going to be either DLL Injection or Hooks (neither of which I have any experience with), so I was also wondering your guys' thoughts on these methods.
I know there are a few applications out there that already do this, but I thought this could be a learning experience, and as well, all of the applications I tried do not work very well (although this could be the case with all of them due to the way Windows functions).
If any of you are still unsure about what I am talking about, MaxMax does exactly what I want (although it doesn't work so well, as I stated in my previous paragraph).
Thank you all in advance.
What you're probably looking for is the work area setting, that you can set/retrieve with the SystemParametersInfo function, called with the flags SPI_SETWORKAREA/SPI_GETWORKAREA.
What you want to do is use a global windows hook to handle WM_GETMINMAXINFO. As you may be aware, this is the message that is:
Sent to a window when the size or position of the window is about to
change. An application can use this message to override the window's
default maximized size and position, or its default minimum or maximum
tracking size.
The best way to use this to override the default maximum is to fill in the MINMAXINFO structure like so:
case WM_GETMINMAXINFO: {
DefWindowProc(hWnd, message, wParam, lParam);
MINMAXINFO* mmi = (MINMAXINFO*)lParam;
mmi->ptMaxTrackSize.x = 100;
mmi->ptMaxTrackSize.y = 100;
return 0;
}
This will allow the default values to be assigned to the sizes you don't care about (min x/y) in this case, leaving you to fiddle with the max values as you please. Your windows hook should be done with SetWindowsHookEx() and should look something like this:
SetWindowsHookEx(WH_CALLWNDPROC, hook_procedure, instance_handle, 0);
hMod (instance_handle) should only be set depending on the circumstances (check the docs for this). The dwThreadId mandates a global hook. Your CallWndProc might looks something like this:
__declspec(dllexport) LRESULT WINAPI CallWndProc(int nCode, WPARAM wParam, LPARAM lParam) {
CWPSTRUCT* cwp = (CWPSTRUCT*)lParam;
if(WM_GETMINMAXINFO == cwp->message) {
DefWindowProc(hWnd, message, wParam, lParam);
MINMAXINFO* mmi = (MINMAXINFO*)lParam;
mmi->ptMaxTrackSize.x = 100;
mmi->ptMaxTrackSize.y = 100;
return 0;
}
return CallNextHookEx(next_hook, nCode, wParam, lParam);
}
Unfortunately something you are going to have to deal with is that the only windows that will be hooked are the ones that had were existing when you made your call to SetWindowsHookEx(). I'm not aware of a clean way of getting past this, short of looping a call to SetWindowsHookEx() (ergh!).
You could potentially do this with DLL injection and effectively subclass every window with EnumWindows, EnumChildWindow and SetWindowLongPtr/SetWindowSubclass. But why go to all that trouble when you could just use SetWindowsHookEx? :)
To alter the x/y, you might have to add an override for WM_SYSCOMMAND and check for SC_MAXIMIZE then use SetWindowPos/MoveWindow to position it properly (if you don't want it on the default 0, 0).

C++: How to center MessageBox?

Using Visual Studio C++ with MFC. How do I center a MessageBox to it's parent window? Currently it centers to the desktop.
You need to install a hook and change the dialog box position on creation.
int MessageBoxCentered(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
// Center message box at its parent window
static HHOOK hHookCBT{};
hHookCBT = SetWindowsHookEx(WH_CBT,
[](int nCode, WPARAM wParam, LPARAM lParam) -> LRESULT
{
if (nCode == HCBT_CREATEWND)
{
if (((LPCBT_CREATEWND)lParam)->lpcs->lpszClass == (LPWSTR)(ATOM)32770) // #32770 = dialog box class
{
RECT rcParent{};
GetWindowRect(((LPCBT_CREATEWND)lParam)->lpcs->hwndParent, &rcParent);
((LPCBT_CREATEWND)lParam)->lpcs->x = rcParent.left + ((rcParent.right - rcParent.left) - ((LPCBT_CREATEWND)lParam)->lpcs->cx) / 2;
((LPCBT_CREATEWND)lParam)->lpcs->y = rcParent.top + ((rcParent.bottom - rcParent.top) - ((LPCBT_CREATEWND)lParam)->lpcs->cy) / 2;
}
}
return CallNextHookEx(hHookCBT, nCode, wParam, lParam);
},
0, GetCurrentThreadId());
int iRet{ MessageBox(hWnd, lpText, lpCaption, uType) };
UnhookWindowsHookEx(hHookCBT);
return iRet;
}
::AfxMessageBox() appears on the center of the MainFrame for me. Which is basically a call to ::MessageBox() with a handle to the MainFrame as the first parameter. Isn't that working for you?
You can't. That's why a lot of people write their own MessageBox classes.
Who said "can't"?
Try this:
This is for Win32 API, written in C. Translate it as you need...
case WM_NOTIFY:{
HWND X=FindWindow("#32770",NULL);
if(GetParent(X)==H_frame){int Px,Py,Sx,Sy; RECT R1,R2;
GetWindowRect(hwnd,&R1); GetWindowRect(X,&R2);
Sx=R2.right-R2.left,Px=R1.left+(R1.right-R1.left)/2-Sx/2;
Sy=R2.bottom-R2.top,Py=R1.top+(R1.bottom-R1.top)/2-Sy/2;
MoveWindow(X,Px,Py,Sx,Sy,1);
}
} break;
Add that to the WndProc code... You can set position as you like, in this case it just centres over the main program window. It will do this for any messagebox, or file open/save dialog, and likely some other native controls. I'm not sure, but I think you may need to include COMMCTRL or COMMDLG to use this, at least, you will if you want open/save dialogs.
I experimented with looking at the notify codes and hwndFrom of NMHDR, then decided it was just as effective, and far easier, not to. If you really want to be very specific, tell FindWindow to look for a unique caption (title) you give to the window you want it to find.
This fires before the messagebox is drawn onscreen, so if you set a global flag to indicate when action is done by your code, and look for a unique caption, you be sure that actions you take will only occur once (there will likely be multiple notifiers). I haven't explored this in detail, but I managed get CreateWindow to put an edit box on a messagebox dialog. It looked as out of place as a rat's ear grafted onto the spine of a cloned pig, but it works. Doing things this way may be far easier than having to roll your own.
Crow.
EDIT: Small correction to handle the problem raised by Raymond Chen. Make sure that parent handles agree throughout, and this should work ok. It does for me, even with two instances of the same program...