Home > Software design >  Error: The operation completed successfully (Command Line Game Engine in C using Windows API)
Error: The operation completed successfully (Command Line Game Engine in C using Windows API)

Time:11-02

As described in the title I want to write a game engine using the command line console. Following closely the project of oneLoneCoder, the code that I have written so far is the following. It creates a command console after it checks that the dimensions given by the user are correct.

#include <Windows.h>
#include <iostream>
#include <cassert>

namespace CmdLineConsoleEngine {

    void Error(const HANDLE& hConsole, const wchar_t* msg) {
    
        wchar_t buf[256];
        FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 256, NULL);
        SetConsoleActiveScreenBuffer(hConsole);
        wprintf(L"ERROR: %s\n\t%s\n", msg, buf);
        exit(-1);
    }

    auto CreateConsole() -> HANDLE {

        HANDLE hConsole = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
        if (hConsole == INVALID_HANDLE_VALUE)
            Error(hConsole, L"Bad Handle"); // in which cases does this occur?
        else {
            SetConsoleActiveScreenBuffer(hConsole);
        }
        return hConsole;
    }

    void CheckConsoleSize(const HANDLE& hConsole, int screenWidth, int screenHeight) {

        assert(hConsole != nullptr);
        assert(screenWidth > 0);
        assert(screenHeight > 0);

        const COORD largestSize = GetLargestConsoleWindowSize(hConsole);
        if (screenWidth > largestSize.X) 
            Error(hConsole, L"The width dimension is too large.");
        if (screenHeight > largestSize.Y) 
            Error(hConsole, L"The height dimension is too large.");
        return;
    }

    void SetConsoleWindowSize(HANDLE& hConsole, int screenWidth, int screenHeight) {

        CheckConsoleSize(hConsole, screenWidth, screenHeight);

        const SMALL_RECT minimalRectWindow = { 0, 0, 12, 6 };
        SetConsoleWindowInfo(hConsole, TRUE, &minimalRectWindow);

        COORD coord = {static_cast<short>(screenWidth), static_cast<short>(screenHeight)};
        if (!SetConsoleScreenBufferSize(hConsole, coord))
            Error(hConsole, L"SetConsoleScreenBufferSize");

        SMALL_RECT rectWindow = {0, 0, static_cast<short>(screenWidth - 1), static_cast<short>(screenHeight - 1)};
        if (!SetConsoleWindowInfo(hConsole, TRUE, &rectWindow))
            Error(hConsole, L"SetConsoleWindowInfo");
        return;
    }

    void WriteToConsole(const HANDLE& hConsole, const wchar_t* screen, int screenWidth, int screenHeight) {

        DWORD dwBytesWritten = 0;
        WriteConsoleOutputCharacter(hConsole, screen, screenWidth * screenHeight, { 0,0 }, &dwBytesWritten);
    }

}

int main() {

    HANDLE hConsole = CmdLineConsoleEngine::CreateConsole();
    int screenWidth = 160;
    int screenHeight = 140;
    CmdLineConsoleEngine::SetConsoleWindowSize(hConsole, screenWidth, screenHeight);
    return 0;
}

Executing the main() function the output that I get is

ERROR: The height dimension is too large.
The operation completed successfully.    

It is the Error function acting funny. For your convenience, the call hierarchy is SetConsoleWindowSize -> CheckConsoleSize -> Error

Any idea on why this happens?

CodePudding user response:

Your error reporting in CheckConsoleSize() is wrong. GetLastError() is only meaningful if GetLargestConsoleWindowSize() returns a COORD containing all zeros, which you are not checking for. What you have described sounds like the COORD is simply containing sizes that are smaller than you are expecting, but are not zeros. That is why GetLastError() is returning 0, and thus FormatMessage() is returning "The operation completed successfully".

Try something more like this instead:

#include <Windows.h>
#include <iostream>
#include <cassert>

namespace CmdLineConsoleEngine {

    void Error(HANDLE hConsole, const wchar_t* msg) {
        SetConsoleActiveScreenBuffer(hConsole);
        wprintf(L"ERROR: %s\n", msg);
        exit(-1);
    }

    void ErrorWithCode(HANDLE hConsole, const wchar_t* msg, DWORD ErrorCode = GetLastError()) {
        wchar_t buf[256] = {};
        FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, ErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 256, NULL);
        SetConsoleActiveScreenBuffer(hConsole);
        wprintf(L"ERROR: %s\n\t%s\n", msg, buf);
        exit(-1);
    }

    HANDLE CreateConsole() {
        HANDLE hConsole = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
        if (hConsole == INVALID_HANDLE_VALUE)
            ErrorWithCode(hConsole, L"Bad Handle"); // in which cases does this occur?
        SetConsoleActiveScreenBuffer(hConsole);
        return hConsole;
    }

    void DestroyConsole(HANDLE& hConsole) {
        if (hConsole != INVALID_HANDLE_VALUE) {
            CloseHandle(hConsole);
            hConsole = INVALID_HANDLE_VALUE;
        }
    }

    void CheckConsoleSize(HANDLE hConsole, int screenWidth, int screenHeight) {
        assert(hConsole != INVALID_HANDLE_VALUE);
        assert(screenWidth > 0);
        assert(screenHeight > 0);
        const COORD largestSize = GetLargestConsoleWindowSize(hConsole);
        if (largestSize.X == 0 && largestSize.Y == 0) 
            ErrorWithCode(hConsole, L"GetLargestConsoleWindowSize");
        if (screenWidth > largestSize.X) 
            Error(hConsole, L"The width dimension is too large.");
        if (screenHeight > largestSize.Y) 
            Error(hConsole, L"The height dimension is too large.");
    }

    void SetConsoleWindowSize(HANDLE hConsole, int screenWidth, int screenHeight) {
        CheckConsoleSize(hConsole, screenWidth, screenHeight);

        const SMALL_RECT minimalRectWindow = { 0, 0, 12, 6 };
        if (!SetConsoleWindowInfo(hConsole, TRUE, &minimalRectWindow))
            ErrorWithCode(hConsole, L"SetConsoleWindowInfo");

        COORD coord = {static_cast<short>(screenWidth), static_cast<short>(screenHeight)};
        if (!SetConsoleScreenBufferSize(hConsole, coord))
            ErrorWithCode(hConsole, L"SetConsoleScreenBufferSize");

        SMALL_RECT rectWindow = {0, 0, static_cast<short>(screenWidth - 1), static_cast<short>(screenHeight - 1)};
        if (!SetConsoleWindowInfo(hConsole, TRUE, &rectWindow))
            ErrorWithCode(hConsole, L"SetConsoleWindowInfo");
    }

    void WriteToConsole(HANDLE hConsole, const wchar_t* screen, int screenWidth, int screenHeight) {
        DWORD dwBytesWritten = 0;
        if (!WriteConsoleOutputCharacter(hConsole, screen, screenWidth * screenHeight, {0, 0}, &dwBytesWritten))
            ErrorWithCode(hConsole, L"WriteConsoleOutputCharacter");
    }
}

int main() {
    HANDLE hConsole = CmdLineConsoleEngine::CreateConsole();
    CmdLineConsoleEngine::SetConsoleWindowSize(hConsole, 160, 140);
    CmdLineConsoleEngine::DestroyConsole(hConsole);
    return 0;
}

On a side note: calling Error/WithCode() if CreateConsoleScreenBuffer() fails doesn't make sense, as you would be calling SetConsoleActiveScreenBuffer() with an invalid console handle.

  • Related