I use Visual Studio 2022 Community (17.1.3) with C++ 17
It was a pain to manage menus from the raw Win32 API, but with the help of the OOP, I have successfully encapsulated all functions related to menus, so I don't need to care anymore about indexes if I want to do something with one of the items inside a menu.
However, when the mouse cursor hits an item, VS throws the following exception:
wil::ResultException at memory location 0x[Address].
[Rethrow] at memory location 0x0000000000000000.
The app can still run (it doesn't crash, the exception is actually in the output window of VS!)
GetLastError() at the end of the execution returns 0. I can also retrieve what item has been clicked by the user with WM_COMMAND, and it works well.
I don't know whether it's normal or not, but I also tried to compile the template project and another project I've done in the past with the Win32 API, and guess what, the same happens. Hence I think it's not a specific problem related to my code (although I don't have much experience in C++, considering it's been only two years since I started to learn it), but a problem of the IDE itself.
main.cpp
// ---------- main.cpp
#include <Windows.h>
#include "resource.h"
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam);
// Globals
HINSTANCE inst;
HWND mainWnd;
HMENU menuBar;
HMENU subMenu1;
HMENU subMenu2;
int WINAPI wWinMain(HINSTANCE p_hInst, HINSTANCE p_hPrevInst, LPWSTR p_lpszArgs, int p_nCmdShow)
{
inst = p_hInst;
// register the main window class
WNDCLASSEX wcex = { 0 };
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.lpfnWndProc = WindowProc;
wcex.hInstance = inst;
wcex.lpszClassName = L"Menus";
if (!RegisterClassExW(&wcex))
{
// handle error
}
mainWnd = CreateWindowExW(NULL, L"Menus", L"Using menus",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 500, NULL, NULL, inst, NULL);
if (NULL == mainWnd)
{
// Handle error
}
ShowWindow(mainWnd, p_nCmdShow);
UpdateWindow(mainWnd);
// Create a message structure and start the main loop
MSG message;
while (GetMessage(&message, NULL, NULL, NULL))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
return (int)message.wParam;
}
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
// Create and set the menu
menuBar = CreateMenu();
subMenu1 = CreatePopupMenu();
subMenu2 = CreatePopupMenu();
AppendMenu(subMenu1, MF_STRING, ID_ITM1, L"Item 01");
AppendMenu(subMenu1, MF_STRING, ID_ITM2, L"Item 02");
AppendMenu(subMenu1, MF_SEPARATOR, NULL, NULL);
AppendMenu(subMenu1, MF_STRING, ID_ITMCLOSE, L"Close");
AppendMenu(subMenu2, MF_STRING, ID_ITMABOUT, L"About...");
AppendMenu(menuBar, MF_POPUP, (UINT_PTR)subMenu1, L"Menu");
AppendMenu(menuBar, MF_POPUP, (UINT_PTR)subMenu2, L"Help");
SetMenu(hWnd, menuBar);
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case ID_ITMABOUT:
MessageBox(mainWnd, L"Using menus -- Just an example for Stack Overflow", L"About...", MB_OK);
}
break;
case WM_DESTROY:
PostQuitMessage(GetLastError());
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
resource.h
#pragma once
#define ID_ITM1 5000
#define ID_ITM2 5100
#define ID_ITM3 5200
#define ID_ITM4 5300
#define ID_ITMCLOSE 5400
#define ID_ITMABOUT 5500
/* -------------------------------------------------------------
(Don't forget to add an empty line to avoid the "unexpected
end of line error of VS if you don't use the integrated resource
editor.")
------------------------------------------------------------- */
This sample contains the minimum code to create an empty window and assign it a menu.
If you highlight one of the items or click the "About" item and look at the Output window of VS, exceptions will start to throw without any reason, but the app will continue to run fine.
Please, note that it will take some seconds, be patient.
My Visual Studio Output Window looks like that.
Edit:
clearer explanations about the problem.
There's indeed a problem with menus in VS2022 (17.1.4, std/C++17 flag).
When I compile the code above (which does not contain any bugs), it throws the wil::ResultException in my output window, but the program itself continues to run fine.
Related
I'm trying to create a Windows desktop app which has a Rich Edit control, and I want to handle certain keystrokes within the control e.g. hitting enter will execute some code based on what's been typed on that line, similar to a console app. I started with the regular Windows Desktop App template in Visual Studio, and created my control within the WndProc:
case WM_CREATE:
{
LoadLibrary(TEXT("Msftedit.dll"));
//Console creation
hwndEdit = CreateWindowEx(
WS_EX_CLIENTEDGE, // extended styles
MSFTEDIT_CLASS, // Predefined class
NULL, // text
WS_TABSTOP | WS_CHILD | WS_VISIBLE | WS_VSCROLL |
ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL, // Styles
0, // x position - size and position handled separately
0, // y position
0, // Button width
0, // Button height
hWnd, // Parent window
(HMENU)ID_CONSOLE, // Control ID
(HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),
NULL); // Pointer not needed
}
break;
So far, so good. However, when I try to create a procedure for the control and attach it, it doesn't seem to do anything and to be honest, based on the documentation and a lot of Google searches, I'm still not entirely sure how it should be done. My procedure looks like the below:
LRESULT CALLBACK EditControlProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
//static HWND hwndEdit;
switch (uMsg)
{
case WM_PAINT:
return TRUE;
// Other cases...
case WM_KEYDOWN:
switch (wParam)
{
case VK_RETURN:
MessageBox(NULL, L"You pressed enter!", L"Title", NULL);
}
/*case WM_CREATE:
{
HWND console = GetDlgItem(hWnd, ID_CONSOLE);
SetWindowSubclass(console, EditControlProc, 0, 0);
return TRUE;
}*/
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
and my attempt to attach that to the control (which I based on the example here) was:
case WM_CREATE:
{
HWND console = GetDlgItem(hWnd, ID_CONSOLE);
SetWindowSubclass(console, EditControlProc, 0, 0);
return TRUE;
}
I tried handling this within various messages (the last of which was WM_CREATE as above - the link shows WM_INITDIALOG but that didn't work) with no success, and to be honest I'm not sure where I should add this in my case.
I know this is a really basic question and I feel like there should be loads of examples to show me how it's done and I might just be making a stupid mistake (far from unheard of in my short programming studentship), but I've been searching for days and haven't found what I'm looking for so would really appreciate a pointer or two.
Thanks!
So my program works, all apart from one thing, I would like for my button, 'pushBtn' , aka BTN_PUSH_TALK , to send a BN_PUSHED or BN_UNPUSHED message so I can handle it accordingly.
Following steps online, as well as trial and improvement, right now the only response I ever get is once I am done holding / clicking the button.
pushBtn = CreateWindowEx(0, L"BUTTON", L"TALK", WS_CHILD |
WS_VISIBLE |
BS_DEFPUSHBUTTON , 0 , 290 , 50, 50,
hWnd,(HMENU)BTN_PUSH_TALK, GetModuleHandle(NULL), NULL);
Handler (or at least what matters) :
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)
{
bool asd;
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case BTN_PUSH_TALK:
switch (HIWORD(wParam))
{
case BN_UNPUSHED:
if (connected && inputChoiceStr == "Push To Talk") {
tplug->setDuck(false);
}
break;
case BN_PUSHED:
if (connected && inputChoiceStr == "Push To Talk") {
tplug->setDuck(true);
}
break;
}
break;
I expected once i clicked and held down the button , that the BN_PUSHED case would be entered, however it is not.
On letting go, I expect the BN_UNPUSHED case to be entered, but this was not the case either.
case BTN_PUSH_TALK is reached, meaning the button is identifiable, however the switch case within this block of code is never reached.
Buttons send WM_COMMAND on click. To achieve a push/release notification you must subclass the button class (SetWindowLongPtr() with GWLP_WNDPROC) and then handle WM_LBUTTONDOWN and WM_LBUTTONUP in your new Window Proc.
If I'm reading the question right, your goal is to get notifications when a standard push button is initially pushed by the user, whereas standard notification behavior of buttons only posts WM_COMMANDs on "clicks" where a click is the whole mouse down plus mouse up sequence.
Historically in order to get the BN_PUSHED and BN_UNPUSHED notifications in your WM_COMMAND handler you had to use the BS_NOTIFY window style when creating the button. However, if you read the documentation for BN_PUSHED or BN_UNPUSHED you will see
This notification code is provided only for compatibility with 16-bit versions of Windows earlier than version 3.0. Applications should use the BS_OWNERDRAW button style and the DRAWITEMSTRUCT structure for this task.
These were very old notifications that from what I can tell are not just deprecated but no longer even supported. You can do, however, as the documentation suggests: use an owner drawn button i.e. a button created with the BS_OWNERDRAW style.
This turns out to be more difficult than just creating the button with BS_NOTIFY turned on, because the button will no longer perform default painting by itself. Given this added chore, I'd recommend not doing it this way unless you want to custom paint your buttons anyway -- unless you happen to want some nonstandard visual look-and-feel for these buttons as well as nonstandard notification behavior. Otherwise, I would probably just do Win32 subclassing as someone else suggested to trap WM_LBUTTONDOWN etc. and then call the standard button WNDPROC after doing some action on the events i cared about.
Anyway the minimal owner drawn button that reports button down and button up events is like the following. (I post the button events as custom messages but you could do whatever you wish there)
#include <windows.h>
#define BTN_ID 101
#define WM_PUSHBUTTONDOWN WM_APP + 1
#define WM_PUSHBUTTONUP WM_APP + 2
HINSTANCE g_instance = 0;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
g_instance = hInstance;
MSG msg = { 0 };
WNDCLASS wc = { 0 };
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_BACKGROUND);
wc.lpszClassName = L"owner_draw_btn";
if (!RegisterClass(&wc))
return -1;
if (!CreateWindow(wc.lpszClassName, L"foobar", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 640, 480, 0, 0, hInstance, NULL))
return -1;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT HandleDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
auto* dis = reinterpret_cast<DRAWITEMSTRUCT*>(lParam);
if (dis->CtlType != ODT_BUTTON)
return 0;
auto style = (dis->itemState & ODS_SELECTED) ?
DFCS_BUTTONPUSH | DFCS_PUSHED :
DFCS_BUTTONPUSH;
auto rect = &dis->rcItem;
DrawFrameControl(dis->hDC, rect, DFC_BUTTON, style);
TCHAR text[512];
auto n = GetWindowText(dis->hwndItem, text, 512);
DrawText(dis->hDC, text, n, rect, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
if (dis->itemAction == ODA_SELECT) {
PostMessage(
hWnd,
(dis->itemState & ODS_SELECTED) ? WM_PUSHBUTTONDOWN : WM_PUSHBUTTONUP,
dis->CtlID,
0
);
}
return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
CreateWindow(
L"button", L"foobar",
BS_OWNERDRAW | WS_CHILD | WS_VISIBLE,
10, 10, 150, 35, hWnd,
(HMENU) BTN_ID,
g_instance,
0
);
return 0;
case WM_DRAWITEM:
return HandleDrawItem(hWnd, wParam, lParam);
case WM_PUSHBUTTONDOWN:
OutputDebugString(L"Button down event\n");
break;
case WM_PUSHBUTTONUP:
OutputDebugString(L"Button up event\n");
break;
case WM_CLOSE:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
The code below, sends a WM_CHANGEUISTATE to the window procedure itself, with the arguments:
LOWORD(wParam) = UIS_CLEAR
HIWORD(wParam) = UISF_HIDEACCEL
lParam = 0x00000000
when the window client area is left clicked with the mouse.
According to this blog by Raymond Chen this should make the mnemonics in the System menu to be shown, when the menu is accessed with the mouse. The paragraph below was extracted from this article:
Clearing a flag shows the corresponding indicator. For example, if you
have a UIS_CLEAR for UISF_HIDEĀFOCUS, that means that you want to show
focus indicators.
In my case, I have a UIS_CLEAR for UISF_HIDEACCEL, meaning that I want to show the menu accelerators.
If you run the code below and click with the mouse left button on the app client area, you should make the accelerators visible in the System menu, even when this menu is accessed with the mouse. But that doesn't happen, i.e., if you activate the system menu with a left click on the window's icon, or with a right click on the window's title bar, the mnemonics in the System menu will not be shown. What am I missing?
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pszCmdLine, int nCmdShow)
{
WNDCLASSEX wndclassx;
wchar_t szAppName[] = L"WM_CHANGEUISTATE";
wndclassx.cbSize = sizeof(WNDCLASSEX);
wndclassx.style = CS_HREDRAW | CS_VREDRAW;
wndclassx.lpfnWndProc = WndProc;
wndclassx.cbClsExtra = 0;
wndclassx.cbWndExtra = 0;
wndclassx.hInstance = hInstance;
wndclassx.hIcon = 0;
wndclassx.hCursor = LoadCursor(0, IDC_ARROW);
wndclassx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndclassx.lpszClassName = szAppName;
wndclassx.lpszMenuName = nullptr;
wndclassx.hIconSm = 0;
if (!RegisterClassEx(&wndclassx)) return 0;
HWND hWnd = CreateWindow(szAppName, szAppName, WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, hInstance, 0);
ShowWindow(hWnd, SW_MAXIMIZE);
UpdateWindow(hWnd);
MSG msg;
while (GetMessage(&msg, 0, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_LBUTTONUP:
{
BOOL b;
// Note that in my system (Windows 10) I have:
//
// Control Panel > Ease of Access > Ease of Access Center > Make the keyboard easier
//
// and the option "Underline keyboard shortcuts and access keys" unmarked (the default). Therefore, the value
// returned in b below will be 0x00000000 (FALSE).
SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &b, 0);
// If b = FALSE, the SendMessage() below should underline the accelerators in the System menu, when this menu is
// accessed with the mouse. But that doesn't work. Why?
if( !b ) SendMessage(hwnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_CLEAR, UISF_HIDEACCEL), NULL);
}
break;
// Setting a break in the return below, one can see that WM_CHANGEUISTATE message is being sent to the window and
// passed to DefWindowProc().
case WM_CHANGEUISTATE:
return DefWindowProc(hwnd, message, wParam, lParam);
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
This seems like a bug/oversight in Windows. DefWindowProc does not send WM_QUERYUISTATE before displaying the system menu. The menu implementation checks the last input event directly and if it was from a keyboard it displays the underlines.
I tried sending and posting WM_CHANGEUISTATE and WM_UPDATEUISTATE in response to WM_ENTERMENULOOP, WM_INITMENUPOPUP, WM_NCRBUTTONDOWN and WM_SYSCOMMAND without any luck.
The only workaround I was able to come up with is to hack the HTSYSMENU/icon menu by changing SC_MOUSEMENU to SC_KEYMENU:
case WM_SYSCOMMAND:
if ((wParam & 0xFFF0) == SC_MOUSEMENU)
{
return SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, ' ');
}
return DefWindowProc(hwnd, message, wParam, lParam);
SC_KEYMENU has special handling in DefWindowProc and forces underlines if applicable.
This does not handle right-click on the icon, caption nor task bar...
I have set the entry point to WinMain but when I run the app it starts and doesn't display, I then have to shut it with task manager. Here's the code upto WinMain() :
#include <Windows.h>
// forward declarations
LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
// The entry point into a windows program
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int windowStyle )
{
....
I'm not experienced in C++, and I don't know what this is doing, except making my exe smaller, which is what I'm trying to achieve.
edit : What I'm trying to do is create a very small window exe to understand how demo coder's work. So I'm thinking of creating a small c++ window app that provides a window handle to which I can attach SlimDX (if i can statically link the final c++ dll to a C# app, but I'm not there yet) I have my BasicWindow.exe down to 6,656 bytes. So I'm experimenting with anything I can find to get that size down to <3k.
[2012.Jan.10] Well I've had some success by rebuilding minicrt (available from http://www.benshoof.org/blog/small-programs/) under VS2010 and adding it as an additional dependency. I couldn't Ignore All Default Libraries as suggested, but I now have a Windowed application with an exe size of 4,096 bytes. I'd say that's some significant success. I'm within striking distance now. Every reduction from here on in, is more room for SlimDX. I'm pretty happy considering the only c++ apps I've ever written are console apps and a basic window :) I've been lucky I know !
A typical application should not mess up with Entry point setting of linker. Entry point should be set on a function included in the standard runtime library (which is wWinMainCRTStartup for unicode application for windows subsystem). This function does stuff like the proper initialization of CRT and creation of global objects. By rerouting entry point to your WinMain you will get undefined behavior unless you know precisely what you are doing and somehow implementing CRT initialization in your own WinMain. In my opinion the resulting size decrease will be negliable and the whole affair is hardly worth the risk.
When you set the entry point to WinMain, Windows doesn't give your program a console window, because WinMain is for programs with GUIs that don't need a console window. Your program is indeed running, though with no GUI you don't see anything happen.
I had the same issue. I wasn't satisfied with the answer marked here. So, I started digging in more and found that WS_VISIBLE was missing from CreateWindowEx function.
WS_OVERLAPPEDWINDOW with WS_VISIBLE makes the code work perfectly.
And regarding the file size, I was able to bring down the size of the code for x64 build to 3,072 bytes 1,600 bytes using Visual Studio Community Edition 2013 running on Windows 7 Ultimate SP1 x64. All this, without using minicrt.lib, you mentioned above.
Here's a screenshot of the executable properties.
For reference, here is the full program :
#include <Windows.h>
// forward declarations
LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); // WindowProcedure function
// The entry point into a windows program
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int windowStyle )
{
// create and register a local window class > exit on failure
WNDCLASSEX wcx;
HINSTANCE zhInstance = GetModuleHandle(NULL);
wcx.cbSize = sizeof(WNDCLASSEX); // size of structure
wcx.style = CS_HREDRAW | CS_VREDRAW; // redraw if size changes
wcx.lpfnWndProc = WndProc; // window procedure
wcx.cbClsExtra = 0; // no extra class memory
wcx.cbWndExtra = 0; // no extra windows memory
wcx.hInstance = zhInstance; // handle to instance (owner)
wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION); // predefined icon
wcx.hCursor = LoadCursor (NULL, IDC_ARROW); // predefined arrow
wcx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);//(HBRUSH) (COLOR_WINDOW + 1);// white background brush
wcx.lpszMenuName = NULL; // name of menu resource
wcx.lpszClassName = TEXT("BasicWindow"); // name of window class
wcx.hIconSm = (HICON)LoadImage(zhInstance, // small class icon
MAKEINTRESOURCE(5),
IMAGE_ICON,
GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON),
LR_DEFAULTCOLOR);
if (!RegisterClassEx(&wcx))
{
MessageBoxA(0, "Error registering window class!","Error",MB_ICONSTOP | MB_OK);
DWORD result = GetLastError();
return 0;
}
// create window > exit on failure
HWND hwnd; // the window handle
hwnd = CreateWindowEx(
WS_EX_STATICEDGE,
wcx.lpszClassName,
TEXT("Basic Window"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
320,
240,
NULL,
NULL,
zhInstance,
NULL);
if (hwnd == NULL)
{
MessageBoxA(0,"Error creating window!","Error",MB_ICONSTOP | MB_OK);
return 0;
}
// show window
ShowWindow(hwnd, SW_MAXIMIZE);
// send a WM_PAINT message to the window
UpdateWindow(hwnd);
// enter message loop, break out of loop if there is an error (result = -1)
MSG msg; // for storing the windows messages
int status; // for storing the status of the windows message service where -1 = error, 0 = WM_QUIT, n = any other message)
while ((status = GetMessage(&msg,(HWND) NULL,0,0)) != 0 && status != -1)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
UNREFERENCED_PARAMETER(lpCmdLine);
}
LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_CLOSE:
DestroyWindow(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
default:
return DefWindowProc(hWnd,message,wParam,lParam);
}
return 0;
}
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);
}