关于鼠标右键弹不出问题的解决 

2006-12-11 11:40 发布

3654 1 0

在我正式开始写这个地图编辑器的时候,我并没有料到这个问题会困扰我长达一年之久,直到近日在网上某BBS中意外地发现了解决这个问题的方法,我已不只是为这个只需要一行代码即可解决的问题感慨了,我甚至感到了羞愧。正所谓“吃一堑长一智”,又有所谓“独乐乐不如众乐乐”,我便把这个困扰我的问题及它的解决方案写下来,与同我有一样问题的朋友们分享。

问题是这样的(这里直接拷贝了我在论坛上的提问):我的地图编辑器有个问题老是解决不了,就是右键菜单、对话框显示的问题。怎么说呢,当我有时(注意是有时,有时又可以)点击右键时,右键菜单显示不了,但我确定它确实弹出来了,因为那时画面就停住了,且我在右键菜单出现的大概位置点击后,能实现相应项的功能。也就是说,右键菜单弹出来了,但是显示不出来。对话框也有这个问题。

问题的解决从看到那篇《[转帖]在全屏(资源独占)游戏中弹出非模态对话框的》开始。原来,在全屏模式下,DirectDraw独占了显示,这导致通过GDI创建的对话框或其它窗口无法正常显示(In full-screen mode, DirectDraw has exclusive control over the display. As a result, dialog boxes and other windows created through GDI are not normally visible.)。当用MessageBox()、DialogBox()等弹出对话框或菜单时,这些对话框或菜单只能显示在隐藏的GDI表面上,而在被DirectDraw所控制的主表面上,它们是不会被显示出来的。所以,我们应该采用特殊技巧。这个技巧说来也非常简单,就是调用IDirectDraw7::FlipToGDISurface方法,将GDI表面设为主表面。这个方法在MessageBox()、DialogBox()等需要用到系统GDI的API前使用。之后问题解决。

楼主新帖

TA的作品 TA的主页
B Color Smilies

全部评论1

  • 暴米花
    暴米花 2006-12-11 11:41:00
    转帖]在全屏(资源独占)游戏中弹出非模态对话框的

    DirectDraw 7.0
    Displaying a Window in Full-Screen Mode
    [Visual Basic] 
    The information in this topic pertains only to applications written in C++. 
    [C++] 
    In full-screen mode, DirectDraw has exclusive control over the display. As a result, dialog boxes and other windows created through GDI are not normally visible. However, by using special techniques you can incorporate a Windows dialog box, HTML Help, or any other kind of window in your application.

    The FSWindow Sample illustrates how a dialog box can be displayed and updated in a full-screen application, and how mouse clicks and keystrokes work just as if the dialog box were being displayed by GDI.

    In FSWindow, the dialog box is created and "shown" as an ordinary dialog window:

    hWndDlg = CreateDialog(g_hInstance,
            MAKEINTRESOURCE(IDD_DIALOG_SAMPLE),
            hWnd, (DLGPROC) SampleDlgProc);
    ShowWindow(hWndDlg, SW_SHOWNORMAL);

    Of course, at this point the dialog box is shown only on the hidden GDI surface. It does not appear on the primary surface, which is controlled by DirectDraw. 

    If the hardware capabilities include DDCAPS2_CANRENDERWINDOWED (see DDCAPS), displaying and updating the dialog box is easy. The application simply calls the IDirectDraw7::FlipToGDISurface method, which makes the GDI surface the primary surface. From now on, all updates to the dialog box will be displayed automatically, because GDI is now rendering directly to the front buffer. The application continues rendering to the back buffer, and on each pass through the rendering loop the contents of the back buffer are blitted to the front buffer by DirectDraw. The dialog box is not overwritten because the front buffer is clipped to the application window, and the dialog box is obscuring part of that window.

    The following code, from the FSWindow_Init function, creates the clipper, associates it with the application window, and brings the GDI surface to the front:

    if (ddObject->CreateClipper(0, &ddClipper, NULL) == DD_OK)
        ddClipper->SetHWnd(0, hwndAppWindow);
    ddObject->FlipToGDISurface();

    Then, in the FSWindow_Update function, the following code blits the rendered contents of the back buffer to the clipping region:

    ddFrontBuffer->SetClipper(ddClipper);
    ddFrontBuffer->Blt(NULL, ddBackBuffer, NULL, DDBLT_WAIT, NULL);

    Note that because the GDI surface is the primary surface, Windows continues displaying the mouse cursor. (This would not be the case, however, if the application were using DirectInput with the mouse device at the exclusive cooperative level.)

    For hardware that does not have the DDCAPS2_CANRENDERWINDOWED capability, the process of displaying and updating a window in full-screen mode is somewhat more complicated. In this case, the application is responsible for obtaining the image of the window created by GDI and blitting it to the back buffer after the full-screen rendering has been done. The entire back buffer is then flipped to the front in the usual way.

    The FSWindow sample provides two different methods for accessing the display memory of the window, depending on whether the content is static or dynamic. The method for static content is faster because it involves blitting from a memory device context rather than a screen device context. This method should be used for windows that do not change, such as informational dialog boxes. (Remember, though, that unless you manually update the bitmap in response to events, even basic animation such as a button press will not be visible to the user.)

    If the content is static, FSWindow calls the CreateBMPFromWindow function when the window is initialized. This function creates a bitmap and blits the contents of the window into it. The bitmap handle is stored in the global variable hwndFSWindowBMP. Whenever the primary surface is about to be updated, this bitmap is blitted to the back buffer, as follows:

    if (FSWindow_IsStatic)
    {
        hdcMemory = CreateCompatibleDC(NULL);
        SelectObject(hdcMemory, hwndFSWindowBMP);
        BitBlt(hdcBackBuffer, x, y, cx, cy, hdcMemory, 0, 0, SRCCOPY);
        DeleteDC(hdcMemory);
    }

    If, on the other hand, the content of the window is dynamic, the following code is executed. It blits the image directly from the GDI surface (represented by the hdcScreen device context) to the back buffer.

    BitBlt(hdcBackBuffer, x, y, cx, cy, hdcScreen, x, y, SRCCOPY);

    The coordinates represent the position and dimensions of the window on the GDI surface, as retrieved through a call to GetWindowRect.

    When the FSWindow application is running on hardware that does not have the DDCAPS2_CANRENDERWINDOWED capability, it does not use the GDI surface, so Windows cannot display the mouse cursor. The application takes over this task by obtaining information about the cursor and displaying it on the back buffer just before the flip.

    首先,本文的方法只适合用C++写的程序

    在全屏模式,由于DirectDraw太公孙,因此只有采用特殊技巧,才能显示对话框,html帮助,etc

    下面就是说用FSWindow创建对话框。

    一般来说,是:

    hWndDlg = CreateDialog(g_hInstance,
            MAKEINTRESOURCE(IDD_DIALOG_SAMPLE),
            hWnd, (DLGPROC) SampleDlgProc);
    ShowWindow(hWndDlg, SW_SHOWNORMAL);

    当然这只是在默认是GDI的时候才可以,有时候就不行,比如现在。

    如果硬件支持DDCAPS2_CANRENDERWINDOWED,那么就比较好说。应用程序首先调用IDirectDraw7::FlipToGDISurface ,让GDI

    接口获得控制权。。。。。。。。。。。(此处省略250字)

    下面这个代码,来自FSWindow_Init函数,创建clipper,关联应用程序窗口,并且把GDI升权:

    if (ddObject->CreateClipper(0, &ddClipper, NULL) == DD_OK)
        ddClipper->SetHWnd(0, hwndAppWindow);
    ddObject->FlipToGDISurface();

    然后下面这段参考自FSWindow_Update的代码负责维护:

    ddFrontBuffer->SetClipper(ddClipper);
    ddFrontBuffer->Blt(NULL, ddBackBuffer, NULL, DDBLT_WAIT, NULL);

    由于GDI获得权限,因此这种情况下鼠标显示很正常。

    如果硬件不支持DDCAPS2_CANRENDERWINDOWED ,那么应用程序需要先接受GDI画好的图片,然后。。。。。。。。。(省略200

    字)

    FSWindow提供2种方法,这2种方法分别适用于静态content和动态content。静态content比较快,因为内存设备环境比窗口设

    备环境快。当然局限就是只能画不变化的对话框,比如一些提示了,请记住采用这种方法必须自己改变图片,如果打算有一些

    好看的操作。(估计你要的就是这种。。)

    对于静态的,在初始化窗口的时候,FSWindow 调用CreateBMPFromWindow ,该函数创建位图句柄并在窗口上显示,保存位图

    句柄的hwndFSWindowBMP是全局变量。如下:

    if (FSWindow_IsStatic)
    {
        hdcMemory = CreateCompatibleDC(NULL);
        SelectObject(hdcMemory, hwndFSWindowBMP);
        BitBlt(hdcBackBuffer, x, y, cx, cy, hdcMemory, 0, 0, SRCCOPY);
        DeleteDC(hdcMemory);
    }


    对于动态的,用下面的函数:
    BitBlt(hdcBackBuffer, x, y, cx, cy, hdcScreen, x, y, SRCCOPY);
    该函数的参数来自GetWindowRect()

    如果硬件不支持DDCAPS2_CANRENDERWINDOWED 的时候,画鼠标需要自己画:displaying it on the back buffer just before 

    the flip

你可能喜欢

关于鼠标右键弹不出问题的解决 
联系
我们
快速回复 返回顶部 返回列表