Golang中sys包与I/O操作相关的问题求助

Golang中sys包与I/O操作相关的问题求助 由于我想访问一些底层API来使用CreateFile函数执行I/O操作 syscall.CreateFile( name *uint16…) 在此过程中,我遇到了一个问题:name参数的类型是*uint16,但它应该是一个数组([]uint16),以便能够处理UTF-16格式的字符串。正如我们在微软提供的示例中看到的 -> 链接,其中TEXT宏将字符串转换为wchar_t数组,或者我们可以说[]uint16。

提前感谢,如果我说的有误请见谅,因为我在这个领域只是个初学者。


更多关于Golang中sys包与I/O操作相关的问题求助的实战教程也可以访问 https://www.itying.com/category-94-b0.html

10 回复

谢谢,先生,帮了大忙。

更多关于Golang中sys包与I/O操作相关的问题求助的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


感谢,我只是Go语言的初学者,不了解这种情况 😅。感谢指出我的错误。

啊,原来是这样!我以前知道这个,但忘记了! 图片

当您有一个 []utf16 文件名时,需要像 &fileName[0] 这样传递它。在 C 语言中,数组会“退化”为指针。而在 Go 语言中,您必须手动完成此操作。

要使用这些底层API,我认为你必须使用unsafe包,并编写类似于这段C代码的Go代码

为什么不使用官方的Go解决方案呢?

windows 包

import "golang.org/x/sys/windows"

我正在撰写一篇关于如何在GoLang中编写多线程复制程序的博客,这就是我这样做的原因。我想比较实现复制操作的各种方法的性能。例如使用io.copy、os.readfile,以及最后使用依赖于操作系统的底层API。

我能够运行程序,主要问题在于我无法传递正确的文件名,因为该函数只接受单个UTF-16字符,而我想发送一个数组。

像这样的底层不安全 API 可能很难正确使用。我很好奇,为什么你自称是该领域的新手,却首先尝试使用它们呢? 🙂

要使用这些底层 API,我认为你必须使用 unsafe 包,并编写类似于以下 C 代码的 Go 代码:https://gist.github.com/xebecnan/6d070c93fb69f40c3673

skillian: 当你有一个 []utf16 文件名时,你需要像 &fileName[0] 这样传递它。

官方的 Go 解决方案:

Package windows

import "golang.org/x/sys/windows"

func UTF16PtrFromString

func UTF16PtrFromString(s string) (*uint16, error)

UTF16PtrFromString 返回指向 UTF-8 字符串 s 的 UTF-16 编码的指针,并添加了终止 NUL 字符。如果 s 在任何位置包含 NUL 字节,则返回 (nil, syscall.EINVAL)。

我想通过 CreateFile 函数访问一些底层 API 来进行 I/O 操作 syscall.CreateFile( name *uint16…)

我不明白。

这是一个众所周知的问题,并且已经解决了。为什么不使用官方的 Go 解决方案来提供 name *uint16 呢?

Package windows

import "golang.org/x/sys/windows"

func CreateFile

func CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile Handle) (handle Handle, err error)

func UTF16PtrFromString

func UTF16PtrFromString(s string) (*uint16, error)

UTF16PtrFromString 返回指向 UTF-8 字符串 s 的 UTF-16 编码的指针,并添加了终止 NUL。如果 s 在任何位置包含 NUL 字节,则返回 (nil, syscall.EINVAL)。

在Go中使用syscall.CreateFile处理UTF-16字符串时,确实需要将字符串转换为*uint16类型。虽然参数类型显示为*uint16,但实际上它指向的是UTF-16编码的字符数组(相当于C语言中的wchar_t*)。

以下是正确的转换方法:

package main

import (
    "golang.org/x/sys/windows"
    "syscall"
    "unsafe"
)

func main() {
    // 要打开的文件名(支持Unicode)
    fileName := "C:\\test\\文件.txt"
    
    // 将Go字符串转换为UTF-16指针
    utf16FileName, err := syscall.UTF16PtrFromString(fileName)
    if err != nil {
        panic(err)
    }
    
    // 调用CreateFile
    handle, err := windows.CreateFile(
        utf16FileName,                    // 文件名(UTF-16)
        windows.GENERIC_READ,             // 访问模式
        windows.FILE_SHARE_READ,          // 共享模式
        nil,                              // 安全属性
        windows.OPEN_EXISTING,            // 创建方式
        windows.FILE_ATTRIBUTE_NORMAL,    // 文件属性
        0,                                // 模板文件句柄
    )
    
    if err != nil {
        panic(err)
    }
    
    defer windows.CloseHandle(handle)
    
    // 使用文件句柄进行后续操作...
}

或者,如果你需要手动转换字符串,可以使用以下方法:

func stringToUTF16Ptr(s string) *uint16 {
    // 将字符串转换为UTF-16切片
    utf16 := syscall.StringToUTF16(s)
    
    // 返回切片第一个元素的指针
    return &utf16[0]
}

// 使用示例
fileNamePtr := stringToUTF16Ptr("C:\\test\\example.txt")
handle, err := windows.CreateFile(
    fileNamePtr,
    windows.GENERIC_READ | windows.GENERIC_WRITE,
    0,
    nil,
    windows.CREATE_ALWAYS,
    windows.FILE_ATTRIBUTE_NORMAL,
    0,
)

关键点:

  1. syscall.UTF16PtrFromString() 会自动处理字符串到UTF-16指针的转换
  2. 返回的指针指向一个以null结尾的UTF-16字符串数组
  3. Windows API期望的是以null结尾的宽字符串,这正是UTF16PtrFromString提供的格式

对于需要传递字符串数组(多个字符串)的情况,可以使用:

import "golang.org/x/sys/windows"

// 多个文件名的示例
fileNames := []string{"file1.txt", "file2.txt"}
var namePtrs []*uint16

for _, name := range fileNames {
    ptr, err := syscall.UTF16PtrFromString(name)
    if err != nil {
        panic(err)
    }
    namePtrs = append(namePtrs, ptr)
}

// 现在namePtrs就是[]*uint16,可以传递给需要多个文件名的API

这样就能正确处理UTF-16编码的文件名,并兼容Windows的Unicode API。

回到顶部