【发布时间】:2018-09-14 22:55:51
【问题描述】:
我正在重构屏幕效果的 C 实现,通过移动桌面快照的位获得。
代码的灵感来自此视频:Screen Melting Effect
我已将代码封装在一个名为 ScreenMelter 的单例类中:
class ScreenMelter
{
private:
HWND hWnd;
static unsigned int TimerID;
static unsigned int nScreenWidth;
static unsigned int nScreenHeight;
protected:
bool InitClass()
{
WNDCLASS wndClass = { 0, MelterProc, 0, 0, GetModuleHandle(NULL), NULL, LoadCursor(NULL, IDC_ARROW), 0, NULL, L"Melter" };
if (!GetClassInfo(GetModuleHandle(NULL), L"Melter", &wndClass))
{
if (!RegisterClass(&wndClass))
{
MessageBox(NULL, L"Cannot register class!", NULL, MB_ICONERROR | MB_OK);
return false;
}
}
return true;
}
bool InitWindow()
{
nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
hWnd = CreateWindow(L"Melter", NULL, WS_POPUP, 0, 0, nScreenWidth, nScreenHeight, HWND_DESKTOP, NULL, GetModuleHandle(NULL), NULL);
if (!hWnd)
{
MessageBox(NULL, L"Cannot create window!", NULL, MB_ICONERROR | MB_OK);
return false;
}
return true;
}
void FreeWindow()
{
if (hWnd)
DestroyWindow(hWnd);
}
void FreeClass()
{
UnregisterClass(L"Melter", GetModuleHandle(NULL));
}
static LRESULT WINAPI MelterProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch (Msg)
{
case WM_CREATE:
{
std::cout << "I got created! \n";
HDC hdcWindow = GetDC(hWnd);
HDC hdcDesktop = GetDC(HWND_DESKTOP);
BitBlt(hdcWindow, 0, 0, nScreenWidth, nScreenHeight, hdcDesktop, 0, 0, SRCCOPY);
ReleaseDC(hWnd, hdcWindow);
ReleaseDC(HWND_DESKTOP, hdcDesktop);
TimerID = SetTimer(hWnd, 0,
1, // Speed of the timer
NULL);
ShowWindow(hWnd, SW_SHOW);
break;
}
case WM_ERASEBKGND:
break;
case WM_PAINT:
ValidateRect(hWnd, NULL);
break;
case WM_TIMER:
{
HDC hdcWindow = GetDC(hWnd);
int nWidth = (rand() % 150);
int nYPos = (rand() % 15);
int nXPos = (rand() % nScreenWidth) - (150 / 2);
BitBlt(hdcWindow, nXPos, nYPos, nWidth, nScreenHeight, hdcWindow, nXPos, 0, SRCCOPY);
ReleaseDC(hWnd, hdcWindow);
break;
}
case WM_KEYDOWN:
{
if (wParam != VK_ESCAPE)
break;
}
case WM_CLOSE:
case WM_DESTROY:
{
// It kills the timer, but I wonder, why that doens't responde after one call
KillTimer(hWnd, TimerID);
TimerID = NULL;
PostQuitMessage(0);
std::cout << "-> I got destroyed!\n";
break;
}
default:
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
return 0;
}
public:
void StartMelting(int32_t duration)
{
InitClass();
InitWindow();
MSG Msg = { 0 };
auto Start = std::chrono::steady_clock::now();
while (Msg.message != WM_QUIT)
{
if (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
std::cout << "Msg ";
}
auto End = std::chrono::steady_clock::now();
if (std::chrono::duration_cast<std::chrono::seconds>(End - Start).count() >= duration) break;
}
FreeWindow();
FreeClass();
}
protected:
ScreenMelter()
{ }
public:
ScreenMelter(const ScreenMelter&) = delete;
ScreenMelter& operator=(const ScreenMelter&) = delete;
static ScreenMelter& GetInstance()
{
static ScreenMelter melter;
return melter;
}
~ScreenMelter()
{
FreeWindow();
FreeClass();
}
};
该类按应有的方式工作,但仅在第一次创建窗口时。经过一些调试,我发现在第一次调用 StartMelting(seconds) 后,窗口会正确创建,但在收到 WM_DESTROY/WM_CLOSE 消息后不久就会关闭它。
以下代码显示了这一点:
int main()
{
ScreenMelter& melter = ScreenMelter::GetInstance();
int input;
int32_t TimeInSeconds(2);
while (1)
{
std::cin >> input;
if (input == 0)
break;
melter.StartMelting(TimeInSeconds);
}
}
盯着屏幕看了将近一个小时后,我决定需要一些帮助。
问题:
- 是什么导致窗口在第一次之后收到关闭消息
创建窗口?资源被正确释放后
计时器到期,或按下
ESC键!
编辑 1:
为了让我的问题更清楚,这是应用程序的输出:
// First call
1
I got created!
Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg
Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg
Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg
Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg
Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg
Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg
Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg Msg -> I got destroyed!
// Second call
1
I got created!
Msg -> I got destroyed!
// Third call
1
I got created!
Msg -> I got destroyed!
【问题讨论】:
-
仅供参考,即使 在 处理完消息后,您仍在返回 DefWindowProc 调用。检查您的文件;这是不对的。通常只有在不处理消息时才调用 DefWindowProc。
-
请出示minimal reproducible example,不要只是转储代码。
-
我已将交换机中的 breaks 替换为
return 0;(表示消息已处理的信号),但我仍然遇到同样的问题:程序正常工作仅在第一次初始化时 -
@Lundin 在构造函数中用false初始化!
-
您在
StartMelting的底部调用FreeClass,这会取消注册该类,但您从未将init重置为false,因此下次创建窗口将失败,因为该类不是t 注册。