【问题标题】:winapi destroyWindow() immediately exits my programwinapi destroyWindow() 立即退出我的程序
【发布时间】:2012-09-29 01:07:27
【问题描述】:

好的,我可以理解该函数是否会返回错误或抛出异常,但由于某种原因,我对 DestroyWindow 的调用实际上会在此时退出程序。就像实际的 exit() 函数改变了我的程序流程一样。文档没有提到这样的内容,我无法弄清楚发生了什么,因为我没有收到错误代码。有没有人遇到过这样的事情?

我用这个对象比使用 winapi 做的更多,所以忽略它的其余部分。这里还有什么问题?

SYNC_WinSystem.h

#ifndef SYNC_WINSYSTEM_H
#define SYNC_WINSYSTEM_H

#include "SYNC_ISystem.h"

#include <Windows.h>
#include <array>
#include "SYNC_Winput.h"
#include "SYNC_IRenderer.h"
#include "SYNC_D3D11Renderer.h"

#define FULL_SCREEN true

// SYNC_WinSystem
class SYNC_WinSystem : public SYNC_ISystem
{   
public:
    class WindowsContext;

    SYNC_WinSystem();

    virtual long Initialize(InitializeContext *);
    virtual void Init_Loop();
    virtual void Shutdown();

    virtual long MakeDirectory(std::string);
    virtual bool CreateSkin(std::string, std::string, SYNC::ISkin *&);
    virtual ISound * CreateSound();

    LRESULT CALLBACK MessageHandler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);

private:
    virtual long InitializeWindows();
    virtual bool Frame();
    virtual void ShutdownWindows();

private:
    SYNC_Winput m_Input;

private:
    std::shared_ptr<SYNC_IRenderer> m_Graphics;
    HINSTANCE m_hinstance;
    HWND m_hwnd;
    int m_screenWidth;
    int m_screenHeight;
    std::string m_WindowName;
};

// SYNC_WinSystem::WindowsContext
class SYNC_WinSystem::WindowsContext : public SYNC_ISystem::InitializeContext
{
public:
    WindowsContext();
    std::string Type();

    HINSTANCE m_hinstance;
    std::string m_WindowName;

private:
    const static std::string m_Identifier;
};


#endif

SYNC_WinSystem.cpp

#include "SYNC_WinSystem.h"

// SYNC_WinSystem definitions
SYNC_WinSystem * g_windows;

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);

SYNC_WinSystem::SYNC_WinSystem()
    : m_Graphics(new SYNC_D3D11Renderer)
{
    m_hinstance = nullptr;
    m_hwnd = nullptr;
    m_screenWidth = 0;
    m_screenHeight = 0;
}

long SYNC_WinSystem::Initialize(InitializeContext  * context)
{
    long result = 0;
    char errors[256];
    WindowsContext * cContext;
    if(context->Type() == "WindowsContext")
        cContext = static_cast<WindowsContext *>(context);
    else
        return false;

    m_hinstance = cContext->m_hinstance;
    m_WindowName = cContext->m_WindowName;
    g_windows = this;


    result = InitializeWindows();
    if(result)
    {
        sprintf_s(errors, "The Window could not initialize. Windows error code: %i", result);
        MessageBox(NULL, errors, "Error!", MB_OK); 
        return result;
    }

    std::array<std::string, 3> folderNames=
    {{
        "Compiled_Models",
        "Temp_Models",
        "Materials"
    }};

    for(int i = 0; i < (int) folderNames.size(); i++)
    {
        result = MakeDirectory(folderNames[i]);
        if( result && (result != ERROR_ALREADY_EXISTS))
        {
            sprintf_s(errors, "Error creating directory \" %s \" for system. Windows error code: %i", folderNames[i].c_str(), result);
            MessageBox(NULL, errors, "Error!", MB_OK);
            return result;
        }
        result = 0;
    }

    SYNC_D3D11Renderer::D3D11Context graphicsContext;
    graphicsContext.fullscreen = true;
    graphicsContext.hwnd = m_hwnd;
    graphicsContext.screenDepth = 1000.0f;
    graphicsContext.screenNear = 0.1f;
    graphicsContext.screenWidth = m_screenWidth;
    graphicsContext.screenHeight = m_screenHeight;

    if(!m_Graphics->Initialize(&graphicsContext))
        return false;

    return result;
}

void SYNC_WinSystem::Init_Loop()
{

    MSG msg;
    bool done, result;

    ZeroMemory(&msg, sizeof(MSG));
    done = false;

    while(!done)
    {
        if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        if(m_Input.IsKeyPressed(VK_ESCAPE))
        {

            done = true;
        }
        else
        {
            result = Frame();
            if(!result)
            {
                done = true;
            }
        }

    }
}

void SYNC_WinSystem::Shutdown()
{
    ShutdownWindows();
}

long SYNC_WinSystem::MakeDirectory(std::string dirName)
{
    DWORD result = 0;
    long returnValue = 0;

    dirName.insert(0, ".\\");
    result = CreateDirectory(dirName.c_str(), NULL);

    if(result == 0)
    {
        returnValue = GetLastError();
    }
    return returnValue;
}

bool SYNC_WinSystem::Frame()
{

    if(!m_Graphics->Frame())
        return false;

    return true;
}

long SYNC_WinSystem::InitializeWindows()
{
    DWORD result = 0;
    WNDCLASSEX wc;
    DEVMODE dmScreenSettings;
    int posX, posY;

    wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wc.lpfnWndProc = &WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = m_hinstance;
    wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
    wc.hIconSm = wc.hIcon;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = m_WindowName.c_str();
    wc.cbSize = sizeof(WNDCLASSEX);



    if(RegisterClassEx(&wc) == 0)
    {
        result = GetLastError();
        return result;
    } 

    m_screenWidth = GetSystemMetrics(SM_CXSCREEN);
    m_screenHeight = GetSystemMetrics(SM_CYSCREEN);

    if(FULL_SCREEN)
    {
        memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
        dmScreenSettings.dmSize = sizeof(dmScreenSettings);
        dmScreenSettings.dmPelsWidth = (unsigned long) m_screenWidth;
        dmScreenSettings.dmPelsHeight = (unsigned long) m_screenHeight;
        dmScreenSettings.dmBitsPerPel = 32;
        dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

        ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN);
        posX = posY = 0;

        m_hwnd = CreateWindowEx(WS_EX_APPWINDOW, 
                             m_WindowName.c_str(), 
                             m_WindowName.c_str(),
                             WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP,
                             posX, posY, m_screenWidth, m_screenHeight,
                             NULL, NULL, m_hinstance, NULL);

    }
    else
    {
        m_screenWidth = 800;
        m_screenHeight = 600;

        posX = ((GetSystemMetrics(SM_CXSCREEN)/2) - (m_screenWidth/2));
        posY = ((GetSystemMetrics(SM_CYSCREEN)/2) - (m_screenHeight/2));

        m_hwnd = CreateWindowEx(WS_EX_APPWINDOW, 
                             m_WindowName.c_str(), 
                             m_WindowName.c_str(),
                             WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP,
                             posX, posY, m_screenWidth, m_screenHeight,
                             NULL, NULL, m_hinstance, NULL);
    }



    if(!m_hwnd)
    {
        result = GetLastError();
        return result;
    }

    ShowWindow(m_hwnd, SW_SHOW);
    SetForegroundWindow(m_hwnd);
    SetFocus(m_hwnd);

    return result;
}

void SYNC_WinSystem::ShutdownWindows()
{
    ShowCursor(true);

    if(FULL_SCREEN)
    {
        ChangeDisplaySettings(NULL, 0);
    }


    if(DestroyWindow(m_hwnd) == 0)
    {
        char meh[256];
        sprintf(meh, "error: %i" , GetLastError());
        MessageBox(NULL, meh, "error!", MB_OK);
    }

    m_hwnd = NULL;

    UnregisterClass(m_WindowName.c_str(), m_hinstance);
    m_hinstance = NULL;

    g_windows = NULL;

    return;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
    switch(msg)
    {
        case WM_DESTROY:
        {

            PostQuitMessage(0);
            return 0;
        }
        case WM_CLOSE:
        {
            PostQuitMessage(0);
            return 0;
        }
        default:
        {
            return g_windows->MessageHandler(hwnd, msg, wparam, lparam);
        }
    }
}

LRESULT CALLBACK SYNC_WinSystem::MessageHandler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{

    switch(msg)
    {
        case WM_KEYDOWN:
        {
            m_Input.KeyDown((unsigned int) wparam);
            return 0;
        }
        case WM_KEYUP:
        {
            m_Input.KeyDown((unsigned int) wparam);
            return 0;
        }
        default:
        {
            return DefWindowProc(hwnd, msg, wparam, lparam);
        }
    }


}

ISound * SYNC_WinSystem::CreateSound()
{
    return nullptr;
}

bool SYNC_WinSystem::CreateSkin(std::string filename, std::string shaderName, SYNC::ISkin *& skin)
{
    if(!m_Graphics->CreateSkin(filename, shaderName, skin))
        return false;

    return true;
}


// SYNC_WinSystem::WindowsContext definitions
const std::string SYNC_WinSystem::WindowsContext::m_Identifier = "WindowsContext";

SYNC_WinSystem::WindowsContext::WindowsContext()
{


}

std::string SYNC_WinSystem::WindowsContext::Type()
{
    return m_Identifier;
}

快速解释我如何做到这一点。窗口句柄和实例在对象中有私有成员,在 Initialize() 和 InitializeWindows() 函数期间,窗口类和窗口本身被创建。 windows 过程在下面定义为全局函数,因为您不能将成员函数用作 windows 过程。我通过在顶部创建一个全局指针(gasp)并将其分配给系统的 this 指针来避开这个问题,这允许该过程调用成员函数过程。仍然只允许一个系统实例,但这就是我所需要的:。任何人,在关机期间,都会调用 ShutdownWindows(),然后调用 DestroyWindow。正是在此调用期间,我的程序才结束,没有错误,也没有异常。 MSVC++ express 告诉我它返回错误代码 3,但就 Windows 错误代码而言,这只是一个 ERROR_PATH_NOT_FIND,在这种情况下没有任何意义。有人知道吗?

main.cpp

#include <memory>
#include <Windows.h>

#include "Syncopate.h"
#include "SYNC_WinSystem.h"
using namespace std;

#include "SYNC_Winput.h"

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR lpCmdLine, INT nCmdShow)
{
    //create a new instance of the engine
    std::shared_ptr<SYNC_WinSystem> system( new SYNC_WinSystem);

    // create an init context for this specific derivative of SYNC_ISystem
    SYNC_WinSystem::WindowsContext context;
    context.m_hinstance = hInstance;
    context.m_WindowName = "Syncopate";

    // Initialize the system object. if something goes wrong, return ERROR
    if(system->Initialize(&context))
        return 1;

    SYNC::ISkin * model;

    if(!system->CreateSkin("data.txt", "ColorShader", model))
        return 1;

    system->Init_Loop();

    system->Shutdown();

    return 0;
}

【问题讨论】:

    标签: c++ visual-studio-2010 winapi


    【解决方案1】:

    您的消息循环中没有任何内容会注意到WM_QUIT 并中止。消息循环的“标准”做法是调用GetMessage() 直到它失败,这表示WM_QUIT,但您调用的是PeekMessage(),它根本没有处理WM_QUIT。您的消息循环仅在 done 为真时退出,这只会在按下转义键或您的 Frame() 调用失败时发生。

    正如您所发现的,解决方案是重新设计循环以正确处理 WM_QUIT 消息,并且在循环结束后不调用 DestroyWindow。

    【讨论】:

      【解决方案2】:

      似乎发生的事情是您处理WM_DESTROY 消息并显式调用PostQuitMessage。我怀疑某处WM_QUIT 消息正在被处理并导致某种退出。我会看看我是否能找到任何进一步的信息,但这是我目前的理解。

      经验法则:

      当您收到WM_CLOSE 消息时,请致电DestroyWindow。当您收到WM_DESTROY 消息时,请致电PostQuitMessage。因为您使用的是PeekMessage,所以您将获得WM_QUIT 消息的消息结构。当你找到这条消息时,你应该结束你的循环。

      【讨论】:

      • 是的,当我调用 ShutdownWindows() 时,我明确调用 destroyWindow() 处理并调用 PostQuitMessage(0) 发布 WM_QUIT,并且应该结束消息循环,然后继续关闭其余部分我的应用程序,但它只是在调用 destroyWindow() 时中止。
      • windows 是否有任何育儿功能?有没有安装挂钩?当您调用 DestroyWindow 时,您能否确保您的 HWND 有效,以防万一?这一切都在一个线程中吗?
      • 这一切都在一个线程中,如果你计算使用窗口来显示 d3d 图形,那么是的。不知道什么是钩子,所以可能不是。我该如何确保 HWND 在通话之前仍然有效?
      • 在处理 WM_Destroy 和调用 DestroyWindow 的位置放置一个断点。确保它们没有被多次击中。
      • 好的,因为直接全屏直到所有关机结束,我无法在销毁窗口看到断点的结果,但我能够找到断点在 WM_DESTROY 消息只被命中一次。
      【解决方案3】:

      IIRC 退出代码 3 是您通过调用 abort 得到的。

      不管怎样,

          case WM_CLOSE:
          {
              PostQuitMessage(0);
              return 0;
          }
      

      非常糟糕(它会终止您的消息循环),并且很可能是您的问题的原因。

      删除它。


      更新:由于上述建议并没有改善,我怀疑你是在一个已经被破坏的窗口上调用DestroyWindow

      通常,您应该只调用DestroyWindow 来响应WM_CLOSE。对于一般窗口,这是默认处理。提供的代码(正如我正在写的那样)中没有指示如何调用您的“关闭”代码,但由于 WM_CLOSEWM_DESTROY 的相同处理,我怀疑这是在消息之后发出的调用循环。

      在这种情况下,您确实很可能在一个已经被破坏的窗口上调用DestroyWindow

      【讨论】:

      • 如果我是正确的,WM_CLOSE 会在用户单击 X 按钮时发送,并且应该调用 DestroyWindow 本身。另一方面,WM_DESTROY 是由DestroyWindow 发送的,是这里感兴趣的消息。
      • @AnthonyBurleigh:一般窗口对WM_CLOSE的默认处理是调用DestroyWindow;但是这不适用于对话框。
      • @Cheers 和 hth。 -alf - 消息循环是 Init-Loop 函数,调用它来启动 windows 和 d3d 的消息循环/渲染循环。我有一个函数,当按下转义时退出循环。当这个循环退出时,窗口会因为某种原因被破坏吗?是的,在消息循环退出后调用关闭函数
      • 你最好贴一些代码。但首先,将问题简化为一个完整的小程序
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-04-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-02
      • 1970-01-01
      相关资源
      最近更新 更多