O que são sinal de regras de extensão para chamar funções da API do Windows (stdcall)?Isto é necessário para chamar WInAPI de Ir, que é rigoroso sobre tipos int
Pergunta
Ops, não era uma coisa que eu esqueci que quando eu fiz esta resposta, e é algo de que eu sou não tem certeza sobre mim e que eu não consigo encontrar informações no MSDN e o Google e o Estouro de Pilha de pesquisa.
Há um número de lugares na API do Windows, onde você utilize um número negativo, ou um número muito grande para caber em um número inteiro assinado;por exemplo, CW_USEDEFAULT
, INVALID_HANDLE_VALUE
, GWLP_USERDATA
, e assim por diante.No mundo de C, está tudo lindo e perfeito:a linguagem inteiro promoção de regras de vir para o resgate.
Mas Vá, eu tenho que passar todos os meus argumentos para funções como uintptr
(o que é equivalente a C uintptr_t
).O valor de retorno da função é também devolvida a esta forma e, em seguida, será preciso comparar.Vá não permite inteiro promoção, e ele não permite que você para converter uma assinado expressão constante em um não assinados em tempo de compilação.
Agora, eu tenho um pouco mais de um jerry-rig conjunto para tratar essas constantes em minha Biblioteca de INTERFACE. (Aqui está um exemplo do que esta solução parece em ação. No entanto, eu não estou muito satisfeito com essa solução;parece-me como se fosse assumindo coisas sobre a ABI, e eu quero ter a certeza absoluta do que estou fazendo.
Então a minha pergunta é:como são valores assinados tratados ao passá-los para funções de API do Windows e como eles são tratados quando retornar?
Todos os meus constantes são gerado automaticamente (exemplo de saída).O autogenerator usa um ffi C, que eu prefiro não usar para o projeto principal, pois eu posso chamar DLLs diretamente (isto também faz cross-compilação mais fácil pelo menos para o resto do ano).Se eu pudesse de alguma forma de alavancagem que, por exemplo, fazendo tudo dentro de um C-variável do lado do formulário
uintptr_t x_CONST_NAME = (uintptr_t) (CONST_NAME);
que seria útil.Mas eu não posso fazer isso sem essa resposta.
Obrigado!
Atualização
Alguém no IRC colocá-lo de forma diferente (reformatada para evitar a rolagem horizontal):
[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
Basicamente isso, mas especificamente para o Windows API de interoperabilidade, para os parâmetros passados, e independentemente de uintptr tamanho.
Solução
@twotwotwo comentários à minha pergunta me apontou a direção certa.Se o Estouro de Pilha permitida a marcação de comentários, respostas e ter várias respostas marcadas, eu faria isso.
tl;dr versão:o que eu tenho agora é corrigir depois de tudo.
Eu escrevi um programa (abaixo) que simplesmente despejado todas as constantes do pacote syscall e olhou para constantes que foram negativos, mas não == -1 (como que poderia ser apenas ^0
).O padrão de identificadores de arquivo (STD_ERROR_HANDLE
, STD_INPUT_HANDLE
, e STD_OUTPUT_HANDLE
) são (-12, -10, e -11, respectivamente).O código no pacote syscall passa essas constantes como o único argumento de getStdHandle(h int)
, que produz o necessário identificador de ficheiro para o pacote de sistema operacional. getStdHandle()
passa este int para um geradas automaticamente a função de GetStdHandle(stdhandle int)
que envolve uma chamada para o GetStdHandle()
chamada de sistema. GetStdHandle()
leva o int e simplesmente converte-lo para uintptr
para passar para syscall.Syscall()
.Embora nenhuma explicação é dada no autogenerator de origem (mksyscall_windows.go), se este não trabalho, nem seria fmt.Println()
=P
Todos os itens acima é idêntico em ambos os windows/386 e windows/amd64;a única coisa em um processador específico do arquivo é GetStdHandle()
, mas o código é idêntico.
Meu negConst()
a função já está fazendo a mesma coisa, apenas mais diretamente.Como tal, posso presumir com segurança que ele está correto.
Obrigado!
// 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
}
}