句柄无效

时间:2019-04-01 23:17:12

标签: c windows console

尝试使用SetConsoleScreenBufferSize,但失败并显示“句柄无效”。在最后一个错误。将发布所有代码,但这是一些重点:

使用它来调整缓冲区大小:

int TGHandleResizeEvent(struct TGHandle *tgHandle, INPUT_RECORD record) {
    if (record.EventType == WINDOW_BUFFER_SIZE_EVENT) {
        WINDOW_BUFFER_SIZE_RECORD size = record.Event.WindowBufferSizeEvent;
        sizeTGDrawBuffer(&tgHandle->drawBuffer, size.dwSize.X, size.dwSize.Y);
        clearTGDrawBuffer(&tgHandle->drawBuffer);
        COORD bufferNewSize = {
            size.dwSize.X,
            size.dwSize.Y
        };
        return SetConsoleScreenBufferSize(&tgHandle->screenBufferHandle, bufferNewSize);
    }
}

使用此方法分配句柄:

struct TGHandle TG() {
    struct TGHandle tgHandle;
    tgHandle.screenBufferHandle = CreateConsoleScreenBuffer(
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,
        CONSOLE_TEXTMODE_BUFFER,
        NULL
    );
    CONSOLE_SCREEN_BUFFER_INFO info;
    GetConsoleScreenBufferInfo(tgHandle.screenBufferHandle, &info);
    tgHandle.drawBuffer = createTGDrawBuffer(info.dwSize.X, info.dwSize.Y);
    // Create the input buffer
    tgHandle.inputBufferSize = 32;
    tgHandle.inputBuffer = malloc(sizeof(INPUT_RECORD) * tgHandle.inputBufferSize);
    // Hook up the input handle
    tgHandle.inputHandle = GetStdHandle(STD_INPUT_HANDLE);
    return tgHandle;
}

这里是完整代码。

tg.h

#ifndef TG_H
#define TG_H

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

#define FOREGROUND_WHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
#define BACKGROUND_WHITE BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE

// A drawing buffer, for general purposes
struct TGDrawBuffer {
    COORD size;
    CHAR_INFO *buffer;
};

struct TGDrawBuffer createTGDrawBuffer(int, int); // Function to allocate a drawing buffer
void sizeTGDrawBuffer(struct TGDrawBuffer*, int, int); // Resize a draw buffer
void clearTGDrawBuffer(struct TGDrawBuffer*); // Fill a buffer with blank cells
void TGDrawPixel(struct TGDrawBuffer*, int, int, CHAR_INFO); // Draw to a single cell on the buffer
void TGDrawAttribute(struct TGDrawBuffer*, int, int, int); // Modify a single attribute. X, Y, Attr
void TGDrawCharInfoString(struct TGDrawBuffer*, int, int, CHAR_INFO*, int); // X, Y, string, int. Draws to max X
CHAR_INFO* TGCharToCharInfo(char*, int); // Convert basic characters to CHAR_INFO. String, length.
void TGDrawString(struct TGDrawBuffer*, int, int, char*, int); // X, Y, string, length. Draws to max X
void freeTGDrawBuffer(struct TGDrawBuffer*); // Function to de-allocate a drawing buffer

int CharInfoStrlen(CHAR_INFO*); // Get length of a CHAR_INFO as if it were a string

// Essentially a drawing context to the screen
struct TGHandle {
    HANDLE screenBufferHandle, inputHandle;
    struct TGDrawBuffer drawBuffer;
    INPUT_RECORD *inputBuffer;
    int inputBufferSize;
};

struct TGHandle TG(); // Initialization function, which returns a drawing context to the screen
void useTGHandle(struct TGHandle*); // Make a screen drawing context active
void updateTGHandle(struct TGHandle*); // Displays what has been drawn
void setTGHandleCursorVisibility(struct TGHandle*, int); // True / False
int getTGInput(struct TGHandle*, INPUT_RECORD*, int); // Fill input into a buffer
int getTGNextInput(struct TGHandle*, INPUT_RECORD*); // Get a single INPUT_RECORD or return false
int TGHandleResizeEvent(struct TGHandle*, INPUT_RECORD); // Resize is not handled automatically

#endif

tg.c

#include "tg.h"
#include <string.h>

struct TGDrawBuffer createTGDrawBuffer(int width, int height) {
    struct TGDrawBuffer tgDrawBuffer;
    tgDrawBuffer.buffer = NULL; // Init the buffer to NULL
    sizeTGDrawBuffer(&tgDrawBuffer, width, height);
    return tgDrawBuffer;
}

void sizeTGDrawBuffer(struct TGDrawBuffer* drawBuffer, int width, int height) {
    // Using free/ malloc here because we aren't interested in retaining data
    if (drawBuffer->buffer) {
        free(drawBuffer->buffer);
    }
    drawBuffer->buffer = malloc(sizeof(CHAR_INFO) * (width * height));
    // Copy the size to the buffer record
    drawBuffer->size.X = width;
    drawBuffer->size.Y = height;
}

void clearTGDrawBuffer(struct TGDrawBuffer *tgBuffer) {
    int i = 0, limit = tgBuffer->size.X * tgBuffer->size.Y;
    // Create a blank CHAR_INFO
    CHAR_INFO clearChar;
    clearChar.Char.AsciiChar = ' ';
    clearChar.Char.UnicodeChar = ' ';
    clearChar.Attributes = FOREGROUND_WHITE; // Would be confusing without this
    // Set everything to that buffer
    while (i < limit) {
        tgBuffer->buffer[i] = clearChar;
        i++;
    }
}

void TGDrawPixel(struct TGDrawBuffer *tgBuffer, int x, int y, CHAR_INFO character) {
    tgBuffer->buffer[(tgBuffer->size.X * y) + x] = character;
}

void TGDrawAttribute(struct TGDrawBuffer *tgBuffer, int x, int y, int attr) {
    tgBuffer->buffer[(tgBuffer->size.X * y) + x].Attributes = attr;
}

void TGDrawCharInfoString(struct TGDrawBuffer *tgDrawBuffer, int x, int y, CHAR_INFO *string, int length) {
    int charsToWrite = length;
    int distanceToEnd = (tgDrawBuffer->size.Y - 1) - y;
    if (distanceToEnd < charsToWrite)
        distanceToEnd = charsToWrite;
    int startPos = x + (tgDrawBuffer->size.X * y);
    int i = 0;
    while (i < distanceToEnd) {
        tgDrawBuffer->buffer[startPos + x] = string[i];
        i++;
    }
}

CHAR_INFO* TGCharToCharInfo(char* string, int length) {
    if (length == -1)
        length = strlen(string);
    // TODO
}

void TGDrawString(struct TGDrawBuffer *tgDrawBuffer, int x, int y, char *string, int length) {
    int charsToWrite = length;
    int distanceToEnd = (tgDrawBuffer->size.Y - 1) - y;
    if (distanceToEnd < charsToWrite)
        charsToWrite = distanceToEnd;
    int startPos = x + (tgDrawBuffer->size.X * y);
    int i = 0;
    while (i < charsToWrite) {
        tgDrawBuffer->buffer[startPos + i].Char.AsciiChar = string[i];
        tgDrawBuffer->buffer[startPos + i].Char.UnicodeChar = string[i];
        i++;
    }
}

void freeTGDrawBuffer(struct TGDrawBuffer *drawBuffer) {
    free(drawBuffer->buffer);
}

struct TGHandle TG() {
    struct TGHandle tgHandle;
    tgHandle.screenBufferHandle = CreateConsoleScreenBuffer(
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,
        CONSOLE_TEXTMODE_BUFFER,
        NULL
    );
    CONSOLE_SCREEN_BUFFER_INFO info;
    GetConsoleScreenBufferInfo(tgHandle.screenBufferHandle, &info);
    tgHandle.drawBuffer = createTGDrawBuffer(info.dwSize.X, info.dwSize.Y);
    // Create the input buffer
    tgHandle.inputBufferSize = 32;
    tgHandle.inputBuffer = malloc(sizeof(INPUT_RECORD) * tgHandle.inputBufferSize);
    // Hook up the input handle
    tgHandle.inputHandle = GetStdHandle(STD_INPUT_HANDLE);
    return tgHandle;
}

void useTGHandle(struct TGHandle *tgHandle) {
    SetConsoleActiveScreenBuffer(tgHandle->screenBufferHandle);
    // Update the buffer sizes
    CONSOLE_SCREEN_BUFFER_INFO info;
    GetConsoleScreenBufferInfo(tgHandle->screenBufferHandle, &info);
    sizeTGDrawBuffer(&tgHandle->drawBuffer, info.dwSize.X, info.dwSize.Y);
    clearTGDrawBuffer(&tgHandle->drawBuffer);
}

void updateTGHandle(struct TGHandle *tgHandle) {
    COORD size = { tgHandle->drawBuffer.size.X, tgHandle->drawBuffer.size.Y }; // Buffer size
    COORD pos = { 0, 0 }; // Start of the buffer coord
    SMALL_RECT rect = {
        .Left = 0,
        .Top = 0,
        .Right = size.X - 1,
        .Bottom = size.Y - 1
    }; // Rect to draw to on destination
    WriteConsoleOutput(
        tgHandle->screenBufferHandle,
        tgHandle->drawBuffer.buffer,
        size,
        pos,
        &rect
    );
}

void setTGHandleCursorVisibility(struct TGHandle *tgHandle, int visible) {
    // Copy the already-available cursor info
    CONSOLE_CURSOR_INFO info;
    GetConsoleCursorInfo(tgHandle->screenBufferHandle, &info);
    // Modify the cursor visibility
    info.bVisible = visible;
    SetConsoleCursorInfo(tgHandle->screenBufferHandle, &info);
}

// You should be able to use a TGHandle's input buffer rather than creating your own
// for maximum memory conservation
int getTGInput(struct TGHandle *tgHandle, INPUT_RECORD *inputBuffer, int max) {
    int availableRecords;
    GetNumberOfConsoleInputEvents(tgHandle->inputHandle, &availableRecords);
    int amountToRead = max;
    if (availableRecords < max) {
        amountToRead = availableRecords;
    }
    int numberRead;
    ReadConsoleInput(
        tgHandle->inputHandle,
        inputBuffer,
        amountToRead,
        &numberRead
    );
    return numberRead;
}

// This function should be pretty performant if someone would not like to use
// the above function and mess around with buffers.
// Input record info: https://docs.microsoft.com/en-us/windows/console/input-record-str
int getTGNextInput(struct TGHandle *tgHandle, INPUT_RECORD *record) {
    int availableRecords;
    GetNumberOfConsoleInputEvents(tgHandle->inputHandle, &availableRecords);
    if (availableRecords == 0) {
        return 0;
    }
    ReadConsoleInput(
        tgHandle->inputHandle,
        tgHandle->inputBuffer,
        1,
        &availableRecords
    );
    *record = tgHandle->inputBuffer[0];
    return 1;
}

int TGHandleResizeEvent(struct TGHandle *tgHandle, INPUT_RECORD record) {
    if (record.EventType == WINDOW_BUFFER_SIZE_EVENT) {
        WINDOW_BUFFER_SIZE_RECORD size = record.Event.WindowBufferSizeEvent;
        sizeTGDrawBuffer(&tgHandle->drawBuffer, size.dwSize.X, size.dwSize.Y);
        clearTGDrawBuffer(&tgHandle->drawBuffer);
        COORD bufferNewSize = {
            size.dwSize.X,
            size.dwSize.Y
        };
        return SetConsoleScreenBufferSize(&tgHandle->screenBufferHandle, bufferNewSize);
    }
}

test.c


#include "tg.h"
#include <time.h>
#include <stdio.h>

void getMessageAsStr(char *buf) {
    FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL, GetLastError(), 0,
        buf, 256, NULL);
}

int main() {

    // Error buffer
    char buf[256];

    // Create a drawing context to the screen
    struct TGHandle context = TG();
    useTGHandle(&context);
    setTGHandleCursorVisibility(&context, 0); // Hide the cursor of course
    struct TGDrawBuffer *buffer = &context.drawBuffer;

    // Create a CHAR_INFO to draw with
    CHAR_INFO info;
    info.Attributes = BACKGROUND_BLUE | FOREGROUND_WHITE;
    info.Char.AsciiChar = ' ';
    info.Char.UnicodeChar = ' ';

    INPUT_RECORD input;

    const int STRING_BUF_SIZE = 64;
    char *fpsCountBuffer = malloc(sizeof(char) * STRING_BUF_SIZE);

    long start, end;

    start = QueryPerformanceCounter(&start);

    int running = 1;
    while (running) {

        // Start off with a nice clean slate
        //clearTGDrawBuffer(buffer);

        // Collect input to react to resize
        while (getTGNextInput(&context, &input)) {
            if (input.EventType == WINDOW_BUFFER_SIZE_EVENT) {
                if (!TGHandleResizeEvent(&context, input)) {
                    OutputDebugString("Couldn't resize:\n");
                    getMessageAsStr(buf);
                    OutputDebugString(buf);
                }
            }
        }

        // Draw line along top and bottom
        int i = 0;
        while (i < buffer->size.X) {
            TGDrawPixel(buffer, i, 0, info);
            TGDrawPixel(buffer, i, buffer->size.Y - 1, info);
            i++;
        }
        i = 0;
        // Draw vertical lines
        while (i < buffer->size.Y) {
            TGDrawPixel(buffer, 0, i, info);
            TGDrawPixel(buffer, buffer->size.X - 1, i, info);
            i++;
        }

        // FPS count!
        // Get time elapsed in millis
        QueryPerformanceCounter(&end);
        long fps = 1000000 / (end - start);
        // Put it into the screen buffer
        snprintf(fpsCountBuffer, STRING_BUF_SIZE, "Running at %ldhz, %dx%d", fps, buffer->size.X, buffer->size.Y);
        TGDrawString(buffer, 1, 1, fpsCountBuffer, strlen(fpsCountBuffer));
        start = end;

        updateTGHandle(&context);
    }

}

1 个答案:

答案 0 :(得分:2)

我发布后立即发现了它。您可以看到我在TGHandleResizeEvent中获取句柄的指针位置

return SetConsoleScreenBufferSize(&tgHandle->screenBufferHandle, bufferNewSize);

实际上,这是无效的句柄。更正的代码:

return SetConsoleScreenBufferSize(tgHandle->screenBufferHandle, bufferNewSize);