如何解决 Kotlin/Native 在 Windows 下 main 函数的 args 乱码?

之前在用 Kotlin/Native 写 codex-kkp 的时候遇到了一个问题:

当我尝试在 Windows 的命令行上向它的产物 exe 传递参数的时候,传入的中文参数会变成我们熟悉又陌生的乱码"锟斤拷"。

bash 复制代码
codex-kkp-cli.exe "分析代码"
# 实际收到的参数变成了乱码

问题分析

那么为什么会这样呢?众所周知,"锟斤拷"系列的乱码通常是 GBK 和 UTF-8 之间的错误转码导致的。

而又众所周知,Windows 存在两套字符API:GBK 是 Windows 默认的中文系统编码(A 版本, ANSI),

UTF-16 则是内核的原生编码(W 版本, 宽、Unicode)。

参考文档:

而 Kotlin/Native mingwX64 平台 的 main 函数编译后会使用 ANSI 版本的API的入口点:

launcher.cpp#L72-L78

中的 Konan_main 函数:

CPP 复制代码
extern "C" RUNTIME_EXPORT int Konan_main(int argc, const char** argv) {
    return Init_and_run_start(argc, argv, 1);
}

StubIrDriver.kt#L225-L232

里面生成的 main 函数:

Kotlin 复制代码
out("extern int Konan_main(int argc, char** argv);")
out("")
out("__attribute__((__used__))")
out("int $entryPoint(int argc, char** argv)  {")
out("  return Konan_main(argc, argv);")
out("}")

也就是:

CPP 复制代码
extern int Konan_main(int argc, char** argv);

int main(int argc, char** argv) {
    return Konan_main(argc, argv);
}

它没有使用 wmainwchar_t** argv,所以它使用的是 ANSI 的 API 而不是 Unicode 的那个。

这个问题在 YouTrack 上也有相关记载:

在 KT-80201 中,也有热心网友贴出了解决方案,这也是接下来要进行介绍的内容。

解决方案

如果你比较熟悉 Windows 的 API,那么应该很快就能想到该如何了解。但是我就不一样了,我对这类 native 相关的东西一窍不通(

OK 言归正传,由于 Kotlin 的 main 函数接收到的 args 已经是处于乱码状态的错误参数,因此我们不能直接使用这个 args 了,

而是要用 Windows 的 W 版本 API 来直接获取通过 UTF-16 编码的正确参数,以此绕过 ANSI 的入口点带来的错误结果。

那么怎么绕开呢?说难也不难,我们可以直接通过 platform.windows.GetCommandLineW() 来获取 UTF-16 的命令行参数。

完整代码参考如下:

Kotlin 复制代码
fun getUnicodeArgs(): Array<String> = memScoped {
    // 获取原始的 UTF-16 命令行
    val commandLine = GetCommandLineW() ?: return@memScoped emptyArray()

    // 解析命令行为参数数组
    val argc = alloc<IntVar>()
    val argv = CommandLineToArgvW(commandLine.toKString(), argc.ptr)
        ?: return@memScoped emptyArray()

    try {
        val argCount = argc.value
        if (argCount <= 1) {
            // 只有程序名本身,没有其他参数
            return@memScoped emptyArray()
        }

        // 转换参数(跳过程序名)
        Array(argCount - 1) { index ->
            argv[index + 1]?.toKStringFromUtf16() ?: ""
        }
    } finally {
        LocalFree(argv)
    }
}

通过 GetCommandLineW 获取到W版本的命令行参数,然后通过 CommandLineToArgvW 将它们解析为参数数组,

随后将程序名之后的真正的 args 们通过 toKStringFromUtf16 转化为 Kotlin String 之后就得到了之最终我们需要的东西:

不乱码的 args 数组。

在一个 KMP 项目中,我们现在可以通过 expect/actual 来实现 mingwX64 平台下对参数的解析(至少我现在是这么做的):

Kotlin 复制代码
// commonMain - 声明期望函数
internal expect fun resolveArgs(args: Array<String>): Array<String>

// appleMain & linuxMain - 直接返回原参数(这些平台默认 UTF-8)
internal actual fun resolveArgs(args: Array<String>): Array<String> = args

// mingwMain - 使用 Windows Unicode API 重新获取参数
internal actual fun resolveArgs(args: Array<String>): Array<String> {
    // ... Unicode 处理逻辑
}

main 方法中:

Kotlin 复制代码
fun main(args: Array<String>) {
    val processedArgs = resolveArgs(args)
    // 接下来使用 processedArgs 而不是 args(你直接用 args 覆盖也行)
}

总结

根据 KT-80201 的状态,至少目前来看官方还没有解决这个问题。

如果你比较关心这个问题的话,可以追踪下这个 issue,跟踪它的未来进展。

相关推荐
Kapaseker12 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
A0微声z2 天前
Kotlin Multiplatform (KMP) 中使用 Protobuf
kotlin
alexhilton3 天前
使用FunctionGemma进行设备端函数调用
android·kotlin·android jetpack
lhDream3 天前
Kotlin 开发者必看!JetBrains 开源 LLM 框架 Koog 快速上手指南(含示例)
kotlin
RdoZam3 天前
Android-封装基类Activity\Fragment,从0到1记录
android·kotlin
埃博拉酱3 天前
VS Code Remote SSH 连接 Windows 服务器卡在"下载 VS Code 服务器":prcdn DNS 解析失败的诊断与 BITS 断点续传
windows·ssh·visual studio code
Kapaseker4 天前
研究表明,开发者对Kotlin集合的了解不到 20%
android·kotlin
唐宋元明清21884 天前
.NET 本地Db数据库-技术方案选型
windows·c#
郑州光合科技余经理4 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php
feifeigo1234 天前
matlab画图工具
开发语言·matlab