WinApi and C++ - status bar does not update - c++

My status bar does not refresh during the program execution and I don't know why. First the pseudo-code. I have deleted most of it, leaving only the idea.
#include ... (many includes)
using namespace std;
#include "MyHeaderFile.hpp"
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam);
//here some global variables. Thereare more of them, I leave only teh important ones.
HWND g_hButtonStart;
MSG msg;
//----------------------------------- windows ------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{ WNDCLASSEX wc;
HWND hwnd;
memset(&wc,0,sizeof(wc));
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = CreateSolidBrush(RGB(240,240,240));//(HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = "WindowClass";
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
//window class register
if(!RegisterClassEx(&wc))
{ MessageBox(NULL, "Rejestracja klasy okna nie powiodła się!","BMP->DXF: Błąd!",MB_ICONEXCLAMATION|MB_OK);
return 0;
}
hwnd = CreateWindowEx(WS_EX_WINDOWEDGE,"WindowClass","BMP -> DXF",WS_VISIBLE|WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
500,
275,
NULL,NULL,hInstance,NULL);
if(hwnd == NULL)
{ MessageBox(NULL, "Window Creation Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
return 0;
}
//group boxes, text boxes, buttons ...
//most important are the "START" button and the status bar:
g_hButtonStart = CreateWindowEx( 0, "BUTTON", "S T R T", WS_CHILD | WS_VISIBLE, 243, 133, 100, 30, hwnd, NULL, hInstance, NULL );
ShowWindow( hwnd, nCmdShow );
UpdateWindow( hwnd );
//status bar things
INITCOMMONCONTROLSEX icmc;
icmc.dwSize = sizeof( INITCOMMONCONTROLSEX );
icmc.dwICC = ICC_BAR_CLASSES;
InitCommonControlsEx( & icmc );
g_hStatusBar = CreateWindowEx( 0, STATUSCLASSNAME, NULL, SBARS_SIZEGRIP | WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hwnd,( HMENU ) 200, hInstance, NULL );
SendMessage( g_hStatusBar, SB_SETTEXT, 0,( LPARAM ) "some info" );
//status bar - end
while(GetMessage(&msg, NULL, 0, 0))
{ if (!IsDialogMessage(hwnd, &msg))
{TranslateMessage(&msg);
DispatchMessage(&msg); }
}
return msg.wParam;
}
//messages
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{ bool ParameterWarning=false;
string Info;
switch(Message)
{ case WM_CLOSE:
{ DestroyWindow( hwnd );
}
break;
case WM_DESTROY:
{ PostQuitMessage(0);
break;
}
//here is the most important part of the code
case WM_COMMAND:
{ if(( HWND ) lParam == g_hButtonStart )
{ //enabling and disabling some buttons, edit boxes etc.
//some variables declarations and initializations...
SendMessage( g_hStatusBar, SB_SETTEXT, 0,( LPARAM ) "message 1" );
//opening the in file ...
//reading file parameters and checking for errors ...
SendMessage( g_hStatusBar, SB_SETTEXT, 0,( LPARAM ) "message 2" );
//here some large calculations start, lasting for 30mins, for example; written in C++
for(int N=0;N<one_of_the_variables;N++)
{
//calculations part 1; also writing to out file ...
SendMessage( g_hStatusBar, SB_SETTEXT, 0,( LPARAM ) "message 3" );
//calculations part 2; also writing to out file ...
SendMessage( g_hStatusBar, SB_SETTEXT, 0,( LPARAM ) "message 4" );
//calculations part 3; also writing to out file ...
SendMessage( g_hStatusBar, SB_SETTEXT, 0,( LPARAM ) "message 5" );
}
//enabling and disabling some buttons, edit boxes etc. ...
//closing the out file ...
}
default:
return DefWindowProc(hwnd, Message, wParam, lParam);
}
return 0;
}
During the calculations, my main window displays "(Not Responding)" in the title. It doesn't bother me (yet). The status bar shows only some of the messages - it seems to be random. Sometimes only the last message, sometimes two or three of them. But it never shows them all correctly during the whole calculation. What should I do to make it work?

The not responding is another symptom of the problem. A GUI app needs to process its message queue frequently. It is this act of pumping the message queue that allows the UI to update. Your long running tasks stops the message queue from being pumped and leads to the various problems that you report.
The solution is to pump the message queue frequently. Don't perform long running tasks in the main thread because that stops you being able to service the message queue. Move these tasks into a separate thread.

Related

using modulus to find out if number is divisible by variable not working

I have a problem with using the % operator. This is hard to explain, so I'll just show my code first.
#include <windows.h>
const char ClassName[] = "WindowClass";
int divisible = 1;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
SetTimer(hwnd, 1, 50, NULL);
break;
case WM_TIMER:
{
if (divisible % 15 == 0) {
MessageBox(hwnd, "a", "a", MB_ABORTRETRYIGNORE | MB_ICONASTERISK);
}
divisible++;
break;
}
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_SHIELD);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = ClassName;
wc.hIconSm = LoadIcon(NULL, IDI_SHIELD);
RegisterClassEx(&wc);
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
ClassName,
"Tank Survival",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 820, 642,
NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
So what happens is that the messagebox in WM_TIMER repeats every time WM_TIMER is called although it should only run every 15 times WM_TIMER is called.
Any help would be appreciated.
Thanks!
Well, some good points have been made in the comments, but the essential problem here is that Windows continues to dispatch messages (including WM_TIMER messages) while the message box is on the screen so you end up recursively calling MessageBox each time the timer fires (but, since these are all on top of one another, you only see the one).
One way to resolve this is to kill the timer while the message box is on the screen, like so:
case WM_TIMER:
{
if (divisible % 15 == 0) {
KillTimer (hwnd, 1);
MessageBox (hwnd, "a", "a", MB_ABORTRETRYIGNORE | MB_ICONASTERISK);
SetTimer (hwnd, 1, 50, NULL);
}
divisible++;
break;
}
But you should debug this before you change the code, so that you fully understand what's going on.

Failed CreateWindowEx. How do I get my window (with the button) to actually pop up for once?

I can only assume most of this works because I can't get past the CreateWindowEx check.
If someone would double check all of my fun button code that would be great too.
#include <windows.h>
#include <tchar.h> //I was told I needed this line but I don't believe I do.
#define ID_BTNENCRYPT 0
const char g_szClassName[] = "MyWindowClass";
HWND button;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch(msg) { //Fun button stuff
case WM_CREATE: {
button = CreateWindow("button",
"Encrypt Message",
WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
450, 620, 200, 30,
hwnd, (HMENU) ID_BTNENCRYPT, GetModuleHandle(NULL), NULL);
break;
}
case WM_COMMAND: { //More fun button stuff
switch (LOWORD(wParam)){
case ID_BTNENCRYPT: {
::MessageBox(hwnd, "This will be your message once I get my $h!t together", "Encrypted Message", MB_OK);
}
}
break;
}
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
FreeConsole();
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName;
if (!RegisterClassEx(&wc)) {
::MessageBox(NULL, "Window Registration Status: Hopelessly F***ed", "", MB_OK);
return 0;
} //No apparent error in Window Registration
Here's where I need help
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
g_szClassName,
"Great Window",
WS_OVERLAPPEDWINDOW,
300, 300,
350, 350,
NULL, NULL, hInstance, NULL);
if (hwnd == NULL) {
::MessageBox(NULL,"Window Creation Status: Gone to $h!t", "", MB_OK);
}
I unfortunately get the error message that yes, my window creation has failed.
ShowWindow(hwnd, nCmdShow); //Just the end of my code from here on out.
UpdateWindow(hwnd); //Hopefully there aren't any fatal errors.
while(GetMessage(&Msg, NULL, 0, 0) > 0) {
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
Your WndProc() is not returning the return value of DefWindowProc() for unhandled messages. There is a missing return statement, so you end up falling to return 0 for all messages. When WM_NCCREATE returns 0, CreateWindowEx() fails:
If an application processes this message, it should return TRUE to continue creation of the window. If the application returns FALSE, the CreateWindow or CreateWindowEx function will return a NULL handle.
You need to change this:
default:
DefWindowProc(hwnd, msg, wParam, lParam);
To this:
default:
return DefWindowProc(hwnd, msg, wParam, lParam);

C++ Win32 Background Image

I have looked at some background drawing tutorials but I still can't draw my background; it's always white. My resources are already in the project. I have tried a few other ways by using paint instead but it still would not draw the background image.
#include <windows.h>
#include <commctrl.h>
#include "resource.h"
HINSTANCE hInst;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
HWND hProgress, hWndBottom;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
hInst = hInstance;
MSG msg = {0};
WNDCLASS wc = {0};
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hbrBackground = CreatePatternBrush( LoadBitmap( hInstance, MAKEINTRESOURCE(IDB_BG) ));//(HBRUSH)(COLOR_BACKGROUND);
wc.lpszClassName = "Test";
if( !RegisterClass(&wc) )
return 1;
if( !CreateWindow(wc.lpszClassName,
"Tests",
WS_POPUPWINDOW|WS_VISIBLE, //WS_OVERLAPPEDWINDOW|WS_VISIBLE
1,1,200,250,0,0,hInstance,NULL))
return 2;
while( GetMessage( &msg, NULL, 0, 0 ) > 0 )
DispatchMessage( &msg );
return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
//Make TopMost
::SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
INITCOMMONCONTROLSEX InitCtrlEx;
InitCtrlEx.dwSize = sizeof(INITCOMMONCONTROLSEX);
InitCtrlEx.dwICC = ICC_PROGRESS_CLASS;
SendMessage(hProgress, PBM_SETRANGE, 0, MAKELPARAM(0, 100));
switch(message)
{
case WM_CREATE:
{
hProgress = CreateWindowEx(0, PROGRESS_CLASS, NULL,
WS_CHILD | WS_VISIBLE | PBS_SMOOTH,
10, 190, 170, 10,
hWnd, NULL, hInst, NULL);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
If you're going to draw a bitmap for your background, you do not just set the background brush to the handle of your bitmap.
Instead, you set the background brush to NULL, and handle the WM_ERASEBKGND message. You respond to it by drawing your bitmap (e.g., with BitBlt or StretchBlt), then you return TRUE (or any other non-zero value) to tell DefWindowProc that the background has been erased, so it shouldn't try to erase it.
Note that if you're doing this in an MDI program, you need to do this in the MDI client window. With MDI, you have a parent window, a client window, and some number of MDI child windows. What looks like the background of the main window is really occupied by the MDI client window, so that's where you need to draw in your background.

Visual studio "cannot find the path specified" error

I am having a problem creating a simple window in C++ visual studio. I started a new "empty project" and only created one .cpp file. When I try to run the program, I get this error:
Unable to start program C:\...\Project1.exe. The system cannot find the file specified.
Why does this happen? I'm using visual studio 2010.
Here is my code:
#include <windows.h>
// Function prototypes.
LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam );
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int iCmdShow );
int WINAPI WinMain( HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR szCmdLine,
int iCmdShow )
#pragma region part 1 - STARTUP STUFF
WNDCLASS wc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)GetStockObject( WHITE_BRUSH );
wc.hCursor = LoadCursor( NULL, IDC_ARROW );
wc.hIcon = LoadIcon( NULL, IDI_APPLICATION );
wc.hInstance = hInstance;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = TEXT("Philip");
wc.lpszMenuName = 0; // no menu - ignore
wc.style = CS_HREDRAW | CS_VREDRAW; // Redraw the window
RegisterClass( &wc );
HWND hwnd = CreateWindow(
TEXT("Philip"),
TEXT("window's title!"),// appears in title of window
WS_OVERLAPPEDWINDOW,
10, 10,
200, 200,
NULL, NULL,
hInstance, NULL );
ShowWindow(hwnd, iCmdShow );
UpdateWindow(hwnd);
#pragma endregion
#pragma region part 2 - ENTER A LOOP TO CONTINUALLY KEEP CHECKING WITH WIN O/S FOR USER INTERACTION
MSG msg;
while( GetMessage( &msg, NULL, 0, 0 ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
#pragma endregion
return msg.wParam; // return from WinMain
}
LRESULT CALLBACK WndProc( HWND hwnd, // "handle" to the window that this message is for
UINT message, // TYPE of message (e.g. WM_PAINT is a message asking to paint the window)
WPARAM wparam, // information about the actual message
LPARAM lparam ) // MORE info about the message
{
switch( message )
{
case WM_CREATE:
// upon creation, let the speaker beep at 50Hz, for 10ms.
Beep( 50, 10 );
return 0;
break;
case WM_PAINT:
{
HDC hdc;
PAINTSTRUCT ps;
hdc = BeginPaint( hwnd, &ps );
// draw a circle and a 2 squares
Ellipse( hdc, 20, 20, 160, 160 );
Rectangle( hdc, 50, 50, 90, 90 );
Rectangle( hdc, 100, 50, 140, 90 );
EndPaint( hwnd, &ps );
}
return 0;
break;
case WM_DESTROY:
PostQuitMessage( 0 ) ;
return 0;
break;
}
return DefWindowProc( hwnd, message, wparam, lparam );
}
If you want to create a window then your kind of project is Windows Form Application (see picture above).
The solutions are an abstract concept to get together several projects. For example you could want to have a Windows Form Application using the features of a Class Library.

Why aren't Shell_NotifyIcon balloon tips working?

According to everything I've seen, the following C++ program should be displaying a balloon tool tip from the tray icon when I left-click in the application window, yet it's not working. Can anyone tell me what I'm missing?
This is on XP with version 6.0 of Shell32.dll (verified with DllGetVersion).
Thanks!
#include "stdafx.h"
#include "shellapi.h"
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
MSG msg;
WNDCLASS wc;
memset(&wc, 0, sizeof(wc));
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszClassName = "sysTrayTest";
RegisterClass(&wc);
HWND hWnd = CreateWindow("sysTrayTest", "",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, 500, 500,
NULL, NULL, hInstance, NULL);
if (hWnd)
{
ShowWindow(hWnd, nCmdShow);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
{
NOTIFYICONDATA nid;
memset(&nid, 0, sizeof(NOTIFYICONDATA));
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = hWnd;
nid.uID = 1;
Shell_NotifyIcon(NIM_DELETE, &nid);
PostQuitMessage(0);
}
break;
case WM_CREATE:
{
NOTIFYICONDATA nid;
memset(&nid, 0, sizeof(NOTIFYICONDATA));
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = hWnd;
nid.uID = 1;
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
nid.uCallbackMessage = WM_USER + 200;
nid.hIcon = LoadIcon(NULL, IDI_INFORMATION);
lstrcpy (nid.szTip, "Test Tip");
Shell_NotifyIcon(NIM_ADD, &nid);
}
break;
case WM_LBUTTONDOWN:
{
NOTIFYICONDATA nid;
memset(&nid, 0, sizeof(NOTIFYICONDATA));
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = hWnd;
nid.uID = 1;
nid.uFlags = NIF_INFO;
lstrcpy(nid.szInfo, "Test balloon tip");
lstrcpy(nid.szInfoTitle, "Test Title");
nid.dwInfoFlags = NIIF_INFO;
nid.uTimeout = 15000;
Shell_NotifyIcon(NIM_MODIFY, &nid);
}
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Bah, I figured it out. For some reason with the headers I have...
sizeof(NOTIFYICONDATA) == 508
whereas...
NOTIFYICONDATA_V3_SIZE == 504
NOTIFYICONDATA_V2_SIZE == 488
NOTIFYICONDATA_V1_SIZE == 88
If I specify either V2 or V3 instead of sizeof(NOTIFYICONDATA) the balloon tips show up just fine.
Have you checked in the registry under ...
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced
... for EnableBalloonTips? It's something very common for users to turn off.
The problem is that you are assuming Windows is going to send you a WM_LBUTTONDOWN when the user click on the icon, but that is not correct. WM_LBUTTONDOWN is sent only when the user clicks inside the hWnd's client area, if you read carefully the documentation of NOTIFYICONDATA you will realize that when the user clicks the icon Windows will send you a WM_USER+20 message (according to your code) and in the lParam paramter you will get the WM_LBUTTONDOWN notification.