5

糟糕,当我做出这个答案时,我忘记了一件事,这是我对自己不太确定的事情,而且我似乎无法在 MSDN 和 Google 以及 Stack Overflow 搜索中找到相关信息。

在 Windows API 中有很多地方使用负数,或者数字太大而无法放入有符号整数;例如,CW_USEDEFAULTINVALID_HANDLE_VALUEGWLP_USERDATA,等等。在 C 的世界里,一切都很好,花花公子:语言的整数提升规则来拯救。

但是在 Go 中,我必须将所有参数传递给函数 as uintptr(相当于 C 的uintptr_t)。函数的返回值也是这样返回的,然后我需要比较一下。Go 不允许整数提升,也不允许您在编译时将有符号常量表达式转换为无符号常量。

现在,我在我的UI 库中设置了一些 jerry-rig来处理这些常量。(这里是这个解决方案实际效果的一个例子。)但是,我对这个解决方案不太满意;在我看来,它就像在假设关于 ABI 的事情,我想绝对确定我在做什么。

所以我的问题是:将签名值传递给 Windows API 函数时如何处理它们以及返回时如何处理它们?

我所有的常量都是自动生成的(示例输出)。自动生成器使用C ffi,我不想将其用于主项目,因为我可以直接调用 DLL(这也使交叉编译更容易,至少在今年剩下的时间里)。如果我能以某种方式利用它,例如通过将所有内容都变成表单的 C 端变量

uintptr_t x_CONST_NAME = (uintptr_t) (CONST_NAME);

那会很有帮助。但如果没有这个答案,我不能这样做。

谢谢!

更新

IRC 上有人说不同(重新格式化以避免水平滚动):

[19:13] <FraGag> basically, you're asking whether an int with a value of -1
                 will be returned as 0x00000000FFFFFFFF or as 0xFFFFFFFFFFFFFFFF
                 if an int is 4 bytes and an uintptr is 8 bytes

基本上是这样,但专门用于 Windows API 互操作,用于传入的参数,并且不管 uintptr 大小。

4

1 回答 1

0

@twotwotwo 对我的问题的评论为我指明了正确的方向。如果 Stack Overflow 允许将评论标记为答案并标记多个答案,我会这样做。

tl;博士版本:我现在所拥有的毕竟是正确的。

我编写了一个程序(如下),它简单地从包 syscall 中转储所有常量,并查找负数的常量,但不是 == -1(因为那只是^0)。标准文件句柄(STD_ERROR_HANDLESTD_INPUT_HANDLESTD_OUTPUT_HANDLE)分别为(-12、-10 和 -11)。syscall 包中的代码将这些常量作为 的唯一参数传递getStdHandle(h int),从而为包 os 生成所需的文件句柄。getStdHandle()将此 int 传递给自动生成的函数,该函数GetStdHandle(stdhandle int)包装对GetStdHandle()系统调用的调用。GetStdHandle()获取 int 并仅将其转换为uintptr用于传递到syscall.Syscall(). 尽管自动生成器的源代码 (mksyscall_windows.go) 中没有给出解释,但如果这不起作用,那么fmt.Println()=P

以上所有在 windows/386 和 windows/amd64 上都是相同的;特定于处理器的文件中唯一的内容是GetStdHandle(),但相关代码是相同的。

我的negConst()功能已经在做同样的事情,只是更直接。因此,我可以放心地假设它是正确的。

谢谢!

// 4 june 2014
// based on code from 24 may 2014
package main

import (
    "fmt"
    "os"
    "strings"
    "go/token"
    "go/ast"
    "go/parser"
    "code.google.com/p/go.tools/go/types"
    _ "code.google.com/p/go.tools/go/gcimporter"
)

var arch string

func getPackage(path string) (typespkg *types.Package, pkginfo types.Info) {
    var pkg *ast.Package

    fileset := token.NewFileSet()       // parser.ParseDir() actually writes to this; not sure why it doesn't return one instead
    filter := func(i os.FileInfo) bool {
        if strings.Contains(i.Name(), "_windows") &&
            strings.Contains(i.Name(), "_" + arch) &&
            strings.HasSuffix(i.Name(), ".go") {
            return true
        }
        if i.Name() == "race.go" ||     // skip these
            i.Name() == "flock.go" {
            return false
        }
        return strings.HasSuffix(i.Name(), "_windows.go") ||
            (!strings.Contains(i.Name(), "_"))
    }
    pkgs, err := parser.ParseDir(fileset, path, filter, parser.AllErrors)
    if err != nil {
        panic(err)
    }
    for k, _ := range pkgs {        // get the sole key
        if pkgs[k].Name == "syscall" {
            pkg = pkgs[k]
            break
        }
    }
    if pkg == nil {
        panic("package syscall not found")
    }
    // we can't pass pkg.Files directly to types.Check() because the former is a map and the latter is a slice
    ff := make([]*ast.File, 0, len(pkg.Files))
    for _, v := range pkg.Files {
        ff = append(ff, v)
    }
    // if we don't make() each map, package types won't fill the structure
    pkginfo.Defs = make(map[*ast.Ident]types.Object)
    pkginfo.Scopes = make(map[ast.Node]*types.Scope)
    typespkg, err = new(types.Config).Check(path, fileset, ff, &pkginfo)
    if err != nil {
        panic(err)
    }
    return typespkg, pkginfo
}

func main() {
    pkgpath := "/home/pietro/go/src/pkg/syscall"
    arch = os.Args[1]

    pkg, _ := getPackage(pkgpath)
    scope := pkg.Scope()
    for _, name := range scope.Names() {
        obj := scope.Lookup(name)
        if obj == nil {
            panic(fmt.Errorf("nil object %q from scope %v", name, scope))
        }
        if !obj.Exported() {        // exported names only
            continue
        }
        if _, ok := obj.(*types.Const); ok {
            fmt.Printf("egrep -rh '#define[     ]+%s' ~/winshare/Include/ 2>/dev/null\n", obj.Name())
        }
        // otherwise skip
    }
}
于 2014-06-04T22:13:30.087 回答