Сбой функции CreateWindowEx, но GetLastError () возвращает ERROR_SUCCESS - PullRequest
11 голосов
/ 18 декабря 2011

Я пытаюсь создать простое окно с C / 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- [код ошибки]». Самым запутанным является то, что сообщение об ошибке выводится на консоль:

Ой ши- 0

Код ошибки, возвращаемый функцией GetLastError (), равен 0, что соответствует ERROR_SUCCESS!

Я в полной растерянности; что происходит? Я так запутался ...

P.S. Я использую Visual C ++ Express 2010 в Windows 7 32-разрядной версии. Я написал процедуру Windows в другом месте, но она просто возвращает 0 для всех случаев. Однако, если кто-то захочет это увидеть, я буду рад показать это.

Я изменил набор символов Project Default моего проекта Visual C ++ на «Not Set». Мне не нужно префикс L к моим вещам.

Редактировать: добавлено wnd.hInstance = hinst;

Редактировать: удалено ненужное (WNDPROC) приведение

Редактировать: добавлена ​​проверка ошибок для RegisterClassEx

Оказывается, проблема была в Visual C ++ Express (или, по крайней мере, не в самом коде). Я скопировал код в другой проект, и он работал.

Ответы [ 4 ]

37 голосов
/ 18 декабря 2011
wnd.lpfnWndProc = (WNDPROC) MainWProc;

Мы не видим реальной причины, по которой вам нужно использовать актерский состав, но он очень подозрительный. Windows возвращает 0 из GetLastError (), если не видит, что что-то идет не так. Что может произойти, если оконная процедура нарушена. Как этот:

LRESULT CALLBACK MainWProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    return 0;
}

Windows отправляет сообщение WM_NCCREATE, чтобы попросить создать окно. Если это сообщение не будет обработано, окна не будет. И без ошибок. Исправлено:

LRESULT CALLBACK MainWProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
   return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

Твик по мере необходимости для настройки окна. Просто убедитесь, что DefWindowProc () вызывается для каждого сообщения, которое вы не хотите обрабатывать самостоятельно. И держите Petzold под рукой, чтобы избежать простых ошибок. И потерять актерский состав.

2 голосов
/ 18 декабря 2011

Все современные версии 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;
}
0 голосов
/ 13 ноября 2015

В моем случае мне пришлось обрабатывать 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);
}
0 голосов
/ 04 августа 2014

У меня была такая же проблема.

В моем случае я использовал визуальный sutdio без прав администратора.И я обнаружил, что в этом случае я не могу отладить мое приложение.В режиме отладки без прав администратора CreateWindowEx возвращает ноль с кодом ошибки, равным 0, как вы.Но если вы перейдете в каталог сборки, вы можете использовать свое приложение (не в режиме отладки).Так что, если это касается и вас, просто запустите visula studio с правами администратора и все готово.

Я думаю, что есть почти способ использовать режим отладки Visual Studio с правами администратора без запуска Visual Stuido с паролем администратора каждыйвремя.я не знаю, как это сделать, но думаю, что это возможно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...