I want to detect the insertion and removing of any USB drive in my computer. I am not experienced in Windows GUI programming, and usually use Qt, but I had to use the Win32 API this time.
The problem is that lParam is always null.
I read that I need to use RegisterDeviceNotification(), but I used it and even with DEVICE_NOTIFY_ALL_INTERFACE_CLASSES, lParam is always null, however the function succeeds.
This is my code. Most of it I took from a Win32 app template from Visual Studio 2015:
#define MAX_LOADSTRING 100
HINSTANCE hInst; // current instance
WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
// TODO: Place code here.
// Initialize global strings
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_USBSPREAD, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAcceleratorsW(hInstance, MAKEINTRESOURCE(IDC_USBSPREAD));
MSG msg;
// Main message loop:
while (GetMessageW(&msg, nullptr, 0, 0))
{
if (!TranslateAcceleratorW(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
return (int) msg.wParam;
}
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_USBSPREAD));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_USBSPREAD);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
GUID WceusbshGUID = { 0x25dbce51, 0x6c8f, 0x4a72,
0x8a,0x6d,0xb5,0x4c,0x2b,0x4f,0xc8,0x35 };
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, (void*)&WceusbshGUID);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DEVICECHANGE:
{
//if (!lParam) break;
PDEV_BROADCAST_HDR hdr = (PDEV_BROADCAST_HDR)lParam;
if (hdr->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
PDEV_BROADCAST_DEVICEINTERFACE_W device = (PDEV_BROADCAST_DEVICEINTERFACE_W)hdr;
if (hdr->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
if (wParam == DBT_DEVICEARRIVAL) {
MessageBoxA(0, "a usb was inserted", "", 0);
}
else if (wParam == DBT_DEVICEREMOVECOMPLETE) {
MessageBoxA(0, "a usb was removed", "", 0);
}
}
}
}
case WM_NCCREATE: // before window creation
return true;
break;
case WM_CREATE :
{
LPCREATESTRUCT params = (LPCREATESTRUCT)lParam;
GUID InterfaceClassGuid = *((GUID*)params->lpCreateParams);
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
NotificationFilter.dbcc_classguid = InterfaceClassGuid;
HDEVNOTIFY dev_notify = RegisterDeviceNotification(hWnd, &NotificationFilter,
DEVICE_NOTIFY_WINDOW_HANDLE);
if (!dev_notify) {
string error = "failed with error : ";
error += GetLastError();
MessageBoxA(0, error.c_str(), 0, 0);
}
else {
MessageBoxA(0, "succeeded", "", 0);
}
}
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
You need to check wParam first and then figure out what to expect from lParam (it can be NULL in some situations). Also there is a break missing after case.
case WM_DEVICECHANGE:
{
switch(wParam)
{
case DBT_DEVICEARRIVAL:
{
assert(lParam);
::MessageBoxW(NULL, L"a usb was inserted", L"", MB_OK);
::PDEV_BROADCAST_HDR hdr{reinterpret_cast<::PDEV_BROADCAST_HDR>(lParam)};
if(hdr)
{
// check info...
}
break;
}
default:
{
// do nothing...
break;
}
} // switch(wParam)
break;
}
Related
I'm pretty new to C++ and I tried making a window, I wanted to insert a text inside the window. I tried googling and finding some way of doing this, one method I saw was by using the PAINSTRUCT function. I keep getting a const char error, which I can't seem to understand or fix. Can someone help me? I have pasted the code.
#include "framework.h"
#include "MyFirstWindow.h"
#define MAX_LOADSTRING 100
HINSTANCE hInst;
WCHAR szTitle[MAX_LOADSTRING];
WCHAR szWindowClass[MAX_LOADSTRING];
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_MYFIRSTWINDOW, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MYFIRSTWINDOW));
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MYFIRSTWINDOW));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_MYFIRSTWINDOW);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance;
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
char* cpaText[] = {
"Hello World!","Your Boy Made His First Window!"
};
int iY = 5;
for (int iLoopCounter = 0; cpaText[iLoopCounter] != '\0'; iLoopCounter++, iY += 20) {
TextOut(hdc, 5, iY, cpaText[iLoopCounter], strlen(cpaText[iLoopCounter]));
}
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
HWND textfield;
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
I think you should firstly know const char* and char*.
char* is a mutable pointer to a mutable character/string.
const char* is a mutable pointer to an immutable character/string. You
cannot change the contents of the location(s) this pointer points to.
Also, compilers are required to give error messages when you try to do
so. For the same reason, conversion from const char * to char* is
deprecated.
Refer: Difference between char* and const char*?
Then your project is created in a Unicode Character way, so you should use the Unicode Character API to create a string array, such as LPCWSTR, _tcslen.
Minimal code:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
std::vector<LPCWSTR> cpaText;
cpaText = {L"Hello World!",L"Your Boy Made His First Window!",L"YEAH"};
int iY = 5;
for (int iLoopCounter = 0; iLoopCounter < cpaText.size(); iLoopCounter++, iY += 20)
{
TextOut(hdc, 5, iY, cpaText[iLoopCounter], _tcslen(cpaText[iLoopCounter]));
}
EndPaint(hWnd, &ps);
}
break;
Note: Don't forget to add #include <vector>.
Quick question, if I create a ListBox and add a string with a space in it to the listbox, the listbox ends up pretty messy because everything after the space will be added into the next line. In this case shown below it's even worse, because the full amount of numbers is not added to the listbox due to this problem. Is it a problem caused by the converting process of a string to a char? How can i prevent it from happening?
Here is the code i use for the Listbox and the adding Process:
case WM_CREATE:
{
LBTest = CreateWindowEx(WS_EX_CLIENTEDGE
, L"LISTBOX",NULL
, WS_CHILD | WS_VISIBLE | WS_VSCROLL| LBS_NOTIFY
, 7, 7, 300, 600
, hWnd, (HMENU)19, hInst, NULL);
std::wstring str,str2=L"Item Number ";
std::wstringstream ss;
for (int l = 0; l < 210; l++)
{
ss <<str2<< std::to_wstring(l);
ss >> str;
int pos = SendMessage(LBTest, LB_ADDSTRING, 0, (LPARAM)str.c_str());
SendMessage(LBTest, LB_SETITEMDATA, pos, (LPARAM)l);
}
break;
}
Here is the full code :
#include "stdafx.h"
#include "Testproject.h"
#include <iostream>
#include <string>
#include <thread>
#include <sstream>
#define MAX_LOADSTRING 100
const UINT WM_WINDOW_UPDATE = WM_APP + 0;
HINSTANCE hInst;
WCHAR szTitle[MAX_LOADSTRING];
WCHAR szWindowClass[MAX_LOADSTRING];
HWND Button1,LBTest;
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK ListBoxExampleProc(HWND, UINT,
WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_TESTPROJECT, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TESTPROJECT));
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TESTPROJECT));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_TESTPROJECT);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; //
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
LBTest = CreateWindowEx(WS_EX_CLIENTEDGE
, L"LISTBOX",NULL
, WS_CHILD | WS_VISIBLE | WS_VSCROLL| LBS_NOTIFY
, 7, 7, 300, 600
, hWnd, (HMENU)19, hInst, NULL);
std::wstring str,str2=L"Item Number ";
std::wstringstream ss;
for (int l = 0; l < 210; l++)
{
ss.clear()
ss <<str2<< std::to_wstring(l);
ss >> str;
int pos = SendMessage(LBTest, LB_ADDSTRING, 0, (LPARAM)str.c_str());
SendMessage(LBTest, LB_SETITEMDATA, pos, (LPARAM)l);
}
break;
}
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
//TODO: Zeichencode, der hdc verwendet, hier einfügen...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
Listbox result:
So my task is to create 5 child windows and when key is pressed close windows in other order. But I have a problem, when I close first 2 windows the other ones become inactive so I have to click on it so it could be focused window, but how can I prevent other windows to become inactive?
#include "stdafx.h"
#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE hInst; // current instance
WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
int windowsCount = 1;
WCHAR buffer[5];
HWND windows[5];
// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
srand(unsigned(time(NULL)));
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
// Initialize global strings
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_OOP10, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance(hInstance, nCmdShow))
return FALSE;
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_OOP10));
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0)) {
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int)msg.wParam;
}
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
ATOM MyRegisterClass(HINSTANCE hInstance) {
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_OOP10));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_OOP10);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
//
// FUNCTION: InitInstance(HINSTANCE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) {
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(szWindowClass, L"Main Window", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
SetClassLong(hWnd, GCL_HBRBACKGROUND, (LONG)CreateSolidBrush(RGB(rand() % 256, rand() % 256, rand() % 256)));
if (!hWnd)
return FALSE;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
windows[0] = hWnd;
return TRUE;
}
BOOL CreateChildWindow(HWND hWnd) {
_itoa(windowsCount + 1, (char*)buffer, 10);
SetClassLong(hWnd, GCL_HBRBACKGROUND, (LONG)CreateSolidBrush(RGB(rand() % 256, rand() % 256, rand() % 256)));
HWND childWnd = CreateWindow(szWindowClass, buffer, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, windows[windowsCount - 1], nullptr, hInst, nullptr);
if (!childWnd)
return FALSE;
ShowWindow(childWnd, SW_SHOWDEFAULT);
UpdateWindow(childWnd);
windows[windowsCount++] = childWnd;
return TRUE;
}
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_LBUTTONUP:
{
if (windowsCount < 5) {
if (!CreateChildWindow(hWnd))
return FALSE;
}
}
break;
case WM_CLOSE:
{
if (IDOK == MessageBox(hWnd, L"Are you sure you want to quit?", L"You are leaving the program", MB_OKCANCEL | MB_ICONINFORMATION | MB_DEFBUTTON2)) {
if (windowsCount == 1) {
DestroyWindow(windows[--windowsCount]);
PostQuitMessage(NULL);
break;
}
else
DestroyWindow(windows[--windowsCount]);
}
}
break;
case WM_RBUTTONUP:
SetWindowText(hWnd, L"Changed title!");
break;
case WM_KEYDOWN: {
if (windowsCount == 1) {
DestroyWindow(windows[--windowsCount]);
PostQuitMessage(NULL);
break;
}
else
DestroyWindow(windows[--windowsCount]);
}
break;
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
EndPaint(hWnd, &ps);
}
break;
/*case WM_DESTROY:
PostQuitMessage(0);
break;*/
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
UNREFERENCED_PARAMETER(lParam);
switch (message) {
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
This should be part of your application logic. Add event handler to the WM_CLOSE message to all your windows and set focus to the appropriate window that is not closed yet.
Windows cannot decide this for you.
I have the main window open. It was created for me using visual studio. What I am attempting to do is have a main screen with 9 to 12 buttons. These buttons will then open up other windows. I am attempting with one button currently the "InventoryBtn". The inventory window open up and I am able to close it. The second time I try to open the window I get an error. It seems the window is already registered and cant be recreated. What am I doing incorrectly? I stepped thru it and aw that "Destroy:" is being called. The second time I try to show the inventory window the handle is uninitialized.
#include "stdafx.h"
#include "VehManager.h"
#include "M_Inventory.h"
#define MAX_LOADSTRING 100
#define InvWindowBtn 101
#define InvBtn 103
using namespace boost::gregorian;
HINSTANCE hInst;
WCHAR szTitle[MAX_LOADSTRING];
WCHAR szWindowClass[MAX_LOADSTRING];
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK InvWindowProcess(HWND, UINT, WPARAM, LPARAM);
HWND handleforInvWin;
WNDCLASSEX wxInv;
void CreateInventoryWindow();
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_VEHMANAGER, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE (IDC_VEHMANAGER));
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_VEHMANAGER));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_VEHMANAGER);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance;
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
MessageBoxA(NULL, "Main window creation failed.", "904.VehicleManager", MB_OK | MB_ICONINFORMATION);
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HFONT hfDefault;
switch (message)
{
case WM_CREATE:
{
HWND hInvBtn = CreateWindowEx(NULL, L"Button", L"INVENTORY", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
50, 220, 100, 24, hWnd, (HMENU)InvBtn, GetModuleHandle(NULL), NULL);
if (hInvBtn == NULL)
MessageBoxA(hWnd, "Could not create inventory button", "904.VehcleManager", NULL);
hfDefault = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
SendMessage(hInvBtn, WM_SETFONT, (WPARAM)hfDefault, MAKELPARAM(FALSE, 0));
}
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
switch (wmId)
{
case InvBtn:
{
CreateInventoryWindow();
break;
}
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_RBUTTONDOWN:
MessageBox(NULL, L"Right button mouse clicks not allowed.", L"904.VehicleManager", NULL);
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
void CreateInventoryWindow()
{
ZeroMemory(&wxInv, sizeof(WNDCLASSEX));
wxInv.cbClsExtra = NULL;
wxInv.cbSize = sizeof(WNDCLASSEX);
wxInv.cbWndExtra = NULL;
wxInv.hbrBackground = (HBRUSH)COLOR_WINDOW;
wxInv.hCursor = LoadCursor(NULL, IDC_ARROW);
wxInv.hIcon = NULL;
wxInv.hIconSm = NULL;
wxInv.hInstance = hInst;
wxInv.lpfnWndProc = (WNDPROC)InvWindowProcess;
wxInv.lpszClassName = L"Inventory";
wxInv.lpszMenuName = NULL;
wxInv.style = CS_HREDRAW | CS_VREDRAW;
if (!RegisterClassEx(&wxInv))
{
int nResult = GetLastError();
MessageBox(NULL, L"Inventory window class registration failed.", L"904.VehicleManager", MB_ICONEXCLAMATION);
return;
}
handleforInvWin = CreateWindowEx(NULL, wxInv.lpszClassName, L"Open Window 2", WS_OVERLAPPEDWINDOW,
200, 170, 640, 480, NULL, NULL, hInst, NULL);
if (!handleforInvWin)
{
MessageBox(NULL, L"Error with the inventory window handle.", L"904.VehManager", MB_ICONEXCLAMATION);
return;
}
ShowWindow(handleforInvWin, SW_SHOWNOACTIVATE);
UpdateWindow(handleforInvWin);
}
LRESULT CALLBACK InvWindowProcess(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HFONT hfDefault;
switch (message)
{
case WM_CREATE:
{
HWND hInvBtn = CreateWindowEx(NULL, L"Button", L"Close", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
50, 220, 100, 24, hwnd, (HMENU)InvBtn, GetModuleHandle(NULL), NULL);
if (hInvBtn == NULL)
MessageBoxA(hwnd, "Could not create inventory button", "904.VehcleManager", NULL);
hfDefault = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
SendMessage(hInvBtn, WM_SETFONT, (WPARAM)hfDefault, MAKELPARAM(FALSE, 0));
break;
}
case WM_DESTROY:
{
break;
}
case WM_COMMAND:
case InvBtn:
{
DestroyWindow(hwnd);
break;
}
break;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
#include "stdafx.h"
// Mario Headers
#include "GameMain.h"
#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE hInst; // current instance
TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
// Mario global variables =================
CGameMain* gGameMain;
HWND hWnd;
PAINTSTRUCT ps;
// ========================================
// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
// My unprocess function =====================================
void OnCreate(HWND hWnd)
{
}
void OnKeyUp(WPARAM wParam)
{
switch (wParam) {
case VK_LEFT:
gGameMain->KeyReleased(LEFT);
break;
case VK_UP:
gGameMain->KeyReleased(UP);
break;
case VK_RIGHT:
gGameMain->KeyReleased(RIGHT);
break;
case VK_DOWN:
gGameMain->KeyReleased(DOWN);
break;
}
}
void OnKeyDown(HWND hWnd,WPARAM wParam)
{
switch (wParam) {
case VK_LEFT:
gGameMain->KeyPressed(LEFT);
break;
case VK_UP:
gGameMain->KeyPressed(UP);
break;
case VK_RIGHT:
gGameMain->KeyPressed(RIGHT);
break;
case VK_DOWN:
gGameMain->KeyPressed(DOWN);
break;
}
}
void OnPaint(HWND hWnd)
{
HDC hdc = BeginPaint(hWnd,&ps);
RECT rect;
GetClientRect(hWnd,&rect);
HDC hdcDouble = CreateCompatibleDC(hdc);
HBITMAP hdcBitmap = CreateCompatibleBitmap(hdc,rect.right,rect.bottom);
HBITMAP bmOld = (HBITMAP)SelectObject(hdcDouble, hdcBitmap);
gGameMain->SetHDC(&hdcDouble);
gGameMain->SendMessage(MESSAGE_PAINT);
BitBlt(hdc,0,0,rect.right,rect.bottom,hdcDouble,0,0,SRCCOPY);
SelectObject(hdcDouble,bmOld);
DeleteDC(hdcDouble);
DeleteObject(hdcBitmap);
DeleteDC(hdc);
}
void OnDestroy()
{
gGameMain->isPlaying = false;
EndPaint(hWnd,&ps);
}
// My unprocess function =====================================
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_GDIMARIO));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_GDIMARIO);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassEx(&wcex);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, WIDTH, HEIGHT, 0, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
// ---------------- Start gdiplus ------------------
GdiplusStartup(&gdiToken,&gdiStartInput,NULL);
// -------------------------------------------------
// Init GameMain
gGameMain = new CGameMain();
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_KEYDOWN:
OnKeyDown(hWnd,wParam);
break;
case WM_KEYUP:
OnKeyUp(wParam);
break;
case WM_CREATE:
OnCreate(hWnd);
break;
case WM_PAINT:
OnPaint(hWnd);
break;
case WM_DESTROY:
OnDestroy();
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
int APIENTRY _tWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR lpCmdLine,int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
MSG msg;
HACCEL hAccelTable;
// Initialize global strings
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_GDIMARIO, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_GDIMARIO));
// Main message loop:
// GameLoop
PeekMessage(&msg,NULL,0,0,PM_NOREMOVE);
while (gGameMain->isPlaying)
{
while (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (gGameMain->enterNextState) {
gGameMain->SendMessage(MESSAGE_ENTER);
gGameMain->enterNextState = false;
}
gGameMain->SendMessage(MESSAGE_UPDATE);
InvalidateRect(hWnd,NULL,FALSE);
/*if (gGameMain->exitCurrentState) {
gGameMain->SendMessage(MESSAGE_EXIT);
gGameMain->enterNextState = true;
gGameMain->exitCurrentState = false;
}*/
::Sleep(gGameMain->timer);
// Do your game stuff here
}
GdiplusShutdown(gdiToken); // Shut down gdiplus token
return (int) msg.wParam;
}
I use InvalidateRect(hWnd,NULL,FALSE); for repaint window, but the problem I met is when I repaint without any changes in Game struct .
First it paints my logo well, the second time ( just call InvalidateRect(hWnd,NULL,FALSE); without gGameMain->SendMessage(MESSAGE_ENTER); which is init some variables for painting .
Thanks for reading this :)
You're calling BeginPaint() in each wm_paint message, but there's no corresponding call to EndPaint() in the wm_paint message handler. Each BeginPaint() must be matched with a corresponding EndPaint(). Your EndPaint() call in OnDestroy is useless.
Also, don't delete the DC returned by BeginPaint(). It is borrowed, not owned.
Other notes: Try to avoid allocating a new bitmap on every wm_paint message. Allocate the bitmap when the window changes size, and hold onto it to be reused by multiple wm_paint passes.
Also note that you're not optimizing the draw to draw only what has been invalidated in the clip rect. You're drawing everything, blitting everything, and relying on GDI to only take the small portion of the bitmap that is actually in the clip rect. This will become a performance issue as the size of your window/bitmap increases and the number of objects/sprites you have running around on the screen increases. You should only draw things that are visible and within the cliprect.