无法建立视窗

时间:2019-04-23 18:48:26

标签: c++ windows winapi

我正在尝试创建一个简单的窗口,但是由于某种原因它无法正常工作。在过去的几个月中,我实际上已经尝试过了。甚至使用了一些旧代码,无论出于何种原因,它都根本无法运行。

实际上,registerclass函数从不返回任何东西,但这也发生在过去,但是我仍然能够创建一个窗口。这次没有任何作用

我尝试过:

  • 为WNDCLASS的所有成员提供有效值
  • 运行调试/发布
  • 以管理员身份运行
  • 使用GetLastError(总是返回0)
  • 提供不同的类名,因为我知道某些类由于某些原因不起作用,还有窗口名

这是我的代码:

WNDCLASSEXW lpClass = WNDCLASSEXW{ 0x00 };
lpClass.cbSize = sizeof(decltype(lpClass));
lpClass.style = (CS_HREDRAW | CS_VREDRAW);
lpClass.lpfnWndProc = ScreenProcess;
lpClass.hInstance = GetModuleHandleW(nullptr);
lpClass.lpszClassName = L"__TEST__";
lpClass.hbrBackground = CreateSolidBrush(RGB(0, 0, 0));

RegisterClassExW(&lpClass);

if (HWND hwnd = CreateWindowExW(WS_EX_APPWINDOW, lpClass.lpszClassName, L"abc", WS_POPUP,
    CW_USEDEFAULT, CW_USEDEFAULT, 500, 500, nullptr, nullptr, lpClass.hInstance, nullptr))
{
    ShowWindow(hwnd, SW_SHOW);
    UpdateWindow(hwnd);
}

printf("%d", GetLastError());

for (;;) {};

3 个答案:

答案 0 :(得分:2)

您是否将窗口过程定义为这样?

LRESULT CALLBACK ScreenProcess(
    HWND hWnd,
    UINT msg,
    WPARAM wParam,
    LPARAM lParam
)
{
    switch (msg)
    {
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hWnd, msg, wParam, lParam);
    }

    return 0;
}

而不是无限循环,将其放置:

MSG msg{0};

while(GetMessage(&msg, nullptr, 0, 0)){
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

此外,cbSize中的WNDCLASSEXW应该是sizeof(WNDCLASSEXW)。同时将CreateSolidBrush(RGB(0, 0, 0));更改为GetStockObject(BLACK_BRUSH);

希望这会有所帮助!

答案 1 :(得分:1)

这是在VS2017中运行的代码版本(使用C ++ 17)。我添加了调试打印,以便您可以看到ScreenProcess()收到鼠标移动等信息。我还添加了一个类(WindowsClassRegistrator),用于处理您分配的资源之一,以展示如何扩展资源。现有的C结构可自动处理资源释放。

我在常见的Windows消息及其宏名称之间添加了一个映射,以使您更容易了解您实际进入WndProc的内容。当您单击应用程序上的关闭按钮(在任务栏上)时,将收集并显示未知的Windows消息,以便您可以随时扩展要处理/显示的消息。

我还添加了断言函数模板,用于抛出带有适当错误消息的异常,您可以在所有WinAPI函数中使用它们,这些函数可以轻松地检查它们是否成功。

#include "pch.h" // if you use precompiled headers
#include <Windows.h>
#include <Windowsx.h> // GET_X_LPARAM, GET_Y_LPARAM
#include <Olectl.h> // OCM_BASE
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <stdexcept>
#include <map>
#include <unordered_set>

// --- bug hunting support functions start ---
std::string GetLastErrorString() {
    DWORD le = GetLastError();
    LPSTR  lpBuffer = nullptr;

    if (FormatMessageA(
        FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        0, le, 0,
        reinterpret_cast<LPSTR>(&lpBuffer),
        0, NULL
    ))
    {
        std::string rv(lpBuffer);
        LocalFree(lpBuffer);
        return rv;
    }
    else return std::to_string(le);
}

struct win_error : public std::runtime_error {
    win_error(const std::string& prefix) :
        std::runtime_error(prefix + ": " + GetLastErrorString())
    {}
};

// assert that a function does NOT return a specific value
template<typename T>
inline T AssertNEQ(T value, const char* funcname, T got_value) {
    if (value == got_value) throw win_error(funcname);
    return got_value;
}

// assert that a function DOES return a specific value
template<typename T>
inline T AssertEQ(T value, const char* funcname, T got_value) {
    if (value != got_value) throw win_error(funcname);
    return got_value;
}
// --- bug hunting support functions end ---

class WindowsClassRegistrator : public WNDCLASSEXW {
    ATOM wca;

public:
    WindowsClassRegistrator(WNDPROC lpfnWndProc) :
        WNDCLASSEXW{ 0 }, wca{}
    {
        this->cbSize = sizeof(WNDCLASSEXW);
        this->style = CS_SAVEBITS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
        this->lpfnWndProc = lpfnWndProc;
        this->hInstance =
            AssertNEQ<HMODULE>(NULL, "GetModuleHandleW", GetModuleHandleW(nullptr));
        this->lpszClassName = L"__TEST__";
        this->hbrBackground = static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
        this->wca = 
            AssertNEQ<ATOM>(NULL, "RegisterClassExW", RegisterClassExW(this));
    }
    WindowsClassRegistrator(const WindowsClassRegistrator&) = delete;
    WindowsClassRegistrator(WindowsClassRegistrator&&) = delete;
    WindowsClassRegistrator& operator=(const WindowsClassRegistrator&) = delete;
    WindowsClassRegistrator& operator=(WindowsClassRegistrator&&) = delete;
    ~WindowsClassRegistrator() {
        AssertNEQ<BOOL>(FALSE,
            "UnregisterClassW", UnregisterClassW(GetAtomAsStr(), this->hInstance));
    }

    inline LPCWSTR GetAtomAsStr() const noexcept {
        return reinterpret_cast<LPCWSTR>(this->wca);
    }
    inline HINSTANCE GetInstance() const noexcept {
        return this->hInstance;
    }
    inline LPCWSTR GetClassName() const noexcept {
        return this->lpszClassName;
    }
};

std::multimap<UINT, std::string> messages = {
    {WM_NULL, "WM_NULL"},
    {WM_CREATE, "WM_CREATE"},
    {WM_DESTROY, "WM_DESTROY"},
    {WM_MOVE, "WM_MOVE"},
    {WM_SIZE, "WM_SIZE"},
    {WM_ACTIVATE, "WM_ACTIVATE"},
    {WM_SETFOCUS, "WM_SETFOCUS"},
    {WM_KILLFOCUS, "WM_KILLFOCUS"},
    {WM_PAINT, "WM_PAINT"},
    {WM_CLOSE, "WM_CLOSE"},
    {WM_QUIT, "WM_QUIT"},
    {WM_ERASEBKGND, "WM_ERASEBKGND"},
    {WM_SHOWWINDOW, "WM_SHOWWINDOW"},
    {WM_ACTIVATEAPP, "WM_ACTIVATEAPP"},
    {WM_CANCELMODE, "WM_CANCELMODE"},
    {WM_SETCURSOR, "WM_SETCURSOR"},
    {WM_MOUSEACTIVATE, "WM_MOUSEACTIVATE"},
    {WM_VKEYTOITEM, "WM_VKEYTOITEM"},
    {WM_CHARTOITEM, "WM_CHARTOITEM"},
    {WM_WINDOWPOSCHANGING, "WM_WINDOWPOSCHANGING"},
    {WM_WINDOWPOSCHANGED, "WM_WINDOWPOSCHANGED"},
    {SPI_SETDRAGHEIGHT, "SPI_SETDRAGHEIGHT"},
    {WM_HELP, "WM_HELP"},
    {WM_CONTEXTMENU, "WM_CONTEXTMENU"},
    {WM_GETICON, "WM_GETICON"},
    {WM_NCCREATE, "WM_NCCREATE"},
    {WM_NCDESTROY, "WM_NCDESTROY"},
    {WM_NCCALCSIZE, "WM_NCCALCSIZE"},
    {WM_NCHITTEST, "WM_NCHITTEST"},
    {WM_NCPAINT, "WM_NCPAINT"},
    {WM_NCACTIVATE, "WM_NCACTIVATE"},
    {SPI_GETDOCKMOVING, "SPI_GETDOCKMOVING"},
    {WM_KEYDOWN, "WM_KEYDOWN"},
    {WM_KEYUP, "WM_KEYUP"},
    {WM_CHAR, "WM_CHAR"},
    {WM_SYSKEYDOWN, "WM_SYSKEYDOWN"},
    {WM_SYSKEYUP, "WM_SYSKEYUP"},
    {WM_SYSCHAR, "WM_SYSCHAR"},
    {WM_SYSCOMMAND, "WM_SYSCOMMAND"},
    {WM_MOUSEMOVE, "WM_MOUSEMOVE"},
    {WM_LBUTTONDOWN, "WM_LBUTTONDOWN"},
    {WM_LBUTTONUP, "WM_LBUTTONUP"},
    {WM_LBUTTONDBLCLK, "WM_LBUTTONDBLCLK"},
    {WM_RBUTTONDOWN, "WM_RBUTTONDOWN"},
    {WM_RBUTTONUP, "WM_RBUTTONUP"},
    {WM_RBUTTONDBLCLK, "WM_RBUTTONDBLCLK"},
    {WM_MBUTTONDOWN, "WM_MBUTTONDOWN"},
    {WM_MBUTTONUP, "WM_MBUTTONUP"},
    {WM_MBUTTONDBLCLK, "WM_MBUTTONDBLCLK"},
    {WM_MOUSEWHEEL, "WM_MOUSEWHEEL"},
    {WM_XBUTTONDOWN, "WM_XBUTTONDOWN"},
    {WM_XBUTTONUP, "WM_XBUTTONUP"},
    {WM_IME_SETCONTEXT, "WM_IME_SETCONTEXT"},
    {WM_IME_NOTIFY, "WM_IME_NOTIFY"},
    {WM_HOTKEY, "WM_HOTKEY"},
    {0x0313, ": https://stackoverflow.com/questions/10430377/winapi-undocumented-windows-message-0x0313-stable"},
    {WM_PRINT, "WM_PRINT"},
    {WM_APPCOMMAND, "WM_APPCOMMAND"},
};

std::unordered_set<UINT> unmapped_messages;

std::map<WPARAM, std::string> mk_down = {
    {MK_CONTROL, "MK_CONTROL"},
    {MK_LBUTTON,"MK_LBUTTON"},
    {MK_MBUTTON,"MK_MBUTTON"},
    {MK_RBUTTON,"MK_RBUTTON"},
    {MK_SHIFT,"MK_SHIFT"},
    {MK_XBUTTON1,"MK_XBUTTON1"},
    {MK_XBUTTON2,"MK_XBUTTON2"}
};

constexpr int colw = 40;
std::string message_maker(const char* macro, UINT uMsg, UINT offset) {
    std::stringstream ss;
    ss << macro << " + " << std::hex << (uMsg - offset) << " (" << uMsg << ")";
    return ss.str();
}

inline void DisplayMsg(UINT uMsg, WPARAM wParam, LPARAM lParam) {
    std::string message;

    if (uMsg < WM_USER) {
        // there may be duplicate macros for some messages, so show all of them
        auto[rangefirst, rangelast] = messages.equal_range(uMsg);

        if (rangefirst == rangelast) {
            // unmapped message found, store it
            unmapped_messages.emplace(uMsg);
            rangefirst = messages.emplace(uMsg, ": " + std::to_string(uMsg) + " -- UNMAPPED MESSAGE");
            rangelast = rangefirst;
            ++rangelast;
        }
        message = rangefirst->second;
        while (++rangefirst != rangelast) message += " " + rangefirst->second;
    }
    else {
        // https://docs.microsoft.com/en-us/windows/desktop/winmsg/ocm--base
#define REGISTERED_WINDOWS_MESSAGE_BASE (0xC000)
#define SYSRESERVED_BASE                (0x10000)

        if (uMsg < OCM__BASE)
            message = message_maker("WM_USER", uMsg, WM_USER);
        else if (uMsg < WM_APP)
            message = message_maker("(WM_USER) OCM__BASE", uMsg, OCM__BASE);
        else if (uMsg < REGISTERED_WINDOWS_MESSAGE_BASE)
            message = message_maker("WM_APP", uMsg, WM_APP);
        else if (uMsg < SYSRESERVED_BASE)
            message = message_maker("Registered Window Message", uMsg, REGISTERED_WINDOWS_MESSAGE_BASE);
        else
            message = message_maker("Reserved by the system", uMsg, SYSRESERVED_BASE);
    }
    std::cout << std::setw(colw) << std::hex << message << std::setw(18) << wParam
        << std::setw(12) << lParam << "\n";
}

LRESULT CALLBACK ScreenProcess(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    static std::string old_mouse_message;

    switch (uMsg) {
    case WM_MOUSEMOVE:
        {
            std::stringstream ss;
            std::string new_mouse_message;
            int xPos, yPos;

            xPos = GET_X_LPARAM(lParam);
            yPos = GET_Y_LPARAM(lParam);
            ss << std::setw(colw) << "WM_MOUSEMOVE" << std::dec
               << " x=" << std::setw(6) << xPos << " y=" << std::setw(6) << yPos;

            for (auto&[wp, key] : mk_down)
                if (wp&wParam) ss << " " << key;

            new_mouse_message = ss.str();

            if (new_mouse_message != old_mouse_message) {
                std::cout << new_mouse_message << "\n";
                old_mouse_message = std::move(new_mouse_message);
            }
        }
        return 0;
    case WM_NCHITTEST:
        return HTCLIENT;
    case WM_SETCURSOR:
        return TRUE;
    case WM_DESTROY:
        std::cout << std::setw(colw) << "WM_DESTROY" << " ";
        PostQuitMessage(0);
        std::cout << "PostQuitMessage() done\n";
        return 0;
    default:
        DisplayMsg(uMsg, wParam, lParam);
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
}    

int main() {
    try {
        WindowsClassRegistrator wcr(ScreenProcess);

        // use WS_VISIBLE so that you don't have to call ShowWindow()
        HWND hWnd =
            AssertNEQ<HWND>(NULL, "CreateWindowExW",
                CreateWindowExW(
                    WS_EX_APPWINDOW,
                    wcr.GetAtomAsStr(),
                    L"Title string",
                    WS_POPUP | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 
                    CW_USEDEFAULT, // x
                    CW_USEDEFAULT, // y
                    0, // width
                    0, // height
                    nullptr,
                    nullptr,
                    wcr.GetInstance(),
                    nullptr
                )
            );

        MONITORINFO mi = { sizeof(mi) }; // mi.cbSize = sizeof(mi);
        AssertNEQ<BOOL>(FALSE, "GetMonitorInfo",
            GetMonitorInfo(MonitorFromWindow(hWnd, MONITOR_DEFAULTTOPRIMARY), &mi)
        );

        AssertNEQ<BOOL>(FALSE, "SetWindowPos",
            SetWindowPos(hWnd, HWND_TOP,
                mi.rcMonitor.left, mi.rcMonitor.top,
                (mi.rcMonitor.right - mi.rcMonitor.left) / 4, // 1/4 of the screen width
                (mi.rcMonitor.bottom - mi.rcMonitor.top), // height
                SWP_NOOWNERZORDER | SWP_FRAMECHANGED)
            );

        // paint a rectangle in the window
        AssertNEQ<BOOL>(FALSE, "Rectangle", Rectangle(
            AssertNEQ<HDC>(NULL, "GetDC", GetDC(hWnd)),
            10, 10, 100, 100)
        );

        MSG uMsg;
        while (AssertNEQ<BOOL>(-1, "GetMessage", GetMessage(&uMsg, nullptr, 0, 0))) {
            TranslateMessage(&uMsg); // assertion would depend on message type
            DispatchMessage(&uMsg); // assertion would depend on message type
        }
        DisplayMsg(uMsg.message, uMsg.wParam, uMsg.lParam); // WM_QUIT

        if (unmapped_messages.size()) {
            std::cout << "\nYou have collected unmapped messages: \n";

            for (const auto&[msg, text] : messages) {
                std::cout << "/* 0x" << std::setw(4) << std::setfill('0') << msg << " */ ";
                if (unmapped_messages.count(msg) || text[0] == ':') {
                    std::cout << "{0x" << std::setw(4) << std::setfill('0') << msg;
                }
                else {
                    std::cout << "{" << text;
                }
                std::cout << ", \"" << text << "\"},\n";
            }
        }

        return static_cast<int>(uMsg.wParam);
    }
    catch (const std::exception& ex) {
        std::cerr << "Exception: " << ex.what() << "\n";
    }
    return 1;
} // the registered windows class will be unregistered here, when wcr goes out of
  // scope

答案 2 :(得分:0)

这是Visual Studio为您生成的默认Win32项目模板的修改后的单一源文件版本。我刚刚删除了与资源,加速器,菜单和“关于”框有关的内容。

#include <windows.h>

#define MAX_LOADSTRING 100

// Global Variables:
HINSTANCE hInst;                                // current instance
WCHAR szTitle[] = L"Test App";                  // The title bar text
WCHAR szWindowClass[] = L"__TestAppWindowClass__";            // the main window class name

// Forward declarations of functions included in this code module:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR    lpCmdLine,
    _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: Place code here.

    // Initialize global strings
    MyRegisterClass(hInstance);

    // Perform application initialization:
    if (!InitInstance(hInstance, nCmdShow))
    {
        return FALSE;
    }

    MSG msg;

    // Main message loop:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int)msg.wParam;
}



//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex = { 0 };

    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(NULL, IDI_APPLICATION);
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszClassName = szWindowClass;
    return RegisterClassExW(&wcex);
}

//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    hInst = hInstance; // Store instance handle in our global variable

    HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

    if (!hWnd)
    {
        return FALSE;
    }

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    return TRUE;
}

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Processes messages for the main window.
//
//  WM_COMMAND  - process the application menu
//  WM_PAINT    - Paint the main window
//  WM_DESTROY  - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        // TODO: Add any drawing code that uses hdc here...
        EndPaint(hWnd, &ps);
    }
    break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}