Каковы правила расширения sign для вызова функций Windows API (stdcall)?Это необходимо для вызова WinAPI из Go, который строго относится к типам int

StackOverflow https://stackoverflow.com//questions/24022225

Вопрос

Упс, была одна вещь, о которой я забыл, когда готовил этот ответ, и это то, в чем я сам не совсем уверен и по чему, похоже, не могу найти информацию в MSDN, Google и поиске Stack Overflow.

В Windows API есть несколько мест, где вы используете отрицательное число или число, слишком большое, чтобы вместиться в целое число со знаком;например, CW_USEDEFAULT, INVALID_HANDLE_VALUE, GWLP_USERDATA, и так далее.В мире C все прекрасно и денди:на помощь приходят правила целочисленного продвижения языка.

Но в Go я должен передать все свои аргументы функциям следующим образом uintptr (что эквивалентно C's uintptr_t).Возвращаемое значение из функции также возвращается таким образом, и затем мне нужно будет сравнить.Go не разрешает целочисленное продвижение, и это не позволяет вам преобразовывать подписанное постоянное выражение в неподписанное во время компиляции.

Прямо сейчас у меня есть что-то вроде канистры настроен для обработки этих констант в моем Библиотека пользовательского интерфейса. (Вот пример того, как это решение выглядит в действии.) Однако я не совсем удовлетворен этим решением;мне кажется, что это предполагает что-то о ABI, и я хочу быть абсолютно уверен в том, что я делаю.

Итак, мой вопрос заключается в следующем:как обрабатываются подписанные значения при передаче их функциям Windows API и как они обрабатываются при возврате?

Все мои константы следующие автоматически сгенерированный (пример вывода).Автогенератор использует a 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.

Это было полезно?

Решение

Комментарии @twotwotwo к моему вопросу указали мне правильное направление.Если бы переполнение стека позволяло помечать комментарии как ответы и отмечать несколько ответов, я бы так и сделал.

версия tl;dr:в конце концов, то, что у меня есть сейчас, правильно.

Я написал программу (ниже), которая просто выгружала все константы из системного вызова пакета и искала константы, которые были отрицательными, но не == -1 (поскольку это было бы просто ^0).Стандартные дескрипторы файлов (STD_ERROR_HANDLE, STD_INPUT_HANDLE, и STD_OUTPUT_HANDLE) равны (-12, -10 и -11 соответственно).Код в package syscall передает эти константы в качестве единственного аргумента getStdHandle(h int), который создает требуемый дескриптор файла для package 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
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top