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

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

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

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

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

Итак, мой вопрос: как обрабатываются подписанные значения при их передаче функциям 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.


person andlabs    schedule 03.06.2014    source источник
comment
Я не работаю в Windows, но syscall_windows.go объявляет InvalidHandle как ^Handle(0) ( а Handle это uintptr). Итак, краткий ответ на мой эквивалент uintptr(-1)^uintptr(0) (должен применяться в равной степени к 32- и 64-разрядным версиям). Если есть ситуации, когда вам нужно другое низкоуровневое приведение, которое Go не включает в язык, вы можете сделать это с помощью unsafe: play.golang.org/p/JObvq_X5x0   -  person twotwotwo    schedule 04.06.2014
comment
Кроме того, вы можете создать значения uintptr с помощью переноса, если вы объявите их var вместо const и принудительно вычислите их. во время выполнения. Я думаю (размер uintptr) == (размер int) как на windows_386, так и на windows_amd64 (прямо для проверки!), Так что это не проблема. Вы также можете выполнить циклическую арифметику самостоятельно (даже в константах); этот ответ показывает, как.   -  person twotwotwo    schedule 04.06.2014
comment
Ваш первый комментарий заставил меня искать константы в системном вызове пакета ‹ -1; стандартные дескрипторы файлов такие, а автоматически сгенерированный код просто берет константы и выполняет с ними uintptr(int(x)) . Итак, я думаю, это мой ответ: пусть автогенератор сделает эти приведения перед записью выходного файла. Я не уверен, как интегрировать ваш комментарий в самостоятельный ответ, который я сделаю, как только смогу найти, где находится автоматически сгенерированный код системного вызова пакета, но тем временем спасибо! :D   -  person andlabs    schedule 05.06.2014
comment
Что касается вашего второго комментария, @twotwotwo, я сделал это до того, как переключился на автоматически сгенерированные константы; Я надеялся найти способ избежать этого :/   -  person andlabs    schedule 05.06.2014
comment
если подумать, я просто удалю TODO и просто использую negConst()   -  person andlabs    schedule 05.06.2014


Ответы (1)


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

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

Я написал программу (ниже), которая просто сбрасывала все константы из системного вызова пакета и искала константы, которые были отрицательными, но не == -1 (так как это было бы просто ^0). Стандартными дескрипторами файлов (STD_ERROR_HANDLE, STD_INPUT_HANDLE и STD_OUTPUT_HANDLE) являются (-12, -10 и -11 соответственно). Код в системном вызове пакета передает эти константы в качестве единственного аргумента getStdHandle(h int), который создает требуемый дескриптор файла для пакета os. getStdHandle() передает это значение int автоматически сгенерированной функции GetStdHandle(stdhandle int), которая заключает вызов в системный вызов GetStdHandle(). GetStdHandle() берет целое число и просто преобразует его в 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
    }
}
person andlabs    schedule 04.06.2014