I really have two questions; one is much broader than the other.
First, at the top of my code, I've used #define for naming my window class.
On MSDN they recommend using "const wchar_t CLASS_NAME[]" down in the main function, but I couldn't get that to work. Does this have something to do with Unicode (I've had problems with the lpsz stuff as well which seems to be related to that perhaps)? Anyway, point being, I can't figure out why that didn't work. It gave an error saying "Cannot assign const wchar_t to LPCSTR."
My second, and more broad question is, even in the state the code is in below, I debug with no errors and run, but no window opens. I cannot figure out what's missing. Any help would be much appreciated. Thanks.
#include <Windows.h>
#define CLASS_NAME TEXT("Window Class")
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, PSTR lpCmdLine, int nCmdShow)
{
MSG msg;
//Registering the window class.//
WNDCLASS wc = {};
wc.lpfnWndProc = DefWindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
wc.lpszMenuName = NULL;
RegisterClass(&wc);
if (!S_OK)
{
return 0;
}
//Create Window//
HWND hwnd = CreateWindowEx(0, "CLASS_NAME", NULL, NULL, 100, 100, 800, 600, NULL, NULL, hInstance, NULL);
if (hwnd == NULL)
{
return 0;
}
//Show Window//
ShowWindow(hwnd, nCmdShow);
//Message loop//
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
DispatchMessage(&msg);
}
return msg.wParam;
}
The code in question terminates right after registering a window class. It never even creates a window. The issue is the following attempt at error handling:
if (!S_OK)
{
return 0;
}
S_OK is a COM HRESULT value, that evaluates to the constant value 0. Consequently, !S_OK evaluates to true, so that branch is always taken, exiting the user-provided entry point. The compiler issues warning C4127 ("conditional expression is constant") at warning level 4 (which you should be using if at all possible).
Your first question is about mismatching character types (and encodings). The rules are documented under working with strings.
Related
I have the following code,
#include <windows.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
// Register the window class.
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = "Sample Window Class";
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
RegisterClass(&wc);
// Create the window.
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
wc.lpszClassName, // Window class
"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);
// Run the message loop.
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
The error occurs when I try to assign a value of the wrong type 'const char [20]' to 'LPCWSTR' like so:
wc.lpszClassName = "Sample Window Class";
^~~~~~~~~~~~~~~~~~~~~~
If I prefix a string with L"", the Visual C++ compiler will no longer complain.
On the other hand, if I try to compile the same code with gcc or clang, it works just fine,
but after I add the prefix it gives me the following error:
error: assigning to 'LPCSTR' (aka 'const char *') from incompatible type 'const wchar_t[20]'
I wonder what causes this strange behaviour, and how could I make one code compatible for all compilers?
I had practised winapi apps before, but never got this problem.
I have tried making a custom WindowProc to override default behaviour for WM_QUIT/WM_CLOSE/WM_DESTROY messages to do nothing, but the window still closes when I press ALT!
This is the entire code:
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd)
{
const LPCSTR className = "Class name";
WNDCLASS wc = {};
wc.style = 0;
wc.lpfnWndProc = DefWindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = GetModuleHandle(nullptr);
wc.hIcon = nullptr;
wc.hCursor = nullptr;
wc.hbrBackground = nullptr;
wc.lpszMenuName = nullptr;
wc.lpszClassName = className;
RegisterClass(&wc);
HWND window = CreateWindow(className, "Title", WS_OVERLAPPEDWINDOW, 200, 200, 600, 400, nullptr, nullptr, nullptr, nullptr);
ShowWindow(window, SW_SHOW);
MSG msg = {};
while (msg.wParam != WM_QUIT)
{
while(PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}
I believe the window shouldn't close when I press ALT, but it does.
You're mixing the message field with the wParam field:
while (msg.wParam != WM_QUIT)
When you press ALT, the window receives a WM_KEYDOWN message whose wParam is the virtual keycode. It so happens that ALT has the same VK code as the constant for WM_QUIT (0x12).
In addition, you're also translating and dispatching WM_QUIT before quitting. You can deal with both of these by having a single level of indentation with an appropriate check:
while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE) && msg.message != WM_QUIT)
As pointed out in the comment, this is cleaner with GetMessage:
while (GetMessage(&msg, nullptr, 0, 0) > 0)
There are a few problems with your code.
First, you should be using GetMessage() and not PeekMessage(). The fundamental difference is that PeekMessage() is non-blocking, and therefore your program will use 100% of a CPU running that loop.
Also, because the inner loop may or may not loop through multiple messages in a row, checking things on the outer loop like you're doing will be a hit or miss and won't check all messages. You should be checking every message in the inner loop instead.
But in this case it's not necessary since this loop will end if the Window is closed anyway. This check you're doing is pointless.
There are a few other inconsistencies with your code, wc.hInstance should be hInstance, that one you received as parameter in WinMain(). There's no need to go looking for this information using APIs.
Also CreateWindow() should receive hInstance again as it's second to last parameter, not nullptr.
And finally as pointed out by chris' answer, you should be looking for the message type in msg.message and not in msg.wParam.
In addition to all that, this is no place to do a "custom WindowProc". If you want to do a custom WndProc then you should set wc.lpfnWndProc to your own function instead of DefWindowProc, and there you can define custom behavior to your Window.
For example:
LRESULT CALLBACK MyWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CLOSE:
case WM_DESTROY:
// do nothing
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Note there's no need to test WM_QUIT as this message will only happen if your application calls PostQuitMessage().
I don't have much experience with windows programming, but can't specifically see what's wrong with this code, yet the window doesn't open. Although sometimes, not always, it will be open in task manager, so my guess is that it is registering the class and creating the window, but the problem is with the ShowWindow() function. But, I'm not positive.
To my understanding the flow of the program is:
Window is created with the registered class.
The window is shown.
Continuously looks for messages that are processed in the window Proc.
I feel like I've done all these things, so is my understanding wrong, or is my code missing something?
Thanks.
Source Code:
#include <Windows.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_DESTROY)
{
PostQuitMessage(0);
return 0;
}
DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int WINAPI wWinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
//Variable for message loop.
MSG msg;
//Setting up the window class.
WNDCLASSEX windowClass;
windowClass.cbSize = sizeof(windowClass);
windowClass.style = CS_HREDRAW | CS_VREDRAW | CS_PARENTDC;
windowClass.lpfnWndProc = WindowProc;
windowClass.hInstance = hinstance;
windowClass.hbrBackground = (HBRUSH)COLOR_WINDOW;
windowClass.lpszClassName = "WindowClass";
RegisterClassEx(&windowClass);
HWND windowHandle = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, "WindowClass", "My Program", WS_OVERLAPPEDWINDOW, 500, 200, 800, 500, NULL, NULL, hinstance, 0);
if (!windowHandle)
return FALSE;
ShowWindow(windowHandle, nCmdShow);
// Start the message loop.
while (GetMessage(&msg, NULL, 0, 0) != 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Return the exit code to the system.
return msg.wParam;
}
Your window procedure is invoking DefWindowProc but not actually returning the result, and you have undefined behavior because of that. The return value is important, and it can control how the OS handles successive messages to your window. For example, it's important to return the correct value in response to the WM_CREATE message.
Change your window procedure to:
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_DESTROY)
{
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Also, as Mark Ransom identified in the comments section, you should zero-initialize your WNDCLASSEX structure to ensure that you don't get garbage on any members that you didn't explicitly initialize.
I am trying to create a simple window with C/C++ using the native Windows message queue system (without .NET). I followed the MSDN tutorial and wrote some basic code that creates an empty window:
void main()
{
HINSTANCE hinst;
HWND hwndMain;
WNDCLASSEX wnd;
MSG msg;
hinst = GetModuleHandle( NULL );
memset( &wnd, 0, sizeof( wnd ) );
wnd.cbSize = sizeof( wnd );
wnd.lpszClassName = "MainWClass";
wnd.lpfnWndProc = MainWProc;
wnd.hInstance = hinst;
int result = RegisterClassEx( &wnd );
if( !result )
{
printf("RegisterClassEx error: %d\r\n", GetLastError() );
}
hwndMain = CreateWindowEx
(
0, //extended styles
wnd.lpszClassName, //class name
"Main Window", //window name
WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL | WS_MINIMIZEBOX, //style tags
CW_USEDEFAULT, //horizontal position
CW_USEDEFAULT, //vertical position
CW_USEDEFAULT, //width
CW_USEDEFAULT, //height
(HWND) NULL, //parent window
(HMENU) NULL, //class menu
(HINSTANCE) wnd.hInstance, //some HINSTANCE pointer
NULL //Create Window Data?
);
if( !hwndMain )
{
printf("Oh shi- %d\n", GetLastError() );
}
ShowWindow( hwndMain, SW_SHOWDEFAULT );
UpdateWindow( hwndMain );
}
When I run/debug the program, CreateWindowEx returns 0 which means it failed. This triggers the error message "Oh shi- [error code]". The most confusing part is that the error message prints to console:
Oh shi- 0
The error code returned by GetLastError() is 0, which is ERROR_SUCCESS!
I am at a total loss; what is happening? I am so confuse...
P.S.
I am using Visual C++ Express 2010 on Windows 7 32-bit. I have written a Windows Procedure elsewhere but it simply returns 0 for all cases. If, however, anyone wants to see it, I will be happy to show it.
I have changed the Project Default character set of my Visual C++ project to "Not Set". I should not need to prefix L to my things.
Edit: added wnd.hInstance = hinst;
Edit: removed the unnecessary (WNDPROC) cast
Edit: added error checking for RegisterClassEx
It turns out that the problem was with Visual C++ Express (or at least not with the code itself). I copied the code to another project and it worked.
wnd.lpfnWndProc = (WNDPROC) MainWProc;
We can't see the real reason you need to use the cast but it is very fishy. Windows returns 0 from GetLastError() if it didn't see anything going wrong. Which can happen if the window procedure is broken. Like this one:
LRESULT CALLBACK MainWProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return 0;
}
Windows sends the WM_NCCREATE message to ask for the window to be created. If that message doesn't get processed then there will be no window. And no error. Fix:
LRESULT CALLBACK MainWProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
Tweak as necessary to customize the window. Just make sure that DefWindowProc() gets called for every message that you don't want to handle yourself. And keep Petzold close at hand to avoid the simple mistakes. And lose the cast.
All modern versions of Windows use Unicode internally, and by default, Visual Studio projects #define _UNICODE/UNICODE, which causes your application to link to the Unicode versions of the Windows headers.
When you're compiling an application as Unicode, however, the character (and thus "string") types are different. Instead of char, they're now wchar_t. That means that you have to explicitly declare your string literals as long strings by prefixing them with an L.
Alternatively, the Windows headers hide all of this behind macros, but it's no longer necessary because Windows has been Unicode for a long time and that's very unlikely to change.
Beyond that, you're missing several things in your initialization of the WNDCLASSEX structure, like the hInstance member. These things all have to be set perfectly, or things will fail. As well, the RegisterClass(Ex) and CreateWindow(Ex) functions must be passed the exact same string values corresponding to the name of the window class, otherwise they will assume you're talking about two different things. Typos are not forgiven!
I highly recommend that you use the Visual Studio wizards to create a blank (but working!) project template.
The correct boilerplate code goes something like this:
#include <windows.h>
#include <tchar.h>
// Define these here to minimize typos, or preferably, load them from a
// resource file at the top of the main function
#define MYCLASSNAME TEXT("MainWndClass")
#define MYWINDOWNAME TEXT("Main Window")
// Global variable to keep track of your hInstance
HINSTANCE g_hInstance;
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// If you don't process any of the messages yourself, you
// must pass them to DefWindowProc for default handling.
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
// Save the instance handle in a global variable.
g_hInstance = hInstance;
// Register your window class.
// (A full-featured app will probably want to set additional members.)
WNDCLASSEX wcex = {0};
wcex.cbSize = sizeof(wcex);
wcex.lpfnWndProc = WndProc;
wcex.hInstance = hInstance;
wcex.lpszClassName = MYCLASSNAME;
if (!RegisterClassEx(&wcex))
{
MessageBox(NULL, TEXT("Call to RegisterClassEx failed!"), NULL, MB_OK);
return 1;
}
// Create your main window.
HWND hwndMain = CreateWindowEx(0, MYCLASSNAME, MYWINDOWNAME, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL,
hInstance, NULL);
if (!hwndMain)
{
MessageBox(NULL, TEXT("Call to CreateWindowEx failed!"), NULL, MB_OK);
return 1;
}
// Show your main window.
ShowWindow(hwndMain, nCmdShow);
UpdateWindow(hwndMain);
// Run the main message loop.
BOOL bRetVal;
MSG msg;
while ((bRetVal = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if (bRetVal == -1)
{
MessageBox(NULL, TEXT("Error encountered in message loop!"), NULL, MB_OK);
return 1;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
I had the same problem.
In my case its just that i used visual sutdio without admin rights.
And i discovered that in this case i cant debug my application.
In debug mode without admin right, CreateWindowEx return null with error code result to 0 like you.
But if you go to your build directory you can use your app (not in debug mode).
So if this the case fro you too, just start visula studio with admin right and its done.
I think there is almost a way to use visual studio debug mode with admin right without start visual stuido with admin password each time. i dont know how to do that, but i think it may be possible.
In my case i had to handle WNC_NCCREATE manually. DefWindowProc returned zero for WNC_NCCREATE, i fixed that with:
LRESULT CALLBACK MainWProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WNC_NCCREATE)
return true;
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
Weird issue:
Open a large notepad window
create a toolwindow (style WS_EX_TOOLWINDOW)
create 2 more windows (normal overlapped) (WS_OVERLAPPED)
close those 2 overlapped windows (child of desktop or the toolwindow)
the toolwindow jumps behind the notepad window
Does anyone know why this is the case? Or what I could be doing wrong? I would say 'bug in windows', but that is rarely the case.
To answer questions:
It is not a dialog window, but a full window. If i make it have correct children (ie: not a child of desktop), the taskbar entry for the children do not appear (probably easily fixable), but either way, the bug still happens.
I have included example code that shows the issue. I am hoping I am just creating the window wrong or required to respond to a message I am not responding to.
In this example, a tool window will open (no task bar entry, which is what is wanted). Then you click on that window, a subwindow will open. You click on the subwindow, another window will open. Then close both new subwindows and the original window, instead of getting focus, jumps immediately to behind other windows (notepad, etc).
Thanks for any help!
Example code to clarify:
// WindowToback.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include "WindowToback.h"
// Global Variables:
HINSTANCE g_instance;
HWND g_mainWnd = NULL;
wchar_t *szWindowClass = L"WindowToBackSub";
wchar_t *szWindowClass2 = L"WindowToBackSub2";
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK WndProc2(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY _tWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR lpCmdLine,int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
MSG msg;
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
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_WINDOWTOBACK));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_WINDOWTOBACK);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
RegisterClassEx(&wcex);
wcex.lpfnWndProc = WndProc2;
wcex.lpszClassName = szWindowClass2;
return RegisterClassEx(&wcex);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
g_instance = hInstance;
g_mainWnd = CreateWindowEx(WS_EX_TOOLWINDOW,szWindowClass, szWindowClass,WS_OVERLAPPED,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (!g_mainWnd) return FALSE;
ShowWindow(g_mainWnd, nCmdShow);
UpdateWindow(g_mainWnd);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_LBUTTONDOWN:
{
HWND l_hwnd = CreateWindow(szWindowClass2, szWindowClass2, WS_VISIBLE | WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, g_instance, NULL);
ShowWindow(l_hwnd,SW_SHOW);
break;
}
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
LRESULT CALLBACK WndProc2(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_LBUTTONDOWN:
{
HWND l_hwnd = CreateWindow(szWindowClass2, szWindowClass2, WS_VISIBLE | WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, g_instance, NULL);
ShowWindow(l_hwnd,SW_SHOW);
}
break;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
This isn't surprising. In fact, it's exactly the behavior I'd expect.
You're tool window isn't jumping down; rather Notepad is jumping up.
You closed the window that had activation. The system is going to activate the next-highest top-level window in the z-order. Your tool window doesn't a count as a top-level window in this regard (that's part of what being a tool window means). So Notepad gets activated, and it comes to the top.
If you want your tool window to get activated instead, you probably don't really want a tool window.
Are the three windows dialogs to another main window or are they applications in their own right?
If they are dialog windows then I would check that their parent window is correctly assigned.
If they are application windows then I would check that they are appearing in the taskbar.
Without more information about the problem it is hard to give a more meaningful answer.