游戏开发论坛

 找回密码
 立即注册
搜索
查看: 5500|回复: 1

关于拖动窗口消息循环停止的问题

[复制链接]

1

主题

4

帖子

0

积分

新手上路

Rank: 1

积分
0
发表于 2009-7-17 09:08:00 | 显示全部楼层 |阅读模式
这段代码主要用于屏蔽windows系统处理窗口移动,因为windows移动窗口是消息循环回停止,而对于一些应用程序来说,这种机制会带来很多问题,比如说网游就会有很多同步的问题。下面这段代码放到消息处理中应给可以解决这个问题,其他消息自行处理。这只是一种方法,希望还有更好方法的同学拿出来分享,大家共同探讨。

static RECT WindowCurPos;        //
        static POINT MousePrePos, MouseCurPos;
        static bool bNcLButtonDown = false;   
        LRESULT     Result;
        /////////////////////////////////////////////////////////////////////////////////////////
        // Message handler.
        switch(Message)
        {
//鼠标按下获取当前位置,以便取出现对位置
        case WM_NCLBUTTONDOWN:
                GetCursorPos(&MousePrePos);
                bNcLButtonDown = true;
                return DefWindowProc(Window,Message,wParam,lParam);
//鼠标抬起时标记,表示不再拖动,标记设置为false
        case WM_NCLBUTTONUP:       
                bNcLButtonDown = false;
                return DefWindowProc(Window,Message,wParam,lParam);
//鼠标出了标题栏的时候标记也要设置为false,避免鼠标无法定位
        case WM_NCMOUSELEAVE:
                bNcLButtonDown = false;
                return DefWindowProc(Window,Message,wParam,lParam);
//在标题栏上点击右键不做处理,就是不要弹出系统菜单
        case WM_NCRBUTTONDOWN:
                return 0;
        case WM_SYSCOMMAND:
                switch( wParam & 0xfff0 )
                {
///////////////这里直接返回,避免进入消息循环不出来,使游戏freese////////////////////
//当鼠标在标题栏上按住不放时,系统会发送WM_SYSCOMMAND消息,当DefWindowProc
//收到SC_MOVE后,会发送WM_ENTERSIZEMOVE,这个时候整个消息循环就会停止,直到
//DefWindowProc处理完成返回,所以这个消息不能交给系统处理,直接返回
//SC_KEYMENU消息是按快捷键的时候产生的系统菜单,这时候消息循环也是停止的,直到
//DefWindowProc处理完成返回
//SC_MOUSEMENU消息是鼠标左键点击左上角小图标是产生的系统菜单,同上
                        case SC_MOVE:
                        case SC_KEYMENU:
                        case SC_MOUSEMENU:
                                if( IsFullscreen() )
                                        return 1;
                                return 0;
                }
                        /////////////////////////////////////////////////////////////////////////////////////////////////////
        case WM_NCHITTEST:
                // Prevent the user from selecting the menu in fullscreen mode.
                if( IsFullscreen() )
                        return HTCLIENT;

                Result = DefWindowProc( Window, Message, wParam, lParam );
                switch(Result)
                {
                case HTCAPTION:
                        //////////////这里根据鼠标的相对位置设置窗口位置////////////
//这里是根据鼠标消息模拟窗口移动的过程
                        if(bNcLButtonDown)
                        {
                                ::GetCursorPos(&MouseCurPos);
                                ::GetWindowRect(Window, &WindowCurPos);
                                WindowCurPos.left += MouseCurPos.x - MousePrePos.x;
                                WindowCurPos.top += MouseCurPos.y - MousePrePos.y;                                        ::SetWindowPos(Window, HWND_NOTOPMOST, WindowCurPos.left, WindowCurPos.top, WindowCurPos.right, WindowCurPos.bottom, SWP_NOSIZE);
                                MousePrePos = MouseCurPos;
                        }
                        ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                }
                return Result;
}

1

主题

4

帖子

0

积分

新手上路

Rank: 1

积分
0
 楼主| 发表于 2009-7-30 15:42:00 | 显示全部楼层

Re:关于拖动窗口消息循环停止的问题

以上的程序有问题,因为当鼠标移出窗口的是时候就不能收到消息,因此如果一直按住鼠标不放会使标记一直为true。还有就是在windows xp之前的版本甚至在xp的经典窗口模式下都不会收到WM_NCMOUSELEAVE这条消息,这就使得标记不能及时更新
下面是解决办法
        // Message handler.       
        switch(Message)
        {
        case WM_NCLBUTTONUP:        //鼠标抬起时标记,表示不再拖动,标记设置为false
                bNcLButtonDown = false;
                return DefWindowProc(Window,Message,wParam,lParam);
        case WM_NCRBUTTONDOWN://在标题栏上点击右键不做处理,就是不要弹出系统菜单
                return 0;
        case WM_NCMOUSELEAVE://鼠标出了标题栏的时候标记也要设置为false,避免鼠标无法定位
                bNcLButtonDown = false;
                return DefWindowProc(Window,Message,wParam,lParam);
        case WM_SYSCOMMAND:
                switch( wParam & 0xfff0 )
                {
///////////////这里直接返回,避免进入消息循环不出来,使游戏freese////////////////////
//当鼠标在标题栏上按住不放时,系统会发送WM_SYSCOMMAND消息,当DefWindowProc
//收到SC_MOVE后,会发送WM_ENTERSIZEMOVE,这个时候整个消息循环就会停止,直到
//DefWindowProc处理完成返回,所以这个消息不能交给系统处理,直接返回
//SC_KEYMENU消息是按快捷键的时候产生的系统菜单,这时候消息循环也是停止的,直到
//DefWindowProc处理完成返回
//SC_MOUSEMENU消息是鼠标左键点击左上角小图标是产生的系统菜单,同上
                        case SC_MOVE:  //这里是多出来的
                                ::GetCursorPos(&MouseCurPos);                        //鼠标按下获取当前位置,以便取出现对位置
                                ::GetWindowRect(Window, &WindowCurPos);
                                WindowDeltaPos.left = MouseCurPos.x - WindowCurPos.left;
                                WindowDeltaPos.top = MouseCurPos.y - WindowCurPos.top;
                                bNcLButtonDown = true;

                                //解决在经典窗口(Windows Classic Style)模式下,收不到WM_NCMOUSELEAVE的问题
                                if(bNcLButtonDown)
                                {
                                        TRACKMOUSEEVENT   MouseEvent;
                                        MouseEvent.cbSize = sizeof(TRACKMOUSEEVENT);
                                        MouseEvent.dwFlags = TME_LEAVE | TME_NONCLIENT;
                                        MouseEvent.hwndTrack = Window;
                                        MouseEvent.dwHoverTime = 0;
                                        ::TrackMouseEvent(&MouseEvent);
                                }
                        case SC_KEYMENU:
                        case SC_MOUSEMENU:
                                if( IsFullscreen() )
                                        return 1;
                                return 0;
                }
        case WM_NCHITTEST:
                // Prevent the user from selecting the menu in fullscreen mode.
                if( IsFullscreen() )
                        return HTCLIENT;

                ////////////这里根据鼠标的相对位置设置窗口位置///////////////////////////////////////////////////////
                //这里是根据鼠标消息模拟窗口移动的过程
                if(bNcLButtonDown)
                {
                        ::GetCursorPos(&MouseCurPos);
                        ::GetWindowRect(Window, &WindowCurPos);
                        WindowCurPos.left = MouseCurPos.x - WindowDeltaPos.left;
                        WindowCurPos.top = MouseCurPos.y - WindowDeltaPos.top;
                        ::SetWindowPos(Window, HWND_NOTOPMOST, WindowCurPos.left, WindowCurPos.top, WindowCurPos.right, WindowCurPos.bottom, SWP_NOSIZE);
                        MousePrePos = MouseCurPos;
                }
                /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        return DefWindowProc( Window, Message, wParam, lParam );
}

还有一个重要的地方就是鼠标这种情况下鼠标很容易跑到窗口外边,是拖动窗口显得很费力,这就要是窗口在拖动过程中一直更随鼠标运动,但在消息循环中是不能收到鼠标到窗口外的消息的,所以必须找一个一直循环的地方,使窗口在拖动过程中位置一直跟随鼠标运动,下面这段代码就是防止鼠标跑到窗口外的,因为鼠标往下运动仍在窗口中,消息循环中的代码就可以防止鼠标跑出非客户区,但往上拖动就必须在循环里做,因为收不到鼠标消息,下面这段代码是在游戏循环里做的
//现在这些变量是全局的////////////////////////////////////////
RECT WindowCurPos, WindowDeltaPos;        //
POINT MousePrePos, MouseCurPos;
bool bNcLButtonDown = false;
///////////////////////////////////////////////////////////////
        if(bNcLButtonDown)
        {
                ::GetCursorPos(&MouseCurPos);                        //鼠标按下获取当前位置,以便取出现对位置
                ::GetWindowRect(::GetActiveWindow(), &WindowCurPos);
                if(MouseCurPos.y < WindowCurPos.top)
                {
                        WindowCurPos.left = MouseCurPos.x - WindowDeltaPos.left;
                        WindowCurPos.top = MouseCurPos.y - WindowDeltaPos.top;
                        //debugf(_T("Current Window Pos X Is %d Y Is %d"),WindowCurPos.left,WindowCurPos.top);
                        ::SetWindowPos(::GetActiveWindow(), HWND_NOTOPMOST, WindowCurPos.left, WindowCurPos.top, WindowCurPos.right, WindowCurPos.bottom, SWP_NOSIZE);
                        MousePrePos = MouseCurPos;
                }
}
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2025-1-9 16:58

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表