草庐IT

c - 句柄无效

coder 2024-06-20 原文

尝试使用 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://learn.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);
    }
}

测试.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);
    }

}

最佳答案

一发帖就发现了。您可以看到我正在获取 TGHandleResizeEvent 中句柄的指针 location:

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

事实上,这是一个无效句柄。更正后的代码:

return SetConsoleScreenBufferSize(tgHandle->screenBufferHandle, bufferNewSize);

关于c - 句柄无效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55464847/

有关c - 句柄无效的更多相关文章

  1. ruby-on-rails - Rails 5 Active Record 记录无效错误 - 2

    我有两个Rails模型,即Invoice和Invoice_details。一个Invoice_details属于Invoice,一个Invoice有多个Invoice_details。我无法使用accepts_nested_attributes_forinInvoice通过Invoice模型保存Invoice_details。我收到以下错误:(0.2ms)BEGIN(0.2ms)ROLLBACKCompleted422UnprocessableEntityin25ms(ActiveRecord:4.0ms)ActiveRecord::RecordInvalid(Validationfa

  2. ruby - 如何排除无效日期 ruby - 2

    我想知道我应该引用什么异常名称。我的日期无效。我检查了文档,但找不到。BeginDate.new(day,month,year)Rescueexceptionnamestatements 最佳答案 我认为您正在寻找ArgumentError.使用irb:>Date.new(2,-200,3)ArgumentError:invaliddatefrom(irb):11:in`new'from(irb):11所以beginDate.new(2,-200,3)rescueArgumentError#yourlogicend

  3. ruby - 尝试授权服务器到 ruby​​ 中的服务器类型应用程序以访问 Google 日历时无效授权 - 2

    我正在尝试为自己创建一个直接连接到我的日历的应用程序……但我从不想参与重新验证。我只想编写一次身份验证代码并完成它。授权码如下:key=Google::APIClient::PKCS12.load_key(SERVICE_ACCOUNT_PKCS12_FILE_PATH,PASSWORD)asserter=Google::APIClient::JWTAsserter.new(SERVICE_ACCOUNT_EMAIL,'https://www.googleapis.com/auth/calendar',key)@client=Google::APIClient.new@client.a

  4. ruby - `DateTime.strptime` 返回工作日/时间字符串的无效日期 - 2

    为什么Ruby的strptime不将其转换为DateTime对象:DateTime.strptime('Monday10:20:20','%A%H:%M:%S')#=>ArgumentError:invaliddate虽然这些有效?DateTime.strptime('Wednesday','%A')#=>#DateTime.strptime('10:20:20','%H:%M:%S')#=># 最佳答案 这看起来像一个错误-minitech'scomment是正确的。不过,现在有一个解决方法(因为您可能希望它现在起作用):您可以在

  5. Ruby 文件句柄管理(打开的文件太多) - 2

    我在ruby​​(2.0.0p39474)中执行非常快速的文件访问,并不断收到异常Toomanyopenfiles看过thisthread,here,以及各种其他来源,我很清楚操作系统限制(在我的系统上设置为1024)。我执行此文件访问的代码部分是互斥的,并采用以下形式:File.open(filename,'w'){|f|Marshal.dump(value,f)}其中filename会根据调用该部分的线程快速变化。据我了解,此表单在block后放弃其文件句柄。我可以使用ObjectSpace.each_object(File)验证打开的File对象的数量.这报告最多有100个常驻内

  6. ruby-on-rails - 使用机架中间件捕获无效的 JSON 解析错误 - 2

    我正在使用Rails5,我正在尝试改进对我的API的无效JSON请求的错误处理。我尝试通过救援在Controller中解析来处理无效格式的JSON,但意识到如果用户将ContentType添加到他们的请求header,Rails中间件会在我的JSON请求到达Controller之前解析它。我遵循了以下指南:https://robots.thoughtbot.com/catching-json-parse-errors-with-custom-middleware但是,在启动服务器时出现以下错误:.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems

  7. ruby-on-rails - 带有分类器 gem 的无效编码符号 - 2

    当我将项目添加到我的Postgres数据库时,一切似乎都运行良好。在不做任何更改的情况下,只要在我的应用程序中的任何位置启动Madeleine,我的Rails应用程序就会开始失败:EncodingErrorinEventsController#updateinvalidencodingsymbolapp/controllers/events_controller.rb:137:in`update'137是问题行:135defupdate136@event=Event.find(params[:id])137m=SnapshotMadeleine.new("bayes_data")...

  8. ruby - gmail 的 omniauth oauth token 无效 - 2

    我正在尝试获取一个可以与gmail_xauth(rubygem)一起使用的oauthtoken查看用户的邮件。我首先在谷歌上注册了我的应用程序,然后然后设置设备以请求访问邮件:config.omniauth:google,'key','secret',:scope=>'https://mail.google.com/mail/feed/atom/'然后我通过outh/openid流程,谷歌提示我批准访问gmail,使用token将我重定向回应用程序和omniuth凭据中的secret&我的谷歌帐户列出了我的应用程序被授权访问我的数据。到目前为止,一切都很好。现在,当我获取这些凭据并尝试

  9. ruby-on-rails - 奇怪的 ActiveRecord 问题——比如生成无效的 SQL - 2

    最近我们部署了一个新版本的应用程序,从那时起我们就发现ActiveRecord存在一些非常奇怪的问题。例如,这是它每天生成数百次的查询片段,通常是正确的:`entries`.`style`ASt1_r25,`entries`.`pdf_visibility`AS,`entries`.`web_visibility`ASt1_r27这不是打字错误,t1_r26不见了,虽然它应该在的地方有一个空格。但只有那一次。这也不是手写SQL,而是ActiveRecord编写查询并决定所有占位符变量。它同样拙劣地破坏了其他查询,将不应该留空(甚至不应该留空)的东西留空,但只是偶尔留空一次。大多数时候都

  10. ruby - 如何使用 ActiveSupport 3 (Rails) 解析带有不带引号键的无效 JSON - 2

    我需要在Ruby中解析某些无效的JSON。类似于:json_str='{name:"Javier"}'ActiveSupport::JSON.decodejson_str如你所见,它是无效的,因为哈希键没有被引用,它应该是json_str='{"name":"Javier"}'但这无法更改,我必须解析未加引号的键。我可以用ActiveSupport2.x解析它,但ActiveSupport3不允许。它抛出我:Yajl::ParseError:lexicalerror:invalidstringinjsontext.{name:"Javier"}(righthere)------^顺便说

随机推荐