"The handle is invalid" error when calling GetFileInformationByHandle with handle initialized with GetFileVersionInfoSize

huangapple go评论79阅读模式
英文:

"The handle is invalid" error when calling GetFileInformationByHandle with handle initialized with GetFileVersionInfoSize

问题

我正在尝试在Windows上使用Go编程来获取文件信息,包括文件创建时间。

我在golang.org/x/sys/windows中找到了一个函数,可以返回有关文件创建时间的信息,该函数是GetFileInformationByHandleGo文档Windows API文档)。然而,我使用这个函数编写的代码给我返回了一个The handle is invalid错误。

这是我的代码:

package main

import (
	"log"
	"os"

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

func main() {
	name := `C:\Windows\System32\cmd.exe`

	fileInfo, err := os.Stat(name)
	if err != nil {
		log.Fatalf("无法获取 %s 的信息:%s", name, err.Error())
	}

	log.Printf("%s 的基本名称是 %s。\n", name, fileInfo.Name())

	var handle windows.Handle
	_, err = windows.GetFileVersionInfoSize(name, &handle)
	if err != nil && err != windows.ERROR_FILE_NOT_FOUND && err != windows.ERROR_RESOURCE_TYPE_NOT_FOUND {
		log.Fatalf("GetFileVersionInfoSize 错误:路径:%s %s", name, err.Error())
	}

	var hndlFileInfo windows.ByHandleFileInformation
	err = windows.GetFileInformationByHandle(handle, &hndlFileInfo)
	if err != nil {
		if err == windows.ERROR_INVALID_HANDLE { // https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
			log.Println("错误是无效的句柄错误。")
		}
		log.Fatalf("GetFileInformationByHandle 错误:路径:%s %s", name, err.Error())
	}

	log.Println("成功!")
}

当我运行这段代码时,我得到以下输出:

2023/01/11 14:43:19 C:\Windows\System32\cmd.exe 的基本名称是 cmd.exe。
2023/01/11 14:43:19 错误是无效的句柄错误。
2023/01/11 14:43:19 GetFileInformationByHandle 错误:路径:C:\Windows\System32\cmd.exe 句柄无效。

我已确认文件路径是有效的(而且os.Stat调用没有返回错误):

"The handle is invalid" error when calling GetFileInformationByHandle with handle initialized with GetFileVersionInfoSize

我知道System32目录对于32位的Windows程序是不可见的,但我已经验证了我的可执行文件是一个64位程序,使用git-bash上的file工具进行验证:

$ file win_handle_test.exe
win_handle_test.exe: PE32+ executable (console) x86-64 (stripped to external PDB), for MS Windows

由于路径应该是有效的,我可能做错了什么导致句柄无效?

英文:

I am trying to programmatically obtain file information with Go on Windows, including the time the file was created.

I found a function in golang.org/x/sys/windows that returns something with information on when a file is created is GetFileInformationByHandle (Go docs, Windows API docs). However, the code I wrote using this function gives me a The handle is invalid error.

Here is my code:

package main

import (
	"log"
	"os"

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

func main() {
	name := `C:\Windows\System32\cmd.exe`

	fileInfo, err := os.Stat(name)
	if err != nil {
		log.Fatalf("Unable to stat %s: %s", name, err.Error())
	}

	log.Printf("%s has base name %s.\n", name, fileInfo.Name())

	var handle windows.Handle
	_, err = windows.GetFileVersionInfoSize(name, &handle)
	if err != nil && err != windows.ERROR_FILE_NOT_FOUND && err != windows.ERROR_RESOURCE_TYPE_NOT_FOUND {
		log.Fatalf("GetFileVersionInfoSize error: path: %s %s", name, err.Error())
	}

	var hndlFileInfo windows.ByHandleFileInformation
	err = windows.GetFileInformationByHandle(handle, &hndlFileInfo)
	if err != nil {
		if err == windows.ERROR_INVALID_HANDLE { // https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
			log.Println("Error is invalid handle error.")
		}
		log.Fatalf("GetFileInformationByHandle error: path: %s %s", name, err.Error())
	}

	log.Println("Success!")
}

When I run this, I get the following output:

2023/01/11 14:43:19 C:\Windows\System32\cmd.exe has base name cmd.exe.
2023/01/11 14:43:19 Error is invalid handle error.
2023/01/11 14:43:19 GetFileInformationByHandle error: path: C:\Windows\System32\cmd.exe The handle is invalid.

I have confirmed that the file path is valid (plus the os.Stat call does not return an error):

"The handle is invalid" error when calling GetFileInformationByHandle with handle initialized with GetFileVersionInfoSize

I know the System32 directory is not visible for 32-bit Windows programs, but I have verified that my executable is a 64-bit program with the file tool on git-bash:

$ file win_handle_test.exe
win_handle_test.exe: PE32+ executable (console) x86-64 (stripped to external PDB), for MS Windows

Since the path should be valid, what could I be doing wrong so that I am getting an invalid handle?

答案1

得分: 1

看起来Go语言的syscall包原生支持你所需要的功能:

//go:build windows

package main

import (
	"fmt"
	"log"
	"os"
	"syscall"
)

func main() {
	log.SetFlags(0)

	fi, err := os.Stat(`C:\Windows\system32\cmd.exe`)
	if err != nil {
		log.Fatal(err)
	}

	fad, ok := fi.Sys().(*syscall.Win32FileAttributeData)
	if !ok {
		log.Println("没有Win32本地数据")
	} else {
		fmt.Println(
			"文件属性:", fad.FileAttributes,
			"创建时间:", fad.CreationTime,
			"最后访问时间:", fad.LastAccessTime,
			"最后写入时间:", fad.LastWriteTime,
			"文件大小高位:", fad.FileSizeHigh,
			"文件大小低位:", fad.FileSizeLow,
		)
	}
}

在我的系统上,我执行了以下命令:
filever$ GOOS=windows go build
filever$ wine ./filever.exe
输出结果为:
文件属性:32 创建时间:{868251897 31002635} 最后访问时间:{1168986901 31008247} 最后写入时间:{868251897 31002635} 文件大小高位:0 文件大小低位:795516

英文:

Looks like Go has native support for what you're after right in its stock syscall package:

//go:build windows

package main

import (
	"fmt"
	"log"
	"os"
	"syscall"
)

func main() {
	log.SetFlags(0)

	fi, err := os.Stat(`C:\Windows\system32\cmd.exe`)
	if err != nil {
		log.Fatal(err)
	}

	fad, ok := fi.Sys().(*syscall.Win32FileAttributeData)
	if !ok {
		log.Println("No win32-native data")
	} else {
		fmt.Println(
			"FileAttributes:", fad.FileAttributes,
			"CreationTime:", fad.CreationTime,
			"LastAccessTime:", fad.LastAccessTime,
			"LastWriteTime:", fad.LastWriteTime,
			"FileSizeHigh:", fad.FileSizeHigh,
			"FileSizeLow:", fad.FileSizeLow,
		)
	}
}

On my system, I have:
filever$ GOOS=windows go build
filever$ wine ./filever.exe
FileAttributes: 32 CreationTime: {868251897 31002635} LastAccessTime: {1168986901 31008247} LastWriteTime: {868251897 31002635} FileSizeHigh: 0 FileSizeLow: 795516

答案2

得分: 0

我找到了另一种获取创建时间的方法,基于这个答案

package main

import (
	"log"
	"os"
	"time"
	"syscall"
)

func main() {
	name := `C:\Windows\System32\cmd.exe`

	fileInfo, err := os.Stat(name)
	if err != nil {
		log.Fatalf("无法获取 %s 的状态:%s", name, err.Error())
	}

	log.Printf("%s 的基本名称为 %s。\n", name, fileInfo.Name())

	data := fileInfo.Sys().(*syscall.Win32FileAttributeData)
	cTime := time.Unix(0, data.CreationTime.Nanoseconds())

	log.Printf("cTime 在 UTC 时区的时间为:%v\n", cTime.UTC())
	log.Printf("cTime 在本地时区的时间为:%v\n", cTime)
}

输出:

2023/01/11 15:03:58 C:\Windows\System32\cmd.exe 的基本名称为 cmd.exe。
2023/01/11 15:03:58 cTime 在 UTC 时区的时间为:2022-05-10 17:34:57.9429156 +0000 UTC
2023/01/11 15:03:58 cTime 在本地时区的时间为:2022-05-10 13:34:57.9429156 -0400 EDT

这个输出与文件的属性视图中显示的创建时间相匹配。

尽管FILETIME本身是自1601年1月1日以来的时间,以UTC为准,但time.UnixNanoseconds函数都是基于自1970年1月1日以来的时间,以UTC为准。

英文:

I found an alternate way to get the creation time, based on this answer:

package main

import (
	"log"
	"os"
	"time"
	"syscall"
)

func main() {
	name := `C:\Windows\System32\cmd.exe`

	fileInfo, err := os.Stat(name)
	if err != nil {
		log.Fatalf("Unable to stat %s: %s", name, err.Error())
	}

	log.Printf("%s has base name %s.\n", name, fileInfo.Name())

	data := fileInfo.Sys().(*syscall.Win32FileAttributeData)
	cTime := time.Unix(0, data.CreationTime.Nanoseconds())

	log.Printf("cTime in UTC           : %v\n", cTime.UTC())
	log.Printf("cTime in local timezone: %v\n", cTime)
}

Output:

2023/01/11 15:03:58 C:\Windows\System32\cmd.exe has base name cmd.exe.
2023/01/11 15:03:58 cTime in UTC           : 2022-05-10 17:34:57.9429156 +0000 UTC
2023/01/11 15:03:58 cTime in local timezone: 2022-05-10 13:34:57.9429156 -0400 EDT

This output matches the created time in the properties view for the file.

Despite the FILETIME itself being in time since January 1, 1601 UTC, both time.Unix and the Nanoseconds function are based on time since January 1, 1970 UTC.

huangapple
  • 本文由 发表于 2023年1月12日 03:38:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/75088192.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定