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_绘制,该绘制有两种方法:

  • 只需在GUI线程上创建一个计时器,将WM_计时器消息直接发布到TimerRoc或窗口,然后调用引擎的OnTick()部分。如果任何精灵移动,它们将使用invalidate institute()使其区域无效,windows随后会自动发布WM_绘制。如果游戏相对空闲,这有一个非常低的CPU使用率的优势

  • 大多数游戏需要更严格的计时,这可以通过使用基于低优先级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