WINAPI-我想让消息泵在单独的线程中进行 [英] WINAPI - I would like to have the message pump ongoing in a separate thread

查看:66
本文介绍了WINAPI-我想让消息泵在单独的线程中进行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Windows API中,创建窗口需要消息泵来保持窗口的运行和更新.现在,编写消息泵包含一个while循环,该循环控制着整个程序,不允许执行其他操作,这是一个大问题.

In the Windows API, making a window requires a message pump to keep the window running and updated. Now, writing a message pump consists of a while loop, which dominates the entire program, not allowing other things to be executed, which is a big problem.

考虑我的代码,这是我称为CFrame.h的头文件(因为在内部创建了名为CFrame的类,该类打算在Java中模仿JFrame).换句话说,我希望可以创建多个CFrame实例,以便显示多个窗口,并且消息循环不会在创建第一个窗口之后停止这些窗口.

Consider my code, which is a header file that I called CFrame.h (because inside I made a class called CFrame which is meant to mimick JFrame in Java). In other words, I want it to be possible to create multiple instances of CFrame so that multiple windows show up and the message loop will not stop the windows after the first one from being created.

我为函数ThreadExecution()创建了一个新线程,由于某种原因该程序刚刚终止,为什么?

I made a new thread for the function ThreadExecution(), for some reason the program just terminates, why?

#define UNICODE

#include <windows.h>

const wchar_t CLASS_NAME[] = L"Window Class";
static int nWindows = 0; // Number of ongoing windows 

class Size { // Size of the window
private:
    int width;
    int height;
    public:
    Size(int width, int height) :width(width), height(height) {}
    int getWidth() {
        return width;
    }
    int getHeight() {
        return height;
    }
};

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
    case WM_DESTROY: nWindows--; break;
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

void RegisterDetails(HINSTANCE hInstance) { // Registers WNDCLASS
    WNDCLASS wc = {};
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;
    RegisterClass(&wc);
}

void startMessageLoop() { // This is the message loop which must be in a    separate thread
    MSG msg;
    while (nWindows) {
        GetMessage(&msg, NULL, 0, 0);
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

HWND CreateAWindow(LPCWSTR title, Size size, HINSTANCE hInstance) {
    if (nWindows == 0) { // The WNDCLASS only needs to be registered once
        RegisterDetails(hInstance);
    }
    HWND hwnd = CreateWindowEx(0, CLASS_NAME, title, WS_OVERLAPPEDWINDOW,     CW_USEDEFAULT, CW_USEDEFAULT, size.getWidth(), size.getHeight(), NULL, NULL, hInstance, NULL);
    ShowWindow(hwnd, 5);
    return hwnd;
}


void ThreadExecution(HWND hwnd, LPCWSTR title, Size size, HINSTANCE hInstance) {
    hwnd = CreateAWindow(title, size, hInstance);
    nWindows++;
    if (nWindows == 1) // If only one window has been created, the message loop will be called
    {
        startMessageLoop();
    }
}

class CFrame {

private:
    HINSTANCE hInstance;
    Size size;
    HWND hwnd;

public:
    CFrame() { 
    }

    CFrame(LPCWSTR title, Size size, HINSTANCE hInstance) :size(size), hInstance(hInstance) 
    { 
        std::thread t1(ThreadExecution, hwnd, title, size, hInstance);
        t1.detach();
    }
};

推荐答案

现在,编写消息泵包含一个while循环,该循环控制着整个程序,不允许执行其他操作

Now, writing a message pump consists of a while loop, which dominates the entire program, not allowing other things to be executed

传统的 消息循环可能会以这种方式工作,但是肯定可以编写一个消息循环,在消息之间执行其他操作.还有谁说您不能自己使用消息来做事.您可能有一个阻塞调用线程的循环,但是您可以控制该循环在每次迭代中实际执行的操作,因此它可以代表其调用线程执行操作.不需要处理消息.

A traditional message loop might work that way, but it is certainly possible to write a message loop that can do other things in between messages. And who says that you can't use messages themselves to do things. You might have a loop that blocks the calling thread, but you control what that loop actually does on each iteration, so it can do things on behalf of its calling thread. It is not required to only process messages.

换句话说,我希望可以创建多个CFrame实例,以便显示多个窗口,并且消息循环不会在创建第一个窗口后停止这些窗口.

In other words, I want it to be possible to create multiple instances of CFrame so that multiple windows show up and the message loop will not stop the windows after the first one from being created.

任何一种消息循环都可以在同一线程中处理多个窗口.

Any kind of message loop can handle multiple windows in the same thread just fine.

我为函数ThreadExecution()创建了一个新线程,由于某种原因该程序刚刚终止,为什么?

I made a new thread for the function ThreadExecution(), for some reason the program just terminates, why?

因为您的窗口管理是完全错误的.您对Windows和线程如何协同工作有基本的误解.您不会为每个窗口创建单独的线程(尽管从技术上讲可以,但是这样做很浪费).您创建一个线程(或仅使用您的主线程)来创建多个窗口,然后使用单个消息循环为其提供服务.

Because your window management is completely wrong. You have a fundamental misunderstanding of how windows and threads work together. You don't create a separate thread for each window (though you technically can, but it is wasteful to do so). You create one thread (or just use your main thread) to create multiple windows, and then use a single message loop to service them.

尝试更多类似的方法:

#ifndef CFrameH
#define CFrameH

#include <windows.h>

class Size { // Size of the window
private:
    int width;
    int height;
public:
    Size(int width, int height);

    int getWidth() const;
    int getHeight() const;
};

class CFrame {
private:
    HINSTANCE hInstance;
    Size size;
    HWND hwnd;

    virtual LRESULT WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam);

    static void RegisterDetails(HINSTANCE);
    static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

public:
    CFrame(LPCWSTR title, Size size, HINSTANCE hInstance);
    ~CFrame();
};

#endif

CFrame.cpp

CFrame.cpp

#include "CFrame.h"

static LPCWSTR CLASS_NAME = L"Window Class";

Size::Size(int width, int height)
    : width(width), height(height)
{
}

int Size::getWidth() const {
    return width;
}

int Size::getHeight() const {
    return height;
}

CFrame::CFrame(LPCWSTR title, Size size, HINSTANCE hInstance)
    : size(size), hInstance(hInstance) 
{ 
    RegisterDetails(hInstance);

    hwnd = CreateWindowExW(0, CLASS_NAME, title, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, size.getWidth(), size.getHeight(), NULL, NULL, hInstance, this);
    if (hwnd) {
        ShowWindow(hwnd, SW_SHOW);
    }
}

CFrame::~CFrame()
{ 
    if (hwnd)
        DestroyWindow(hwnd);
}

void CFrame::RegisterDetails(HINSTANCE hInstance) { // Registers WNDCLASS
    WNDCLASSW wc = {};
    BOOL bRegistered = GetClassInfoW(hInstance, CLASS_NAME, &wc);
    if ((!bRegisterd) || (wc.lpfnWndProc != &WindowProc)) {
        if (bRegistered) {
          UnregisterClassW(CLASS_NAME, hInstance);
        }
        wc.lpfnWndProc = &WindowProc;
        wc.hInstance = hInstance;
        wc.lpszClassName = CLASS_NAME;
        RegisterClassW(&wc);
    }
}

LRESULT CALLBACK CFrame::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    CFrame *pThis;

    if (uMsg == WM_CREATE) {
        pThis = (CFrame*) ((CREATESTRUCT*)lParam)->lpCreateParams;
        SetWindowLongPtr(hwnd, GWL_USERDATA, (LONG_PTR) pThis);
    } else {
        pThis = (CFrame*) GetWindowLongPtr(hwnd, GWL_USERDATA);
    }

    if (pThis)
        return pThis->WndProc(uMsg, wParam, lParam);

    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}        

LRESULT CFrame::WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    if (uMsg == WM_NCDESTROY) {
        hwnd = NULL;
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

然后您可以在线程中执行以下操作:

Then you can do things like this in a thread:

void ThreadExecution(HINSTANCE hInstance) {
    CFrame frame1(L"frame1", Size(10, 10), hInstance);
    CFrame frame2(L"frame2", Size(20, 20), hInstance);

    MSG msg;
    while (...) {
        if (GetMessage(&msg, NULL, 0, 0)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
}

void ThreadExecution(HINSTANCE hInstance) {
    CFrame frame1(L"frame1", Size(10, 10), hInstance);
    CFrame frame2(L"frame2", Size(20, 20), hInstance);

    MSG msg;
    while (...) {
        if (PeekMessage(&msg, NULL, 0, 0)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else {
            // do something else
        }
    }
}

void ThreadExecution(HINSTANCE hInstance) {
    CFrame frame1(L"frame1", Size(10, 10), hInstance);
    CFrame frame2(L"frame2", Size(20, 20), hInstance);

    // array of event/IO objects that are signaled
    // when things needs to be done...
    HANDLE hObjects[...];
    DWORD dwNumObjects = ...;
    ...

    MSG msg;
    while (...) {
        DWORD dwRet = MsgWaitForMultipleObjects(dwNumObjects, hObjects, FALSE, INFINITE, QS_ALLINPUT);
        if ((dwRet >= WAIT_OBJECT_0) && (dwRet < (WAIT_OBJECT_0+dwNumObjects))) {
            dwRet -= WAIT_OBJECT_0;
            // do something with hObjects[dwRet] ...
        }
        else if (dwRet == (WAIT_OBJECT_0+dwNumObjects)) {
            while (PeekMessage(&msg, NULL, 0, 0)) {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
        ...
    }
}

依此类推...

这篇关于WINAPI-我想让消息泵在单独的线程中进行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆