assembly 使用Windows WinAPI函数写入控制台Unicode(UTF-16)文本?

xdyibdwo  于 2023-05-13  发布在  Windows
关注(0)|答案(1)|浏览(197)

我有一个64位的masm代码,输出到控制台。问题是,通过使用WriteConsoleW,我不能重定向命令或任何东西的输出,因为它只写入控制台缓冲区。但是使用WriteFile会在每个字符之间增加空格,因为16位字符的高位被清零。如何使用WriteFile打印Unicode文本?
我读到here,我可以使用BOM,但这对我不起作用(我添加了另一个WriteFile调用,在第二个WriteFile调用之前写入两个字节FF FE,但它只是打印了一个白色矩形,没有其他内容)。
代码如下:

extern GetStdHandle: proc
extern WriteConsoleW: proc
.data?
    written dq ?
.data
    string dw 0048h,0065h,006ch,006ch,006fh,0020h,0057h,006fh,0072h,006ch,0064h,0021h
    len equ $-string
.code
main proc
    push    rbp
    mov rbp, rsp
    sub rsp, 020h
    and rsp, -10h

    mov rcx, -11
    call    GetStdHandle
    mov rcx, rax
    mov rdx, offset string
    mov r8, len
    mov r9, written
    call    WriteConsoleW

    add rsp, 020h
    mov rsp, rbp
    pop rbp
    ret
main endp
end

当我将WriteConsoleW交换为WriteFile时,它在通过visual studio运行时可以正确打印,但是当我从命令行运行生成的exe时,它打印的不是Hello World!,而是H e l l o W o r l d !
有人知道怎么处理吗?
编辑:我不知道如何理解这一点,但不知何故,当我使用WriteFile代替,16位字符得到打印错误,只有当我单独执行程序。但是,当我将输出重定向到echo命令时,它会正常打印:

sgtfey8w

sgtfey8w1#

C中的相同API产生相同的控制台输出。WriteConsoleW执行WriteFile不执行的到控制台的字符转换。WriteFile只是将字节发送到控制台,控制台在当前代码页中解释它们,对我来说是437(OEM美国)。
我可以通过调用SetConsoleOutputCP(65001)(将控制台代码页设置为UTF-8),然后编写一个UTF-8字符串来让它在C
中工作。请注意,list of code page identifiers包含UTF-16,但它仅适用于托管应用程序(例如C#)。
我打印了一些非ASCII码,看看它是否正确。

// compiled with MSVS "cl /W4 /utf-8 test.cpp"
// source saved in UTF-8 as well.
#include <windows.h>

int main() {
    char s[] = u8"Hello, 马克"; // Note: need a chinese font, but cut/paste
                               // to Notepad and you'll see them if you don't.
    SetConsoleOutputCP(65001);
    auto h = GetStdHandle(STD_OUTPUT_HANDLE);
    DWORD written;
    WriteFile(h, s, sizeof(s), &written, nullptr);
}

输出:

Hello, 马克

您应该能够轻松地将其应用于MASM。
如果您愿意使用C运行时库,那么如果您适当地设置了控制台和文件模式,则这些API都适用于UTF-16:

#include <stdio.h>
#include <io.h>
#include <fcntl.h>

int main()
{
    _setmode(_fileno(stdout), _O_U16TEXT);
    wchar_t s[] = L"Hello, 马克!";
    _write(_fileno(stdout), s, sizeof(s));
    int fd = _open("test.txt", _O_CREAT | _O_WRONLY | _O_U16TEXT);
    _write(fd, s, sizeof(s));
    _close(fd);
}

输出到控制台:

Hello, 马克!

Output to test.txt encoded in UTF-16LE. Note that 马克 is the two unicode code points U+9A5C and U+514B:

编辑

下面是GetFileType的演示。如果运行,它将正确写入控制台。如果重定向到文件,例如“test > out.txt”,输出文件包含UTF-16 LE编码的数据。

#include <windows.h>

int main()
{
    auto h = GetStdHandle(STD_OUTPUT_HANDLE);
    auto type = GetFileType(h);
    
    WCHAR s[] = L"Only 20\u20AC!";  // U+20AC is EURO sign.
    DWORD written;
    
    if(type == FILE_TYPE_DISK)
        WriteFile(h, s, sizeof(s) - sizeof(WCHAR) /* don't send the null */, &written, nullptr);
    else
        WriteConsoleW(h, s, sizeof(s) / sizeof(WCHAR) - 1, &written, nullptr);
}

输出到控制台:

Only 20€!

输出重定向到out.txt

相关问题