You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用JNR-FFI向CMD屏幕缓冲区输出字符表时程序崩溃求助

问题:调用Windows控制台API写入屏幕缓冲区后崩溃(错误0xC0000005)

编写Java库时,通过JNR-FFI调用Kernel32的控制台API直接访问并写入CMD屏幕缓冲区,程序能成功打印第一帧,但之后会以退出代码0xC0000005崩溃。在CMD中运行时冻结无异常,IntelliJ控制台显示错误码,事件查看器记录崩溃模块为KERNELBASE.dll。

原代码

import jnr.ffi.LibraryLoader;
import jnr.ffi.Pointer;

public class Main {

    public interface Kernel32 {
        Kernel32 INSTANCE = LibraryLoader.create(Kernel32.class).load("kernel32");

        int GENERIC_READ = 0x80000000;
        int GENERIC_WRITE = 0x40000000;
        int FILE_SHARE_READ = 0x00000001;
        int FILE_SHARE_WRITE = 0x00000002;
        int CONSOLE_TEXTMODE_BUFFER = 1;

        Pointer CreateConsoleScreenBuffer(int dwDesiredAccess, int dwShareMode, Pointer lpSecurityAttributes, int dwFlags, Pointer lpScreenBufferData);
        void SetConsoleActiveScreenBuffer(Pointer hConsoleOutput);
        int WriteConsoleOutputCharacterW(Pointer hConsoleOutput, char[] lpCharacter, int nLength, int dwWriteCoord, int lpNumberOfCharsWritten);
    }

    private static final int nScreenWidth = 240;
    private static final int nScreenHeight = 135;

    static Kernel32 kernel32 = Kernel32.INSTANCE;

    private static Pointer hConsole = kernel32.CreateConsoleScreenBuffer(
            Kernel32.GENERIC_WRITE | Kernel32.GENERIC_READ,
            Kernel32.FILE_SHARE_READ | Kernel32.FILE_SHARE_WRITE,
            null,
            Kernel32.CONSOLE_TEXTMODE_BUFFER,
            null
    );

    public static void writeCMD(char[] screen){
        try {
            if (screen.length == (nScreenWidth * nScreenHeight) + 1) {
                int lpNumberOfCharsWritten = 0;
                System.out.println(kernel32.WriteConsoleOutputCharacterW(hConsole, screen, (nScreenWidth * nScreenHeight) + 1, 0, lpNumberOfCharsWritten));
            }
        } catch (Exception t) {
            t.printStackTrace();
        }
    }

    public static void main(String[] args) {
        kernel32.SetConsoleActiveScreenBuffer(hConsole);

        char[] screen = new char[(nScreenWidth * nScreenHeight) + 1];
        for (int i = 0; i < screen.length - 1; i++) {
            screen[i] = '-';
        }
        screen[screen.length - 1] = '\0';

        int position = 0;
        while(true){
            screen[position] = '#';
            writeCMD(screen);
            screen[position] = '-';
            position++;
        }
    }
}

崩溃日志

Faulting application name: java.exe, version: 20.0.2.0, time stamp: 0x75f7bb13
Faulting module name: KERNELBASE.dll, version: 10.0.22621.2715, time stamp: 0x10f6a783
Exception code: 0xc0000005
Fault offset: 0x00000000000b5bdc
Faulting process id: 0x0x5B98
Faulting application start time: 0x0x1DA1CB1377C8871
Faulting application path: C:\Program Files\Java\jdk-20\bin\java.exe
Faulting module path: C:\Windows\System32\KERNELBASE.dll
Report Id: 97a532d2-82fd-4b33-a399-ee74ea9e606d
Faulting package full name: 
Faulting package-relative application ID: 

解决方案

崩溃的核心原因是**WriteConsoleOutputCharacterW最后一个参数的错误传递**:该参数是Windows API的输出参数(用于返回实际写入的字符数),需要传递一个可写入的内存地址,而原代码直接传入int值0,导致API尝试往内存地址0写入数据,触发访问违规错误(0xC0000005)。

关键修改步骤

  1. 修正Kernel32接口中WriteConsoleOutputCharacterW的方法签名,将最后一个参数改为jnr.ffi.byref.IntByReference(JNR中用于传递整数输出参数的类型)。
  2. 在调用该方法时,创建IntByReference实例,而非直接传入int值。
  3. 移除字符数组末尾的无效终止符,优化循环逻辑避免数组越界。

修改后的完整代码

import jnr.ffi.LibraryLoader;
import jnr.ffi.Pointer;
import jnr.ffi.byref.IntByReference;

public class Main {

    public interface Kernel32 {
        Kernel32 INSTANCE = LibraryLoader.create(Kernel32.class).load("kernel32");

        int GENERIC_READ = 0x80000000;
        int GENERIC_WRITE = 0x40000000;
        int FILE_SHARE_READ = 0x00000001;
        int FILE_SHARE_WRITE = 0x00000002;
        int CONSOLE_TEXTMODE_BUFFER = 1;

        Pointer CreateConsoleScreenBuffer(int dwDesiredAccess, int dwShareMode, Pointer lpSecurityAttributes, int dwFlags, Pointer lpScreenBufferData);
        void SetConsoleActiveScreenBuffer(Pointer hConsoleOutput);
        // 修改最后一个参数为IntByReference,返回值改为boolean对应API的BOOL类型
        boolean WriteConsoleOutputCharacterW(Pointer hConsoleOutput, char[] lpCharacter, int nLength, int dwWriteCoord, IntByReference lpNumberOfCharsWritten);
    }

    private static final int nScreenWidth = 240;
    private static final int nScreenHeight = 135;

    static Kernel32 kernel32 = Kernel32.INSTANCE;

    private static Pointer hConsole = kernel32.CreateConsoleScreenBuffer(
            Kernel32.GENERIC_WRITE | Kernel32.GENERIC_READ,
            Kernel32.FILE_SHARE_READ | Kernel32.FILE_SHARE_WRITE,
            null,
            Kernel32.CONSOLE_TEXTMODE_BUFFER,
            null
    );

    public static void writeCMD(char[] screen){
        try {
            if (screen.length == nScreenWidth * nScreenHeight) {
                // 创建IntByReference实例接收实际写入的字符数
                IntByReference charsWritten = new IntByReference();
                kernel32.WriteConsoleOutputCharacterW(
                        hConsole, 
                        screen, 
                        nScreenWidth * nScreenHeight, 
                        0, 
                        charsWritten
                );
            }
        } catch (Exception t) {
            t.printStackTrace();
        }
    }

    public static void main(String[] args) {
        kernel32.SetConsoleActiveScreenBuffer(hConsole);

        // 移除多余的终止符,直接创建对应缓冲区大小的数组
        char[] screen = new char[nScreenWidth * nScreenHeight];
        for (int i = 0; i < screen.length; i++) {
            screen[i] = '-';
        }

        int position = 0;
        while(true){
            // 防止数组越界,到达末尾后重置位置
            if (position >= screen.length) {
                position = 0;
            }
            screen[position] = '#';
            writeCMD(screen);
            screen[position] = '-';
            position++;
            // 添加短暂延迟,降低CPU占用并让动画更直观
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者SX12C

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 18:20:39