Функция CreateWindowEx не работает, но GetLastError() возвращает ERROR_SUCCESS
Я пытаюсь создать простое окно с C/С++, используя собственную систему очереди сообщений Windows (без .NET). Я выполнил учебник MSDN и написал базовый код, который создает пустое окно:
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 );
}
Когда я запускаю/отлаживаю программу, CreateWindowEx возвращает 0, что означает, что она не удалась. Это вызывает сообщение об ошибке "Oh shi- [код ошибки]". Наиболее запутанной частью является то, что сообщение об ошибке выводится на консоль:
Oh shi- 0
Код ошибки, возвращаемый GetLastError(), равен 0, что является ERROR_SUCCESS!
У меня полная потеря; что происходит? Я так запутался...
P.S.
Я использую Visual С++ Express 2010 на 32-разрядной версии Windows 7. Я написал процедуру Windows в другом месте, но она просто возвращает 0 для всех случаев. Если, однако, кто-то захочет это увидеть, я с удовольствием покажу его.
Я изменил набор символов проекта по умолчанию моего проекта Visual С++ на "Не задано". Мне не нужно приписывать L моим вещам.
Изменить: добавлено wnd.hInstance = hinst;
Изменить: удалить ненужный (WNDPROC) листинг
Изменить: добавлена проверка ошибок для RegisterClassEx
Оказывается, проблема была в Visual С++ Express (или, по крайней мере, не с самим кодом). Я скопировал код в другой проект, и он сработал.
Ответы
Ответ 1
wnd.lpfnWndProc = (WNDPROC) MainWProc;
Мы не видим реальной причины, по которой вам нужно использовать актерский состав, но это очень подозрительно. Windows возвращает 0 из GetLastError(), если не видит ничего плохого. Что может произойти, если оконная процедура нарушена. Как этот:
LRESULT CALLBACK MainWProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return 0;
}
Windows отправляет сообщение WM_NCCREATE, чтобы попросить создать окно. Если это сообщение не обрабатывается, окна не будет. И никаких ошибок. Fix:
LRESULT CALLBACK MainWProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
При необходимости измените настройку окна. Просто убедитесь, что DefWindowProc() вызывается для каждого сообщения, которое вы не хотите обрабатывать самостоятельно. И держите Петцольда под рукой, чтобы избежать простых ошибок. И потерять бросок.
Ответ 2
Все современные версии Windows используют Unicode внутри и по умолчанию проекты Visual Studio #define _UNICODE
/UNICODE
, что заставляет ваше приложение ссылаться на Unicode-версии заголовков Windows.
Однако, когда вы компилируете приложение как Unicode, типы символов (и, следовательно, "строка" ) различаются. Вместо char
они теперь wchar_t
. Это означает, что вы должны явно объявлять свои строковые литералы длинными строками, предварительно привязывая их к L
.
В качестве альтернативы заголовки Windows скрывают все эти макросы, но это больше не нужно, потому что Windows долгое время была Unicode и это вряд ли изменилось.
Кроме того, вам не хватает нескольких элементов при инициализации структуры WNDCLASSEX
, как и элемент hInstance
. Все это должно быть настроено идеально, иначе все провалится. Кроме того, функции RegisterClass(Ex)
и CreateWindow(Ex)
должны передаваться с теми же строковыми значениями, которые соответствуют имени класса окна, иначе они предполагают, что вы говорите о двух разных вещах. Опечатки не прощены!
Я настоятельно рекомендую вам использовать мастера Visual Studio для создания пустого (но работающего!) шаблона проекта.
Правильный код шаблона выглядит примерно так:
#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;
}
Ответ 3
В моем случае мне пришлось вручную обрабатывать WNC_NCCREATE. DefWindowProc возвращает ноль для WNC_NCCREATE, я исправил это с помощью:
LRESULT CALLBACK MainWProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WNC_NCCREATE)
return true;
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
Ответ 4
У меня была та же проблема.
В моем случае это просто, что я использовал визуальный sutdio без прав администратора.
И я обнаружил, что в этом случае я не могу отлаживать свое приложение.
В режиме отладки без права администратора CreateWindowEx возвращает null с результатом кода ошибки в 0, как вы.
Но если вы заходите в свой каталог сборки, вы можете использовать свое приложение (не в режиме отладки).
Так что, если вы это делаете, просто запустите визулу-студию с правом администратора и ее завершением.
Я думаю, что есть почти способ использовать режим отладки Visual Studio с правом администратора без запуска визуального stuido с паролем администратора каждый раз. Я не знаю, как это сделать, но я думаю, что это возможно.