WINAPI -我想让消息泵在一个单独的线程中进行
WINAPI - I would like to have the message pump ongoing in a separate thread
在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();
}
};
在非main
线程上使用消息泵是完全可以的。但是,消息泵必须位于创建窗口的线程上。在您的示例中,这意味着必须从同一个线程调用CreateAWindow
和startMessageLoop
。
现在,编写消息泵由一个while循环组成,它支配整个程序,不允许执行其他事情
传统的 消息循环可能是这样工作的,但是在消息之间编写一个可以做其他事情的消息循环当然是可能的。谁说你不能用信息本身来做事情。你可能有一个阻塞调用线程的循环,但是你可以控制这个循环在每次迭代中实际做什么,所以它可以代表它的调用线程做一些事情。不需要仅 处理消息。
换句话说,我希望可以创建CFrame的多个实例,以便显示多个窗口,并且消息循环不会在创建第一个窗口后停止窗口。
任何类型的消息循环都可以在同一个线程中处理多个窗口。
我为函数
ThreadExecution()
创建了一个新线程,由于某种原因程序终止了,为什么?
因为你的窗口管理是完全错误的。您对窗口和线程如何协同工作有一个根本性的误解。您不需要为每个窗口创建一个单独的线程(虽然从技术上讲可以这样做,但是这样做很浪费)。你创建一个线程(或者只使用主线程)来创建多个窗口,然后使用一个消息循环来为它们服务。
试试这样写:
#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);
}
}
...
}
}
你可以移动
- <
- 创建窗口/gh>
- 运行消息循环
放入一个方法中,并使用线程来执行这个方法
std::thread似乎不工作,你需要
CreateThread
代替当然,你可以把它封装在类中
当你创建游戏API时,也许你可以使用PeekMessage
(非块)并做自己的计时器或其他东西?
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);
}
- 我应该有 2 个单独的班级,一个用于"logic",一个用于"graphic interface"?
- 查询事件日志.如何查询才能单独获取第一个和最后一个事件?
- window.display() 单独在显示的最后一个缓冲区和当前缓冲区之间切换
- 我可以制作一个对象方法,如果单独调用,它将自行修改,但如果在复制初始化期间调用,则会返回一个新对象?
- 将一个随机的字符序列和int序列解析为单独的向量
- Howo 使用 cl 预处理为 masm 组装生成一个单独的文件
- 显示数组的问题.一切都放在一个单独的行中,但不放入单独的列中
- 我如何构建一个从git克隆的单独的增强库
- 如何将一个类的两个单独实例设置为彼此相等的 c++
- 如何在单独的线程上加载纹理到主内存并使用它在另一个线程上渲染
- 如何在这个交换函数(一个单独的链表)中找到错误
- 如何将正则表达式中组的每次迭代都作为一个单独的组
- 如何避免将两个单独的'>'格式化为一个班次?
- 如何从一个函数中获取2个单独的输出
- C#优雅地关闭一个没有UI的单独应用程序
- 如何将我的C++DLL包含在一个单独的项目中
- 哪个更好,一个类中的类或完全在单独的类中
- 将单独的数字转换为一个整数C++
- 如果我在C++中分配一个带有运算符 new[] 的对象数组,但单独释放它们是否仍然构成内存泄漏
- 为什么 setprecision( ) 在一个数字中给我两个单独的小数点