WINAPI - 我希望消息泵在一个单独的线程中进行

时间:2016-10-06 12:06:40

标签: c++ windows multithreading winapi

在Windows API中,创建窗口需要使用消息泵来保持窗口运行和更新。现在,编写消息泵包含一个while循环,它占据整个程序,不允许执行其他任务,这是一个大问题。

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

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

#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();
    }
};

4 个答案:

答案 0 :(得分:3)

在非<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:fillViewport="true"> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingTop="10dp"> <RelativeLayout android:layout_width="match_parent" android:layout_height="200dp"> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/WelcomeScreenDarker" android:id="@+id/imageBackground" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:id="@+id/themePhoto" android:clickable="true" /> </RelativeLayout> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/artWorkCounter" android:textColor="@color/searchHint" android:textSize="12sp" android:layout_marginRight="80dp" android:layout_gravity="end" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/artWorkDescriptionTitle" android:paddingTop="15dp" android:textSize="20sp" android:textStyle="bold" android:layout_margin="10dp"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/artWorkDescription" android:layout_margin="10dp" /> </LinearLayout> </ScrollView> </LinearLayout> 线程上使用消息泵是完全可以的。但是,消息泵必须位于创建窗口的线程上。在您的情况下,这意味着必须从同一个线程调用mainCreateAWindow

答案 1 :(得分:3)

  

现在,编写消息泵包含一个while循环,它占据整个程序,不允许执行其他任务

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

  

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

任何类型的消息循环都可以处理同一个线程中的多个窗口。

  

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

因为您的窗口管理完全错误。您对Windows和线程如何协同工作存在根本性的误解。你没有为每个窗口创建一个单独的线程(虽然你在技术上可以,但这样做是浪费的)。您创建一个线程(或只使用您的主线程)来创建多个窗口,然后使用单个消息循环来为它们提供服务。

尝试更像这样的事情:

#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

#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);
}

然后你可以在一个帖子中做这样的事情:

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);
            }
        }
        ...
    }
}

等等......

答案 2 :(得分:1)

std :: thread似乎不起作用,您可以使用CreateThread代替

(简单)示例代码

#define UNICODE
#include <windows.h>

const wchar_t CLASS_NAME[] = L"Window Class";

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

DWORD WINAPI CreateWindowAndRunUseMesageLoop(LPVOID* id){
    WCHAR className[] = L"XCLASSSSS";
    WCHAR title[] = L"XTITLE";
    title[0] = *(WCHAR*)id;
    className[0] = *(WCHAR*)id;
    WNDCLASS wc = {};
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = GetModuleHandle(NULL);
    wc.lpszClassName = className;
    RegisterClass(&wc);

    auto hwnd = CreateWindowEx(0, className, title, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT
        , 300, 300, NULL, NULL, GetModuleHandle(NULL), NULL);

    ShowWindow(hwnd, SW_SHOW);

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

int main(){
    HANDLE handle[2];
    WCHAR i = L'0';
    handle[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CreateWindowAndRunUseMesageLoop, &i, 0, NULL);
    WCHAR j = L'1';
    handle[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CreateWindowAndRunUseMesageLoop, &j, 0, NULL);
    WaitForMultipleObjects(2, handle, TRUE, INFINITE);
}

答案 3 :(得分:0)

你可以移动

  • 创建窗口
  • 运行消息循环

进入方法,并使用线程执行此方法

  • std :: thread似乎不起作用,您需要PeekMessage而不是

  • 当然,您可以将其封装在课堂上。

当您创建游戏API时,也许您可​​以使用provided "org.codehaus.groovy:groovy-ant" (非阻止)并自己做计时器或什么?