I'm working on a BHO written a long time ago in C++, without the use of any of the VS wizards. As a result, this project deviates from the COM conventions and the boilerplate for a COM product. I worked with COM long ago, but never really did any Windows GUI/dialog stuff...
I'm trying to add a dialog box to allow the user to set the values of some new settings:
// serverDialog will be NULL
HWND serverDialog = CreateDialog(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_PROPPAGE_SETTINGS), NULL, DialogProc);
id (!serverDialog)
{
int error = GetLastError(); //1813
...
}
....
1813 means that the resource cannot be found. The IDD used there is in resource.h, which I manually included where needed.
DialogProc is defined as:
INT_PTR CALLBACK DialogProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
return FALSE;
}
Which I know I will have to change later if I want the dialog to actually process messages, but I haven't gotten that far yet. The 1813 error suggests failure before the dialog is even created as does the NULL dialog handle returned.
To add the dialog I used the Add Resource wizard and added a small property page.
I've tried to follow advice here, but to no avail.
Thanks!
You are passing GetModuleHandle(NULL) as the instance of the module that contains the resource. But GetModuleHandle(NULL) defines the executable file module. You need to pass the instance of the module containing your code. This question covers that topic: How do I get the HMODULE for the currently executing code?
You probably ought to pass a window handle to the hWndParent parameter so that the dialog is owned.
Related
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
EDIT: I forgot to mention, I do not have source code for the DLL that creates window, so I can't actually change the function to return HWND.
I am creating a Win32 application, and am using a DLL that creates a window for me through one of its exported function "void X();" I call X() in my WinMain().
It does create a window for me. I want to get the HWND of the window that was created by this exported library function, as X() returns void, so I can use it for other API calls.
Can someone tell the easiest to get the HWND?
I have searched and questions answered here, but I cant somehow figure out the exact, appropriate solution. I tried EnumWIndows() and then getting the Process ID, and then comparing with the current thread process ID. But I guess there should be a far better much more efficient and a easy way to get HWND.
After all, I am in the WinMain of the process that created this window in the first place.
If I need to explain anything, that I have missed out writing here, please let me know.
I am sure that this is very basic and am missing something blatantly here. Sorry.
Thanks & Regards!
Use a tool like Spy++ or Winspector to see all of the HWNDs created by your app, in particular their class names and window titles. Then you can copy those values into your code and make a single call to FindWindow() after the DLL has created its window, eg:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
// ...
X();
HWND hWnd = FindWindow("ClassNameHere", "TitleHere");
// ...
return 0;
}
The easiest way to do that is to use the function SetWindowsHookEx(WH_CBT, fun, NULL, GetCurrentThreadId()). Then the fun function, a callback defined by you, will be called when a number of events happen. The one you want is the HCBT_CREATEWND.
Somethink like that (totally untested):
HWND hDllHandle = NULL;
LRESULT CALLBACK X_CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HCBT_CREATEWND)
hDllHandle = (HWND)wParam;
return CallNextHookEx(NULL, nCode, wParam, lParam); //The first parameter is useless
}
HWND CallXAndGetHWND()
{
HHOOK hDllHook = SetWindowsHookEx(WH_CBT, X_CBTProc, NULL, GetCurrentThreadId());
X();
UnhookWindowsHookEx(hDllHook);
//hDllHandle is a global variable, so will be now you window!
return hDllHandle;
}
Note that this function is not thread-aware, but most likely you will call it just once at the beginning of your code, so it shouldn't matter.
And beware! Many functions, even Win32 API functions, create hidden windows. This code will hook all of them and return the last one to be created. Changing it to return any other, or even a list of them, if needed, should be trivial.
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...
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.
So i am playing/implementingtomyown with windows via c book examples and there is something about dll injection part that boggles me and i can't solve it.
I created a dialog that belongs to another thread/process and i am trying to send it TCHAR variable so it can then use that var in some function(both the function and tchar are in the same dll file)
So when the dialog is created and sitting well in another thread i send it a message.
First i declare tchar
TCHAR finalpath[MAX_PATH];
Then later i just fill it with info( i do this in the dll thread, not in the dialog's thread, let me also mention that i must do this in the dll thread because thats only way to fill the required tchar(i am required to get dll working directory and fill it in tchar))
So, when i get this info in my tchar i am trying to send a message to the dialog and use tchar as LPARAM(wparam is hwnd btw)
SendMessage(hWndDIPS, WM_APP, (WPARAM) lista, (LPARAM)finalpath);
Afterwards i do basic schoolwork in another threads dialog procedure loop...
INT_PTR WINAPI Dlg_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
chHANDLE_DLGMSG(hWnd, WM_CLOSE, Dlg_OnClose);
case WM_APP:
SaveListViewItemPositions((HWND) wParam, (TCHAR)lParam);
break;
}
return(FALSE);
}
Function that is supposed to receive the parameter(this function resides in shared dll and is called by the procedure as you see above is defined as follows..
void SaveListViewItemPositions(HWND hWndLV, TCHAR sejv[]) {
...}
The compiler error i get from this is
Error 7 error C2664: 'SaveListViewItemPositions' : cannot convert parameter 2 from 'TCHAR' to 'TCHAR []'
So i have no idea why is this happening. If tchar is array then i need to use it in parameters with [] added as thats how arrays are used in parameters(not to mention that if i dont do it it gives me more errors and i cant use the parameter in function anyways)
So why is it not converting then?
If there is another solution to make this dialog to receive a tchar var then please explain.
Thanks
Even after you'll fix your type declarations and properly cast the LPARAM to a TCHAR*, your code will be incorrect. The 'parameter' you pass in to that window procedure is a pointer, and as any pointer, is only valid within a process address space. The receiver window will have to use ReadProcessMemory and copy the string from your process into its own process. Of course, this implies that the receiver process knows your process id, and has proper privileges to be able to read from your memory. And you also need to pass in the length of the string, since ReadProcessMemory cannot guess where the NULL terminator is (although I reckon that with a MAX_PATH max length, this is not a serious issue).
So you are correct, this is a headache, and more so down the road. The privilege issue may be a show stopper.
There are several IPC mechanisms you could use. An easy one is an anonymous named pipe, see Anonymous Pipe Operations. Shared memory is another, see Using Shared Memory in a Dynamic-Link Library. COM would also work (have the process you 'control' create an instance of a class that is hosted in your process server, and let the COM marshaling do the rest, see Marshaling Details). Or you could hand-marshal a COM interface between the process boundary (see CoMarshalInterface).
I think that your problem is that you're typecasting the LPARAM to a TCHAR instead of an array of TCHARs (TCHAR*). Try changing that and see if it fixes things.