How to draw a line in a static control? - c

I want to draw a line in a static control:
case WM_CREATE:
{
hgraph=CreateWindow(WC_STATIC,NULL,WS_CHILD|WS_VISIBLE|SS_CENTER,20,20,660,80,hWnd,NULL,NULL,NULL);
SendMessage(hgraph,WM_SETTEXT,NULL,(LPARAM) "My Static");
break;
}
case WM_PAINT:
{
hdc=GetDC(hgraph);
hp=CreatePen(0 ,5,RGB(0,100,0));
SelectObject(hdc,hp);
MoveToEx(hdc, 0, 0, 0);
LineTo(hdc, 100, 100);
ReleaseDC(hgraph, hdc);
}
break;
but it goes under the static control:

When drawing to any child window, you need to do your drawing within the WM_PAINT of the child window procedure, not within the WM_PAINT of the parent window as you are doing.
For system controls (e.g. statics), you need to subclass the window, which means that you need to replace the system-defined window procedure with your own. Once you have installed your own window procedure into the system control, you can catch the WM_PAINT event on the system control to do your painting.
The complete procedure is as follows:
Define your Replacement Window Procedure for the Static Control.
We also must define a variable that we can use to store the original system Window Procedure for the control, which we must call at some point to allow the control to be drawn as normal.
static WNDPROC pFnPrevFunc;
static LRESULT CALLBACK ProcessStaticMessages(HWND hWindow,
UINT uMessage,
WPARAM wParam,
LPARAM lParam)
{
/*
* call the original system handler so the control
* gets painted as normal.
*/
(*pFnPrevFunc)(hWindow, uMessage, wParam, lParam);
/*
* perform our custom operations on this control in
* addition to system operations.
*/
switch (uMessage)
{
...
case WM_PAINT:
/*
* static control has just been painted by system.
*/
hDC = GetDC(hWindow);
/* draw your lines on the static control */
ReleaseDC(hWindow, hDC);
return TRUE;
}
return TRUE;
}
Create your static control window.
hWndStatic = CreateWindow(WC_STATIC, (LPSTR) NULL, WS_CHILD|... );
Subclass your static control window (install your window procedure)
pFnPrevFunc = SetWindowLongPtr(hWndStatic,
GWLP_WNDPROC,
(LONG_PTR) ProcessStaticMessages);
If this works correctly, then you should receive WM_PAINT messages inside your private message processing function for the static, and your drawing should occur correctly.

Hei bro! Don't forget to add DefWindowProc in the Static Procedure. Sometime you cannot paint your controls without DefWindowProc function.
Example:
LRESULT CALLBACK StaticProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch(Msg)
{
case WM_PAINT:
// Do paint here.
break;
}
return DefWindowProc(hWnd, Msg, wParam, lParam); // Call Default Window Procedure.
}

Related

In WinAPI, how do I know when the mouse has moved outside the current window?

Basically, I want to write a program that shows in a small window the color of the pixel currently pointed by the mouse cursor.
Of course, I could poll the mouse cursor position once in a while, but I would like to opt to a mechanism that calls my code when the mouse cursor has moved, regardless whether it's pointing the current window or not.
Is there some WinAPI trickery that could achieve that functionality?
After some search, I found this:
HHOOK mouseHook =
SetWindowsHookExA(
WH_MOUSE_LL,
LowLevelMouseProc,
hInstance,
0);
...
LRESULT CALLBACK LowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (wParam == WM_MOUSEMOVE) {
// Notify me.
}
return 0;
}

Moving frameless window by dragging it from a portion of client area

As the title says, I would like to move the window only when the user will drag it from a portion of the client area. This will be an imitation of the normal caption bar movement and it's because my form is custom and it doesn't have any title or caption bars. At the moment, I use the code as follows:
...
case WM_NCHITTEST:
return HTCAPTION;
and that works fine for making the user able to move the window no matter where he drags from. I would like to limit this possibility (only the top of the window will allow movement). I haven't tried checking the position of the mouse pressed because I don't know how to do it in the WM_NCHITTEST message.
I use plain Win32 (winapi) C code (no MFC or anything else at the moment) in Visual Studio 2015.
You will run into trouble if you just return HTCAPTION in response to all WM_NCHITTEST messages. You will break things like scrollbars, close buttons, resizing borders, etc. that are all implemented via different HT* values.
You have the right idea, though. You want to make the client area of your window draggable, so you need to trick Windows into thinking that your client area is actually the caption area (which, as you know, is draggable). That code looks like this:
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// ...
case WM_NCHITTEST:
{
// Call the default window procedure for default handling.
const LRESULT result = ::DefWindowProc(hWnd, uMsg, wParam, lParam);
// You want to change HTCLIENT into HTCAPTION.
// Everything else should be left alone.
return (result == HTCLIENT) ? HTCAPTION : result;
}
// ...
}
However, based on the image in your question, you appear to want to restrict this to only a certain region of your window. You will need to define exactly what that area is, and then hit-test to see if the user has clicked in that area. Assuming that rcDraggable is a RECT structure that contains the bounds of the red box shown in your image (in screen coordinates), you can use the following code:
static RECT rcDraggable = ...
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// ...
case WM_NCHITTEST:
{
// Call the default window procedure for default handling.
const LRESULT result = ::DefWindowProc(hWnd, uMsg, wParam, lParam);
// Get the location of the mouse click, which is packed into lParam.
POINT pt;
pt.x = GET_X_LPARAM(lParam);
pt.y = GET_Y_LPARAM(lParam);
// You want to change HTCLIENT into HTCAPTION for a certain rectangle, rcDraggable.
// Everything else should be left alone.
if ((result == HTCLIENT) && (PtInRect(&rcDraggable, pt))
{
return HTCAPTION;
}
return result;
}
// ...
}
If you define rcDraggable in terms of client coordinates, you will need to convert it to screen coordinates before doing the hit-testing in response to WM_NCHITTEST. To do that, call the MapWindowPoints function, like so:
RECT rc = rcDraggable;
MapWindowPoints(hWnd, /* a handle to your window */
NULL, /* convert to screen coordinates */
reinterpret_cast<POINT*>(&rc),
(sizeof(RECT) / sizeof(POINT)));
You can call some magic code in WM_LBUTTONDOWN handler, AFAIR this:
ReleaseCapture();
SendMessage(yourWindowHandle, WM_SYSCOMMAND, 0xf012, 0) ;
I used this method a few years ago in Delphi and Windows XP. I think it must be similar for c++. Of course, you can check x and y before doing this.

creating a window with createwindow() when clicking a menu command

I want to create a window with CreateWindow() when clicking on a menu item that will be a child of the main window. I know I can use DialogBox() or CreateDialog() but I want to use CreateWindow(). I'm using this code
resource.rc file
#include "resource.h"
IDM_MENU MENU
{
POPUP "&Help"
{
MENUITEM "&About", IDM_HELP
}
}
About window procedure
LRESULT CALLBACK AboutProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
Main window procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDM_HELP:
{
WNDCLASSEX wc;
HWND hDlg;
MSG msg;
SecureZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hIcon = (HICON)GetClassLong(hwnd, GCL_HICON);
wc.hIconSm = (HICON)GetClassLong(hwnd, GCL_HICONSM);
wc.hInstance = GetModuleHandle(0);
wc.lpfnWndProc = AboutProc;
wc.lpszClassName = TEXT("AboutClass");
if(!RegisterClassEx(&wc))
break;
hDlg = CreateWindowEx(0, wc.lpszClassName, TEXT("About"), WS_OVERLAPPEDWINDOW, 0, 0, 300, 200, hwnd, 0, wc.hInstance, 0);
ShowWindow(hDlg, SW_SHOWNORMAL);
while(GetMessage(&msg, 0, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnregisterClass(wc.lpszClassName, wc.hInstance);
}
break;
}
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
Is this a good idea? Can you register more than one class to the same instance? Also, is it a good idea to assign the main window icons to this child window or should I load them each time? Will those icons be deleted when I call UnregisterClass() in IDM_HELP? I have tried this program and everything works and the icons still show in the main window after I close this child window. But I still wonder if it's ok to assign the main window icons to this window since I call UnregisterClass() after the child window closes
There is nothing wrong with using CreateWindow/Ex() instead of CreateDialog()/DialogBox(). And there is nothing wrong with running your own modal message loop, as long as you implement it correctly. For instance, take heed of this warning:
Modality, part 3: The WM_QUIT message
The other important thing about modality is that a WM_QUIT message always breaks the modal loop. Remember this in your own modal loops! If ever you call the PeekMessage function or the GetMessage function and get a WM_QUIT message, you must not only exit your modal loop, but you must also re-generate the WM_QUIT message (via the PostQuitMessage message) so the next outer layer will see the WM_QUIT message and do its cleanup as well. If you fail to propagate the message, the next outer layer will not know that it needs to quit, and the program will seem to "get stuck" in its shutdown code, forcing the user to terminate the process the hard way.
The example you showed is not doing that, so you would need to add it:
ShowWindow(hDlg, SW_SHOWNORMAL);
do
{
BOOL bRet = GetMessage(&msg, 0, 0, 0);
if (bRet > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
if (bRet == 0)
PostQuitMessage(msg.wParam); // <-- add this!
break;
}
}
while (1);
UnregisterClass(wc.lpszClassName, wc.hInstance);
However, your modal window should NOT be using WM_QUIT just to break its modal loop, as doing so would exit you entire application! Use a different signal to make your modal loop break when the window is closed. For example:
ShowWindow(hDlg, SW_SHOWNORMAL);
while (IsWindow(hDlg) && (GetMessage(&msg, 0, 0, 0) > 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
LRESULT CALLBACK AboutProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
Also, a modal window is supposed to disable its owner window, and then re-enable it when closed. Your example is not doing that either, so that needs to be added as well:
ShowWindow(hDlg, SW_SHOWNORMAL);
EnableWindow(hwnd, FALSE); // <-- add this
...
LRESULT CALLBACK AboutProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CLOSE:
EnableWindow(GetWindow(hwnd, GW_OWNER), TRUE); // <-- add this
DestroyWindow(hwnd);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
The Old New Thing blog has a whole series of posts on how to work with modal windows.
Modality, part 1: UI-modality vs code-modality
Modality, part 2: Code-modality vs UI-modality
Modality, part 3: The WM_QUIT message
Modality, part 4: The importance of setting the correct owner for modal UI
Modality, part 5: Setting the correct owner for modal UI
Modality, part 6: Interacting with a program that has gone modal
Modality, part 7: A timed MessageBox, the cheap version
Modality, part 8: A timed MessageBox, the better version
Modality, part 9: Setting the correct owner for modal UI, practical exam
The correct order for disabling and enabling windows
Make sure you disable the correct window for modal UI
Update: based on your comment that "No I don't want a modal window", you can ignore everything said above. All of that applies to modal windows only. Since you do not want a modal window, simply remove the secondary loop altogether and let the main message loop handle everything. Also, you do not need to call UnregisterClass(). It will unregister automatically when the process ends. Call RegisterClass() one time, either at program startup, or at least the first time you display the About window. You can use GetClassInfo/Ex() to know whether the class is already registered, or keep track of it yourself. Think of what happens if the user wants to display the About window more than one time during the process's lifetime. So let it re-use an existing class registration each time.
Try this:
LRESULT CALLBACK AboutProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDM_HELP:
{
WNDCLASSEX wc = {0};
wc.cbSize = sizeof(WNDCLASSEX);
wc.hInstance = GetModuleHandle(0);
wc.lpszClassName = TEXT("AboutClass");
if (!GetClassInfoEx(wc.hInstance, wc.lpszClassName, &wc))
{
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hIcon = (HICON)GetClassLong(hwnd, GCL_HICON);
wc.hIconSm = (HICON)GetClassLong(hwnd, GCL_HICONSM);
wc.lpfnWndProc = AboutProc;
if (!RegisterClassEx(&wc))
break;
}
HWND hDlg = CreateWindowEx(0, wc.lpszClassName, TEXT("About"), WS_OVERLAPPEDWINDOW, 0, 0, 300, 200, hwnd, 0, wc.hInstance, 0);
if (hDlg)
ShowWindow(hDlg, SW_SHOWNORMAL);
}
break;
}
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
Yes, you can use CreateWindow(). You can do anything in your event handler. Using DialogBox() just gives you a modal loop for free (so your main window cannot be interacted with until the dialog box is closed).
Yes, you can register multiple window classes. You are free to register all your window classes in advance; you do not need to call RegisterClass() and UnregisterClass() each time someone clicks your menu item.
I'm not sure if UnregisterClass() frees the various GDI resources allocated to your window class; anyone who knows the answer is free to comment.

Not able to read value from EDIT box (windows programming and C)

I need help with this code. I need to set the focus to a edit button and read the value entered in the edit box and move it to a variable for further processing. This code creates a text prompt with TextOut() which says " Enter the value of mass:" and an editbox with an IDC_EDIT_MASS and hEditMASS next to it.
I am not able to read the value from edit box into variable mass.
And the code is as follows *
#define IDC_EDIT_MASS 103 // Edit box identifier
RESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM
lParam)
{
HWND hEditMASS;
HDC hDC;
PAINTSTRUCT Ps;
HFONT font;
float mass;
char msgMASS[]="Enter the value of mass:";
switch (message) /* handle the messages */
{
case WM_CREATE :
hEditMASS=CreateWindowEx(WS_EX_CLIENTEDGE, “EDIT",
"", WS_CHILD|WS_VISIBLE|ES_MULTILINE|ES_AUTOVSCROLL|ES_AUTOHSCROLL,
550,
200,
200,
20,
hwnd,
(HMENU)IDC_EDIT_MASS,
GetModuleHandle(NULL),
NULL);
Break;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDC_EDIT_MASS:
SendMessage(hEditMASS,WM_GETTEXT, sizeof(buffer)/sizeof(buffer[0]),
reinterpret_cast<LPARAM>(buffer));
int ctxtlen=GetWindowTextlength(GetDlgItem(hwnd, IDC_EDIT_MASS));
GetWindowText(GetDlgItem(hwnd, IDC_EDIT_MASS), buffer,(cTxtLen + 1);
mass=atoi(buffer);
MessageBox(NULL,buffer,"Information",MB_ICONINFORMATION);
break;
}
Break;
case WM_SETFOCUS :
SetFocus (hwnd) ;
break;
case WM_PAINT:
hDC = BeginPaint(hwnd, &Ps);
//inputs prompts ...
TextOut(hDC,300,200,msgMASS,sizeof(msgMASS));
EndPaint(hwnd, &Ps);
break;
case WM_DESTROY:
PostQuitMessage (0); /* send a WM_QUIT to the message queue */
break;
default: /* for messages that we don't deal with */
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
hEditMASS is an local, automatic-storage-duration variable. You set it when the message is WM_CREATE. However, you then access it when the message is WM_COMMAND. Automatic-storage-duration variables do not retain their value between calls. In order for it to retain its value, you must either make it global or make it static, e.g.:
static HWND hEditMASS;
Keep in mind that you'll probably only be able to use your window procedure for one window now, since creating any other window with the same window procedure will end up using the same hEditMASS variable, and when you next try to access hEditMASS, it will point to the edit control in the most-recently-created window with that window procedure.

Trying Subclassing on Console

I want to trap keyboard messages in a console application, so I tried this:
HWND GetConsoleHwnd(void)
{
#define SIZEBUF 1024
char szBuffer[SIZEBUF];
GetConsoleTitle(szBuffer, SIZEBUF);
#undef SIZEBUF
return FindWindow(NULL, szBuffer);
}
LRESULT CALLBACK ConsoleProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_KEYDOWN:
switch (wParam)
{
//VK Cases
}
break;
}
return CallWindowProc(OldConsoleProc, hwnd, msg, wParam, lParam);
}
this in main:
HWND hwndConsole = GetConsoleHwnd();
OldConsoleProc = (WNDPROC) SetWindowLong(hwndConsole, GWL_WNDPROC,
ConsoleProc);
and this Global Var: WNDPROC OldConsoleProc;
but it doesnt work, what I am doing wrong?
You can't subclass a window of another process this way. You can do it with hooks but I wouldn't recommend trying this on console window. ReadConsoleInput is low-level enough, and it's as far as you can get without ugly nonportable hacks (I'm not even sure there are some events reaching WndProc when the console window is full screen).

Resources