Функция 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 с паролем администратора каждый раз. Я не знаю, как это сделать, но я думаю, что это возможно.