Winapi 如何使用Win32 Api设置窗口动画?
你会如何定期在窗户上画一些东西呢 我已经想出了这个(为了清晰起见,去掉了很多)Winapi 如何使用Win32 Api设置窗口动画?,winapi,Winapi,你会如何定期在窗户上画一些东西呢 我已经想出了这个(为了清晰起见,去掉了很多) #包括 无效提款dc(HDC dc){ pen=CreatePen(…) penOld=选择对象(直流,笔) ……这是实际的图纸 …应该经常被称为,因为 …绘制的图片随时间而变化 ……进步 选择对象(dc、笔\旧); 删除对象(笔); } LRESULT回调WindowProc(..){ 开关(Msg){ 案例WM_油漆:{ PAINTSTRUCT-ps; dc=开始修复(hWnd和ps); ..创建了一个内存DC
#包括
无效提款dc(HDC dc){
pen=CreatePen(…)
penOld=选择对象(直流,笔)
……这是实际的图纸
…应该经常被称为,因为
…绘制的图片随时间而变化
……进步
选择对象(dc、笔\旧);
删除对象(笔);
}
LRESULT回调WindowProc(..){
开关(Msg){
案例WM_油漆:{
PAINTSTRUCT-ps;
dc=开始修复(hWnd和ps);
..创建了一个内存DC
…以防止闪烁。
HBITMAP持久位图;
PersistenceBitmap=CreateCompatibleBitmap(dc、windowHeight、windowHeight);
HDC dcMemory=CreateCompatibleDC(dc);
HBITMAP oldBmp=(HBITMAP)选择对象(dcMemory,PersistenceBitmap);
DrawOntoDC(dcMemory);
…一次性“复制”内存dc至dhe窗口dc:
比特比特莱特(dc,
0,0,窗宽,窗高,
dcMemory,
0, 0,
srcopy
);
..…销毁分配的位图和内存DC
……我觉得这是可以实施的
…更好,即在不分配和销毁memroy dc的情况下
..…并使用每个WM_绘制位图。
选择对象(dcMemory,oldBmp);
DeleteDC(dcMemory);
DeleteObject(PersistenceBitmap);
端漆(hWnd和ps);
返回0;
}
违约:
返回DefWindowProc(hWnd、Msg、wParam、lParam);
}
}
DWORD WINAPI定时器(LPP){
…确保窗口
……定期上漆。
HWND HWND=(HWND)*((HWND*)p);
而(1){
睡眠(1000/帧每秒);
无效(hWnd,0,真);
}
}
int APICENTRY WinMain(…){
WNDCLASSEX窗口类;
windowClass.lpfnWndProc=WindowProc;
windowClass.lpszClassName=className;
....
RegisterClassEx(&windowClass);
HWND HWND=CreateWindowEx(
....
类名,
....);
展示窗口(hwnd、SW_展示);
更新窗口(hwnd);
德沃德·特莱德;
HANDLE hTimer=CreateThread(
0, 0,
计时器,
(LPVOID)和hwnd,
0,&threadId);
while(GetMessage(&Msg,NULL,0,0)){
....
}
返回Msg.wParam;
}
我想还有很多地方需要改进,如果有人能指出我忽略了的地方,我将不胜感激。用辅助线程做这种事情并不是最好的。 假设绘制的最佳代码路径始终是通过WM_绘制,该绘制有两种方法:
while(stillRunning)
{
DWORD ret = MsgWaitForMultipleObjects(0,NULL,FALSE,frameIntervalMs,QS_ALLEVENTS);
if(ret == WAIT_OBJECT_0){
while(PeekMessage(&msg,0,0,0,PM_REMOVE)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if(TickGame()) // if TickGame indicates that enough time passed for stuff to change
RedrawWindow(hwndGame,...); // Dispatch a WM_PAINT immediately.
}
这种消息循环的危险在于,如果。。。应用程序进入任何类型的模式状态:-用户开始拖动窗口/弹出一个模式对话框,然后消息被模式循环泵送,因此动画停止。因此,如果需要将高性能消息循环与模式操作混合使用,则需要有一个回退计时器
WRT您的WM_PAINT实现-通常最好(重新)创建backbuffer以响应WM_大小消息。这样,它的大小总是正确的,并且您不会因为每秒多次重新创建大型内存缓冲区而产生相当大的成本。借用不同位置的位和块,我提出了以下解决类似问题的方法。下面是一个用于测试该概念的测试应用程序 在这个测试应用程序中,我使用一个MFC静态窗口,通过定期调用带有MFC静态窗口句柄的
::SetWindowText()
函数,用文本字符串更新该窗口。这可以很好地显示行进中的直角括号,以演示动画是否正常工作
在未来,我打算使用内存驻留位图图像,该图像在动画循环中修改,然后发布到附加到静态文本窗口的位图中。这项技术允许动画位图以更优雅的方式显示正在进行的操作
使用MFC对话框应用程序测试此概念,该应用程序包含两个用于进度指示器的静态窗口和两个用于启动和停止进度指示器的附加按钮“开始”和“停止”。其目标是,当按下开始按钮时,一系列大于的符号会被写入静态窗口,然后清除,然后再次开始。因此,动画看起来像一个LED标志,箭头在水平方向上从左向右移动
这两个按钮只不过是将动画对象中的指示器设置为一(打开)或零(关闭)。执行实际动画的动画对象线程只读取m_state
变量,不修改它
为了本测试的目的,定时器延迟量被硬编码。它很容易成为一个参数
对话框仍然是响应的,即使它正在更新,我也可以为对话框应用程序显示默认的“关于”框,并四处移动“关于”框。我还可以在屏幕上拖动对话框应用程序本身(不显示“关于”框,因为这是一个模式拨号)
while(stillRunning)
{
DWORD ret = MsgWaitForMultipleObjects(0,NULL,FALSE,frameIntervalMs,QS_ALLEVENTS);
if(ret == WAIT_OBJECT_0){
while(PeekMessage(&msg,0,0,0,PM_REMOVE)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if(TickGame()) // if TickGame indicates that enough time passed for stuff to change
RedrawWindow(hwndGame,...); // Dispatch a WM_PAINT immediately.
}
class AnimatedImage
{
UINT m_state; // current on/off state of the animation. if off (0) then the window is not updated
UINT m_itemId; // control identifier of the window that we are going to be updating.
HWND m_targetHwnd; // window handle of the parent dialog of the window we are going to be updating
UINT m_i; // position for the next right angle bracket
wchar_t m_buffer[32]; // text buffer containing zero or more angle brackets which we use to update the window
DWORD m_lastError; // result of GetLastError() in case of an error.
HANDLE m_hTimer; // handle for the timer
HANDLE m_hThread; // handle for the thread created.
LARGE_INTEGER m_liDueTime; // time delay between updates
public:
AnimatedImage(UINT itemId = 0, HWND hWnd = NULL) : m_state(0), m_itemId(itemId), m_targetHwnd(hWnd), m_i(0), m_lastError(0), m_hTimer(NULL), m_hThread(NULL) { memset(m_buffer, 0, sizeof(m_buffer)) ; }
~AnimatedImage() { Kill(); CloseHandle(m_hTimer); CloseHandle(m_hThread); } // clean up the timer and thread handle.
static unsigned __stdcall loop(AnimatedImage *p); // animation processing loop
void Run(); // starts the animation thread
void Start(); // starts the animation
void Stop(); // stops the animation
void Kill(); // indicates the thread is to exit.
// Functions used to get the target animation window handle
// and to set the parent window handle and the dialog control identifier.
// This could be simpler by just requiring the target animation window handle
// and letting the user do the GetDlgItem() function themselves.
// That approach would make this class more flexible.
HWND GetImageWindow() { return ::GetDlgItem(m_targetHwnd, m_itemId); }
void SetImageWindow(UINT itemId, HWND hWnd) { m_itemId = itemId; m_targetHwnd = hWnd; }
};
unsigned __stdcall AnimatedImage::loop(AnimatedImage *p)
{
p->m_liDueTime.QuadPart = -10000000LL;
// Create an unnamed waitable timer. We use this approach because
// it makes for a more dependable timing source than if we used WM_TIMER
// or other messages. The timer resolution is much better where as with
// WM_TIMER is may be no better than 50 milliseconds and the reliability
// of getting the messages regularly can vary since WM_TIMER are lower
// in priority than other messages such as mouse messages.
p->m_hTimer = CreateWaitableTimer(NULL, TRUE, NULL);
if (NULL == p->m_hTimer)
{
return 1;
}
for (; ; )
{
// Set a timer to wait for the specified time period.
if (!SetWaitableTimer(p->m_hTimer, &p->m_liDueTime, 0, NULL, NULL, 0))
{
p->m_lastError = GetLastError();
return 2;
}
// Wait for the timer.
if (WaitForSingleObject(p->m_hTimer, INFINITE) != WAIT_OBJECT_0) {
p->m_lastError = GetLastError();
return 3;
}
else {
if (p->m_state < 1) {
p->m_i = 0;
memset(p->m_buffer, 0, sizeof(m_buffer));
::SetWindowText(p->GetImageWindow(), p->m_buffer);
}
else if (p->m_state < 2) {
// if we are updating the window then lets add another angle bracket
// to our text buffer and use SetWindowText() to put it into the
// window area.
p->m_buffer[p->m_i++] = L'>';
::SetWindowText(p->GetImageWindow(), p->m_buffer);
p->m_i %= 6; // for this demo just do a max of 6 brackets before we reset.
if (p->m_i == 0) {
// lets reset our buffer so that the next time through we will start
// over in position zero (first position) with our angle bracket.
memset(p->m_buffer, 0, sizeof(m_buffer));
}
}
else {
// we need to exit our thread so break from the loop and return.
break;
}
}
}
return 0;
}
void AnimatedImage::Run()
{
m_hThread = (HANDLE)_beginthreadex(NULL, 0, (_beginthreadex_proc_type)&AnimatedImage::loop, this, 0, NULL);
}
void AnimatedImage::Start()
{
m_state = 1;
}
void AnimatedImage::Stop()
{
m_state = 0;
}
void AnimatedImage::Kill()
{
m_state = 3;
}
AnimatedImage xxx;
AnimatedImage xx2;
// TODO: Add extra initialization here
xxx.SetImageWindow(IDC_IMAGE1, this->m_hWnd);
xxx.Run();
xx2.SetImageWindow(IDC_IMAGE2, this->m_hWnd);
xx2.Run();
return TRUE; // return TRUE unless you set the focus to a control
void CMFCApplication2Dlg::OnBnClickedButton1()
{
// TODO: Add your control notification handler code here
xxx.Start();
xx2.Start();
}
void CMFCApplication2Dlg::OnBnClickedButton2()
{
// TODO: Add your control notification handler code here
xxx.Stop();
xx2.Stop();
}
IDD_MFCAPPLICATION2_DIALOG DIALOGEX 0, 0, 320, 200
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
EXSTYLE WS_EX_APPWINDOW
FONT 8, "MS Shell Dlg", 0, 0, 0x1
BEGIN
DEFPUSHBUTTON "OK",IDOK,209,179,50,14
PUSHBUTTON "Cancel",IDCANCEL,263,179,50,14
CTEXT "TODO: Place dialog controls here.",IDC_STATIC,10,96,300,8
LTEXT "Static",IDC_IMAGE1,7,7,110,21
LTEXT "Static",IDC_IMAGE2,64,43,112,27
PUSHBUTTON "Start",IDC_BUTTON1,252,16,50,19
PUSHBUTTON "Stop",IDC_BUTTON2,248,50,57,21
END