简体   繁体   中英

winapi destroyWindow() immediately exits my program

Okay, i can understand if the function would return an error or throw an exception, but for some reason my call to DestroyWindow literally exits the program at that point. Like with the actual exit() function changing my program flow. The documentation mentions nothing like this and i have no means of figuring out what is going on since i get no error code. Has anyone ever encountered something like this?

I'm doing quite a bit more with this object than using winapi, so ignore the rest of it. What else could be wrong here?

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;
}

quick explanation of how i do this. window handle and hinstance have private members in the object, and during the Initialize() and InitializeWindows() functions, the window class and window itself is created. The windows procedure is defined below as a global function, because you can't use a member function as a windows procedure. I dodge around this by making a global pointer (gasp) at the top, and assigning it to the this pointer of the system, which allows the procedure to call a member function procedure. Still only allows one instance of the system, but that's all i need :. Anywho, during shutdown, ShutdownWindows() is called, which then calls DestroyWindow. It is during this call, that my program simply ends, no error, no exception. MSVC++ express tells me it returns error code 3, but as far as windows error codes, that's just an ERROR_PATH_NOT_FIND which makes no sense in this context. Anyone have a clue?

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;
}

What seems to be going on is that you handle the WM_DESTROY message and explicitly call PostQuitMessage . I suspect somewhere that WM_QUIT message is being processed and causing an exit of some sort. I'll see if I can find any further information, but that's my understanding so far.

Rule of Thumb:

When you receive a WM_CLOSE message, call DestroyWindow . When you receive a WM_DESTROY message, call PostQuitMessage . Because you are using PeekMessage , you will get a message structure for a WM_QUIT message. When you find this message, you should end your loop.

There's nothing in your message loop that will notice a WM_QUIT and abort. The "standard" practice for a message loop is to call GetMessage() until it fails, which indicates WM_QUIT , but you're calling PeekMessage() which has no handling for WM_QUIT at all. Your message loop only exits when done is true, which will only happen when escape is pressed or your Frame() call fails.

As you discovered, the solution was to redesign your loop to properly handle WM_QUIT messages and to not call DestroyWindow after the loop has already ended.

IIRC exit code 3 is what you get by calling abort .

Anyway,

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

is very ungood (it terminates your message loop), and most likely the cause of your problems.

Just remove that.


Update: since the above recommendation didn't improve things, I suspect that you're calling DestroyWindow on an already destroyed window.

As a rule you should only call DestroyWindow in response to WM_CLOSE . For general windows that's the default handling. There is no indication in the presented code (as I'm writing this) of how your "shutdown" code is called, but due to the identical handling of WM_CLOSE and WM_DESTROY I suspect that it's a call placed after the message loop.

And in that case it is indeed likely that you're calling DestroyWindow on an already destroyed window.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM