使用-ldflags -H = windowsgui编译golang应用程序时,将输出打印到命令窗口

时间:2014-05-19 16:56:16

标签: go

我有一个通常在后台运行的应用程序,所以我用

编译它
go build -ldflags -H=windowsgui <gofile>

要在命令行检查版本,我想将-V标志传递给命令行以获取保存要打印到版本命令提示符的字符串,然后让应用程序退出。我添加了标志包和代码。当我用

进行测试时
go run <gofile> -V

...打印版本很好。当我编译exe时,它只是退出,什么都不打印。我怀疑它是编译标志,导致它无法访问控制台并将我的文本发送到位桶。

我已尝试使用println和fprintf以及os.stderr.write打印到stderr和stdout的变体,但编译的应用程序中没有任何内容。在使用这些标志编译时,我应该如何尝试将字符串打印到命令提示符?

5 个答案:

答案 0 :(得分:20)

问题是,当使用the "subsystem" variable设置为&#34; Windows&#34;中的PE header的可执行文件创建进程时,该进程已关闭其three standard handles它没有任何控制台 - 无论你是否从控制台运行它都无关紧要。 (事实上​​,如果您从控制台运行其子系统设置为&#34; console&#34; 而不是的可执行文件,则会强制为该进程创建一个控制台,并将该进程附加到该进程 - 你通常会把它看作是一个突然出现的控制台窗口。)

因此,要从Windows上的GUI进程向控制台打印任何内容,您必须将该进程显式连接到附加到其父进程的控制台(如果有的话),例如解释here 。为此,请调用AttachConsole API函数。使用Go,可以使用syscall包完成此操作:

package main

import (
    "fmt"
    "syscall"
)

const (
    ATTACH_PARENT_PROCESS = ^uint32(0) // (DWORD)-1
)

var (
    modkernel32 = syscall.NewLazyDLL("kernel32.dll")

    procAttachConsole = modkernel32.NewProc("AttachConsole")

)

func AttachConsole(dwParentProcess uint32) (ok bool) {
    r0, _, _ := syscall.Syscall(procAttachConsole.Addr(), 1, uintptr(dwParentProcess), 0, 0)
    ok = bool(r0 != 0)
    return
}

func main() {
    ok := AttachConsole(ATTACH_PARENT_PROCESS)
    if ok {
        fmt.Println("Okay, attached")
    }
}

要真正完成,当AttachConsole()失败时,此代码应该采用以下两种路线之一:

  • 调用AllocConsole()为其创建自己的控制台窗口。

    它说这对于显示版本信息几乎没用,因为该过程通常在打印后退出,并且最终的用户体验将是一个控制台窗口弹出并立即消失;高级用户会得到一个提示,他们应该从控制台重新运行该应用程序,但凡人都不可能应对。

  • 发布显示相同信息的GUI对话框。

    我认为这正是所需要的:请注意,显示帮助/使用消息以响应用户指定某些命令行参数通常与控制台精神上相关联但是这不是一个可以遵循的教条:例如,尝试在控制台上运行msiexec.exe /?,看看会发生什么。

答案 1 :(得分:3)

上面的答案很有帮助,但唉它开箱即用对我不起作用。经过一些额外的研究后,我来到了这段代码:

var express = require('express'); // Get the module
var app = express(); // Create express by calling the prototype in var express
var http = require('http').Server(app);
var io = require('socket.io')(http);
// Import events module
var events = require('events');
// Create an eventEmitter object
var eventEmitter = new events.EventEmitter();
var config = require('./config.json');

app.get('/', function(req, res){
  res.sendFile(__dirname + '/view/index.html');
});

io.on('connection', function(socket){
  socket.on('chat message', function(msg){
    io.emit('chat message', msg, {username: socket.username});
  });
  socket.on('connection info', function(usr){
    socket.username = usr;
    io.emit('user add', {username: usr});
  });
});

io.on('connection', function(socket){
        console.log('Passenger Pigeon >> a user connected');
    socket.on('disconnect', function(){
        socket.broadcast.emit('user left', {
            username: socket.username,
        });
        console.log('Passenger Pigeon >> user disconnected');
  });
});

http.listen(process.env.PORT || config.port, function(){
  console.log('listening on *:' + config.port);
});

关键点:在分配/附加控制台之后,需要获取stdout句柄,使用此句柄打开文件并将其分配给os.Stdout变量。如果你需要stdin,你必须为stdin重复相同的操作。

答案 2 :(得分:1)

您可以在不使用-H = windowsgui的情况下获得所需的行为;你基本上创建一个标准应用程序(有自己的控制台窗口),并隐藏它直到程序退出。

func Console(show bool) {
    var getWin = syscall.NewLazyDLL("kernel32.dll").NewProc("GetConsoleWindow")
    var showWin = syscall.NewLazyDLL("user32.dll").NewProc("ShowWindow")
    hwnd, _, _ := getWin.Call()
    if hwnd == 0 {
            return
    }
    if show {
       var SW_RESTORE uintptr = 9
       showWin.Call(hwnd, SW_RESTORE)
    } else {
       var SW_HIDE uintptr = 0
       showWin.Call(hwnd, SW_HIDE)
    }
}

然后像这样使用它:

func main() {
    Console(false)
    defer Console(true)
    ...
    fmt.Println("Hello World")
    ...
}

答案 3 :(得分:0)

此处已发布的解决方案的一个问题是,它们将 all 输出重定向到控制台,因此,如果我运行./myprogram >file,则到file的重定向会丢失。我编写了一个新模块github.com/apenwarr/fixconsole,它避免了这个问题。您可以像这样使用它:

import (
        "fmt"
        "github.com/apenwarr/fixconsole"
        "os"
)

func main() {
        err := fixconsole.FixConsoleIfNeeded()
        if err != nil {
                fmt.Fatalf("FixConsoleOutput: %v\n", err)
        }
        os.Stdout.WriteString(fmt.Sprintf("Hello stdout\n"))
        os.Stderr.WriteString(fmt.Sprintf("Hello stderr\n"))
}

答案 4 :(得分:0)

如果您构建无窗口应用程序,则可以使用PowerShell命令Out-String获得输出

.\\main.exe | out-string

您的构建命令可能类似于:

cls; go build -i -ldflags -H=windowsgui main.go; .\\main.exe | out-string;

cls; go run -ldflags -H=windowsgui main.go | out-string

不需要棘手的系统调用,也不需要内核DLL!