Using this code the resulting dialog box is drawn without ability to be able to be resized by mouse:
#include <windows.h>
static UINT_PTR CALLBACK OFNHookProc (HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) {
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;
}
Removing OFN_ENABLEHOOK shows correct dialog window with resize indicator at bottom right corner. How to make dialog that is user-resizeable and with hook procedure ?
(of course that hook is mock here, only to illustrate the error, no matter what I put inside, of course if it is correct on other matters, result is the same)
You need to include the OFN_ENABLESIZING flag when using OFN_ENABLEHOOK. This is documented behavior:
OPENFILENAME structure
OFN_ENABLESIZING
0x00800000
Enables the Explorer-style dialog box to be resized using either the mouse or the keyboard. By default, the Explorer-style Open and Save As dialog boxes allow the dialog box to be resized regardless of whether this flag is set. This flag is necessary only if you provide a hook procedure or custom template. The old-style dialog box does not permit resizing.
Related
As far as I understand, Windows makes a division of labor with regards to the (re)painting of a given window; the division into background erasure and foreground painting. The WM_ERASEBKGNDmessage is sent to prepare an invalidated portion of a given window for painting, and usually this preparation consists of erasing the background so the actual painting can commence of a clean canvas. It seems to me this message is always sent when Windows invalidates part of a given window (and so is basically always sent in conjunction with a WM_PAINT message being posted). Whenever the application itself invalidates (part of) a given window, the last argument to the InvalidateRect function specifies whether a WM_ERASEBKGNDis to be sent or not. So I wrote a small program to test my assumptions, but its behavior eludes me a little. This is said program:
#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 hwInstance, PWSTR pCmdLine, int nCmdShow)
{
// Register the window class.
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = {0};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
// Create the window.
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
L"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if (hwnd == NULL)
{
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Run the message loop.
MSG msg = {0};
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static int eb_count = 0; // counts number of WM_ERASEBKGND messages
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rect;
wchar_t text[40];
wsprintf(text, L"Number of WM_ERASEBKGND messages: %i\n", eb_count);
GetClientRect(hwnd, &rect);
DrawText(hdc, text, -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint(hwnd, &ps);
return 0;
case WM_RBUTTONDOWN: // repaint whenever RBUTTONDOWN
InvalidateRect(hwnd, NULL, FALSE);
UpdateWindow(hwnd);
return 0;
case WM_ERASEBKGND:
eb_count++;
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
I am handling WM_ERASEBKGND in the window procedure (it is a case in my switch), so it should not be passed to the default window procedure. However, I don't do any actual background erasing (I simply increment a static variable) and I return 0 to indicate that no erasing actually took place. It seems to me that in this program, the background should never get erased. This does, however, happen in two distinct instances.
Whenever I maximize the window, the background of the invalid portion does get erased with the class background brush. But how is this possible? The window procedure certainly does no such thing upon receiving a WM_ERASEBKGND message.
A similar thing happens whenever DrawText repaints its string. I would expect that the incrementing numbers would get painted on top of eachother (leading to an illegible mess, of course). So it seems the 'DrawText' function also somehow erases the background of the rectangle in which it paints its string.
My last question pertains to my assumption that a WM_ERASEBKGND message is sent whenever Windows invalidates part of a window. I noticed that whenever the window is covered by another window and subsequently uncovered, no WM_ERASEBKGND message seems to be sent. Does this mean my assumption is wrong?
Sorry for the long read, but any and all help in answering these questions would be greatly appreciated.
... my assumption that a WM_ERASEBKGND message is sent whenever Windows invalidates part of a window. I noticed that whenever the window is covered by another window and subsequently uncovered, no WM_ERASEBKGND message seems to be sent ...
That's because, as of Vista and later, we have the Desktop Window Manager (DWM) lurking away in the, erm, background. This buffers the contents of all the on-screen windows so that Windows doesn't need to issue WM_ERASEBKGND or WM_PAINT requests when part of one is uncovered - it can just copy the so-called back-buffer back to the screen.
[Parts of] windows do still get invalidated - either by you or the OS - but not as often as they used to be back in the XP days. Try minimising and restoring a window for example - then it has to be redrawn. When you do that, the DWM probably throws away the back-buffer to save memory while the window is minimised.
Apart from that, what the others said in the comments.
As per the answer by #Paul Sanders most recently the Desktop Window Manager is a process that does, actually, cache the contents of windows so it can perform a blend effects when composing the desktop, which means that your Window does not always get repainted in the way it did in earlier editions of Windows.
Prior to that, the move from a co-operatively multitasked to a multithreaded OS (This painting model was in the Windows 3.0 API) introduced some race conditions that, iirc, Windows would try and cover up by doing a pre-emptive background-brush fill in some cases when some process changed the visibility of another processes window. This is what you see when you maximise the window.
Your call to DrawText is working, because DrawText - by default - does its own background erase - you need to call SetBkMode passing in the TRANSPARENT flag to render just the font.
I would like to receive a notification when the mouse cursor change from arrow to hour glass etc.
Until now I managed to get notified for mouse position change events, but I can not figure out how to get the cursor type. It looks like the PCURSORINFO struct does not contain any information about the cursor type.
This is the code I have until now:
static LRESULT CALLBACK WinEventCallback(_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam)
{
PCURSORINFO curInfo;
curInfo->cbSize = sizeof(curInfo);
GetCursorInfo(curInfo);
}
void MouseCursorHook::setMouseCursorHook()
{
HHOOK evntHook = SetWindowsHookEx(WH_MOUSE_LL,
WinEventCallback,
GetModuleHandle(0),
0
);
}
First off, your call to GetCursorInfo() is wrong. You are passing it an uninitialized pointer. It should be like this instead:
CURSORINFO curInfo = {};
curInfo.cbSize = sizeof(curInfo);
GetCursorInfo(&curInfo);
Second, CURSORINFO has a hCursor field. You can compare that against the return value of LoadCursor()/LoadImage() to see if a standard cursor is being used or not. You will have to compare each standard cursor individually, though.
I am writing a dll which is a COM wrapper for another dll (the inner dll) without COM support. The inner dll performs a lengthy computation and lets the outer dll know how progress is going via a callback function. The outer dll just makes the functions visible over COM.
However, I need the outer dll to pop up a progress bar dialog (the COM client I'm serving can't do this itself for various reasons). So how do I go about doing that? All examples I have seen so far revolve around Win32 apps which have a WinMain entry point; what can be done if we're already in a dll call when the dialog is needed?
I'm new to windows GUI programming, so quite out of my depth here. The existing code is included below - specific suggestions on what to call where would be appreciated. I'm guessing I may need to fire off a second thread to refresh the progress dialog.
Inner dll .h file (for implicit linking):
#define INNER_API extern "C" __declspec(dllimport)
//create calculation, passing callbacks for warning messages and progress bar
INNER_API Calculation* __stdcall calc_create(...blah...,
int (__cdecl *set_progressor_callback)(long),
int (__cdecl *print_warning_callback)(const char*));
INNER_API void __stdcall calc_run(Calculation *c);
Then in the outer dll, the com wrapper, ComWrapperObject.cpp:
int my_progressor_callback(long progress)
{
//set progressor to equal progress, but how?
return 0;
}
STDMETHODIMP ComWrapperObject::do_calculation()
{
//fire up progress bar and message window here, but how?
Calculation *calc = calc_create(...blah..., &my_progressor_callback);
calc_run(calc);
//wait for user to dismiss message window, but how?
return S_OK;
}
I'm posting a new answer which is more relevant to your updated question (and in order to be eligible for the bounty). Consider first this minimal source for a regular executable which contains a progress bar:
#include <Windows.h>
#include <CommCtrl.h>
#pragma comment(lib, "Comctl32.lib")
#include "resource.h"
#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#define PROGRESSBAR_TIMER_ID 1
/*
* This callback is invoked each time the main window receives a message.
*/
INT_PTR CALLBACK DialogFunc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg) {
case WM_INITDIALOG: {
/*
* Fire a timer event each second.
*/
SetTimer(hwndDlg, PROGRESSBAR_TIMER_ID, 1000, NULL);
break;
}
case WM_TIMER: {
/*
* Catch the timer event that is fired each second. Increment the progress
* bar by 10% each time.
*/
HWND hwndProgressBar = GetDlgItem(hwndDlg, IDC_PROGRESS1);
UINT iPos = SendMessage(hwndProgressBar, PBM_GETPOS, 0, 0);
/*
* If the position is already full then kill the timer. Else increment the
* progress bar.
*/
if(iPos >= 100) {
KillTimer(hwndDlg, PROGRESSBAR_TIMER_ID);
} else {
SendMessage(hwndProgressBar, PBM_SETPOS, iPos + 10, 0);
}
break;
}
case WM_CLOSE:
EndDialog(hwndDlg, 0);
break;
default:
return FALSE;
}
return TRUE;
}
BOOL LaunchGUI(HINSTANCE hInstance)
{
return DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DialogFunc) == 0;
}
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
/*
* Initialise the common controls DLL.
*/
INITCOMMONCONTROLSEX iccex;
iccex.dwSize = sizeof(iccex);
iccex.dwICC = ICC_PROGRESS_CLASS | ICC_STANDARD_CLASSES | ICC_WIN95_CLASSES;
if(!InitCommonControlsEx(&iccex)) {
MessageBox(NULL, L"Problem initialising common controls DLL.", NULL, MB_OK);
return -1;
}
/*
* Launches the main GUI window.
*/
LaunchGUI(hInstance);
return ERROR_SUCCESS;
}
If you like, I can post the relevant .rc resource file for this program although the code is mostly for you to gain the correct conceptual understanding. So to quickly summarise, this program:
Contains a single dialog containing a single progress bar
Sets a timer to fire each second
Each time the timer fires, the timer message triggers the progress bar to be updated
Graphically, it looks like this:
Your question is how to increment this bar from a DLL instead. What you need to do is to allow some way for the DLL to communicate with the window containing the progress bar. I'm not quite sure how you are loading the DLL, but this is the approach I would take assuming it is done through DLL injection:
Load/inject the DLL into the target
Have the DLL export some initialisation routine allowing it to receive information about the injector/client process.
Use GetProcAddress and CreateRemoteThread from the client to invoke this initialisation routine.
In the DLL, use the received information to get the HWND of the client.
Concretely, the intialisation routine would look like this:
HWND hwndClient = NULL;
BOOL CALLBACK EnumProc(HWND hwnd, LPARAM lParam)
{
DWORD dwPID;
GetWindowThreadProcessId(hwnd, &dwPID);
if(dwPID == lParam) {
hwndClient = hwnd;
}
}
/*
* This code assumes the client has only one window. Given a PID, it populates
* a global to hold the window handle associated with the PID.
*/
DWORD WINAPI ReceiveClientPID(LPVOID dwPID)
{
EnumWindows(EnumProc, (LPARAM)dwPID);
}
The client code might be something like this:
/*
* Depending on your method of injection, you should have a handle to the
* target process as well as a HMODULE of the injected DLL.
*/
void InitDLL(HANDLE hProcess, HMODULE hModule)
{
FARPROC lpInit = GetProcAddress(hModule, "ReceiveClientPID");
HANDLE hThread = CreateRemoteThread(hProcess, NULL, NULL,
(LPTHREAD_START_ROUTINE)lpInit, (LPVOID)GetCurrentProcessId(), NULL, NULL);
if(hThread == NULL) {
MessageBox(NULL, L"Problem calling init routine in DLL", NULL, MB_OK);
} else {
CloseHandle(hThread);
}
}
So now you have the HWND of the client in the DLL and hence a way for you to do the communication. You can then specify a custom message in the client to change the progress bar:
/*
* The new progress position can be passed in wParam.
*/
#define WM_UPDATE_PROGRESS_BAR (WM_APP + 1)
Also add the corresponding case in the DialogFunc (we can remove the WM_TIMER code now because that was only there to demonstrate how to interact with progress bars):
case WM_UPDATE_PROGRESS_BAR:
SendMessage(GetDlgItem(hwndDlg, IDC_PROGRESS1), PBM_SETPOS, wParam, 0);
break;
And now to trigger changes in the progress bar of the client, the DLL simply has to do:
SendMessage(hwndClient, WM_UPDATE_PROGRESS_BAR, ..., 0);
Note that WM_UPDATE_PROGRESS_BAR needs to be redefined in the DLL as well.
To fit this all in with your current code:
/*
* Assumed progress is between 0 and 100. Otherwise it has to be
* normalised so this is the case (or the range of the progress bar
* in the client has to be changed).
*/
int my_progressor_callback(long progress)
{
SendMessage(hwndClient, WM_UPDATE_PROGRESS_BAR, progress, 0);
return 0;
}
Since you state that the DLL has no GUI and the client handles all the user interaction, why don't you send the progress information to the client instead and have it displayed there?
If you want to display the dialog in the DLL, you do so in exactly the same way as you would within a regular executable. There is absolutely no difference. If you want the DLL to continue working while it updates the progress bar, you can just kick off a new thread with CreateThread.
If you show some code, we'll be able to help you more directly.
I am trying to do some action whenever dialog box is Shown. Its like we have modalless dialog, and we are hinding/showing the dialog on some button click. But we we need to perfomr some action whenever dialog is shown. I have added the WM_SHOWWINDOW message but control is not coming inside of OnShowWindow(BOOL bShow, UINT nStatus) function.
We are using ShowWindow(SW_HIDE) and ShowWindow(SW_SHOW) function to hide/show dialog box
Please suggest some pointer how to achieve that.
Thanks in advance
Mukesh
I tested this with notepad and Spy++ with the following code:
#include <Windows.h>
int CALLBACK WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) {
HWND hwnd = FindWindow(NULL, L"Untitled - Notepad");
ShowWindow( hwnd, SW_HIDE );
Sleep(4000);
ShowWindow( hwnd, SW_SHOW );
return ERROR_SUCCESS;
}
For hiding the window, you should be getting WM_SHOWWINDOW, WM_WINDOWPOSCHANGING, then finally WM_WINDOWPOSCHANGED.
For showing the window, the target did not receive WM_SHOWWINDOW, but still got WM_WINDOWPOSCHANGING and WM_WINDOWPOSCHANGED.
You could handle WM_WINDOWPOSCHANGED and check the flags in WINDOWPOS for SWP_HIDEWINDOW/SWP_SHOWWINDOW.
How to display text on dialog-box? for example - I want to display a countdown timer, should I go with static text control?
Use a static text control and then call the function
BOOL SetWindowText(
HWND hWnd,
LPCTSTR lpString
);
to set the text at runtime.
See online Reference