Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow disabling the console in --compile on windows. (/SUBSYSTEM:WINDOWS) #13084

Closed
716774 opened this issue Aug 5, 2024 · 12 comments · Fixed by #15894
Closed

Allow disabling the console in --compile on windows. (/SUBSYSTEM:WINDOWS) #13084

716774 opened this issue Aug 5, 2024 · 12 comments · Fixed by #15894
Labels
bundler Something to do with the bundler enhancement New feature or request good first issue Something that would be good for new contributors windows An issue that is known to occur on Windows

Comments

@716774
Copy link

716774 commented Aug 5, 2024

What is the problem this feature would solve?

Windows GUI

What is the feature you are proposing to solve the problem?

golang > go build -ldflags="-s -w -H windowsgui"

The terminal window will not be displayed.

What alternatives have you considered?

No response

@716774 716774 added the enhancement New feature or request label Aug 5, 2024
@paperclover
Copy link
Member

this is suggesting allowing a flag to set /SUBSYSTEM to WINDOWS? i'm not familiar with go's linker flags.

@716774
Copy link
Author

716774 commented Aug 5, 2024

this is suggesting allowing a flag to set /SUBSYSTEM to WINDOWS? i'm not familiar with go's linker flags.

Yes, currently I am using editbin.exe to modify the compiled exe of bun to /SUBSYSTEM:WINDOWS
I hope to add a --windowsgui parameter in the bun build to set /SUBSYSTEM:WINDOWS during the compilation process

Golang is like this .

I love bun ~

@paperclover paperclover changed the title Suggest adding windowsgui to the build Allow disabling the console in --compile on windows. (/SUBSYSTEM:WINDOWS) Aug 5, 2024
@paperclover paperclover added the bundler Something to do with the bundler label Aug 5, 2024
@paperclover
Copy link
Member

editing title so its searchable. related is #10823, which also involves editing the binary after bundling

@paperclover paperclover added the windows An issue that is known to occur on Windows label Aug 5, 2024
@codenoid
Copy link

@716774 hi what is editbin.exe ? how can I use that to hide console on my windows app?

@716774
Copy link
Author

716774 commented Sep 13, 2024

@716774 hi what is editbin.exe ? how can I use that to hide console on my windows app?

MSVC in Visual Studio
For example, I am 2022

  1. bun build app.js --compile --minify --outfile app
  2. C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.40.33807\bin\Hostx64\x64\editbin.exe /SUBSYSTEM:WINDOWS app.exe

@716774
Copy link
Author

716774 commented Sep 13, 2024

@codenoid

This is the script I wrote, you can modify it to your directory .bat

@echo off

title=go run

echo 正在编译

call bun build app.js --compile --minify --outfile app

echo.

call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.40.33807\bin\Hostx64\x64\editbin.exe" /SUBSYSTEM:WINDOWS app.exe

echo.
pause

@codenoid
Copy link

Thanks a lot @716774 the console is hidden now 🎉

@StrangeBytesDev
Copy link

If you need an easy way to hide the console without actually running a windows executable (like in CI) then this script should be able to do it.

package main

import (
	"encoding/binary"
	"fmt"
	"os"
)

const (
	IMAGE_SUBSYSTEM_WINDOWS_GUI = 2
	PE_HEADER_OFFSET_LOCATION   = 0x3C
	SUBSYSTEM_OFFSET            = 0x5C
)

func main() {
	if len(os.Args) != 2 {
		fmt.Println("Usage: go run main.go <path-to-binary>")
		os.Exit(1)
	}

	filePath := os.Args[1]
	file, err := os.OpenFile(filePath, os.O_RDWR, 0644)
	if err != nil {
		fmt.Printf("Error opening file: %v\n", err)
		os.Exit(1)
	}
	defer file.Close()

	// Find the PE header offset
	_, err = file.Seek(PE_HEADER_OFFSET_LOCATION, 0)
	if err != nil {
		fmt.Printf("Error seeking to PE header offset: %v\n", err)
		os.Exit(1)
	}

	var peHeaderOffset uint32
	err = binary.Read(file, binary.LittleEndian, &peHeaderOffset)
	if err != nil {
		fmt.Printf("Error reading PE header offset: %v\n", err)
		os.Exit(1)
	}

	// Seek to the subsystem field in the PE header
	subsystemOffset := int64(peHeaderOffset) + SUBSYSTEM_OFFSET
	_, err = file.Seek(subsystemOffset, 0)
	if err != nil {
		fmt.Printf("Error seeking to subsystem field: %v\n", err)
		os.Exit(1)
	}

	// Change the subsystem to GUI
	err = binary.Write(file, binary.LittleEndian, uint16(IMAGE_SUBSYSTEM_WINDOWS_GUI))
	if err != nil {
		fmt.Printf("Error writing to subsystem field: %v\n", err)
		os.Exit(1)
	}

	fmt.Println("Successfully modified the binary to hide the console at startup.")
}

@paperclover
Copy link
Member

wait it's THAT easy??! we should port this to zig

stuff for this is in pub fn toBytes( in StandaloneModuleGraph.zig

@paperclover paperclover added the good first issue Something that would be good for new contributors label Oct 17, 2024
@StrangeBytesDev
Copy link

It seems like. The output of something like file my-bin.exe seems to show that its working as intended, and it does what you'd expect when run. I was also able to bang up a script in just JS to do that same.

import fs from 'node:fs'

const IMAGE_SUBSYSTEM_WINDOWS_GUI = 2;
const PE_HEADER_OFFSET_LOCATION = 0x3C;
const SUBSYSTEM_OFFSET = 0x5C;

function modifyBinarySubsystem(filePath: string) {
  const fd = fs.openSync(filePath, 'r+');
  const buffer = Buffer.alloc(4);

  // Read PE header offset from 0x3C
  fs.readSync(fd, buffer, 0, 4, PE_HEADER_OFFSET_LOCATION);
  const peHeaderOffset = buffer.readUInt32LE(0);

  // Seek to the subsystem field in the PE header
  const subsystemOffset = peHeaderOffset + SUBSYSTEM_OFFSET;
  const subsystemBuffer = Buffer.alloc(2);
  subsystemBuffer.writeUInt16LE(IMAGE_SUBSYSTEM_WINDOWS_GUI, 0);

  // Write the new subsystem value
  fs.writeSync(fd, subsystemBuffer, 0, 2, subsystemOffset);

  fs.closeSync(fd);
  console.log("Successfully modified the binary to hide the console at startup.");
}

// Usage: Provide the path to the binary
if (process.argv.length !== 3) {
  console.log('Usage: node modify-binary.js <path-to-binary>');
  process.exit(1);
}

const binaryPath = process.argv[2];
modifyBinarySubsystem(binaryPath);

This admittedly goes well over my head for how its working. I had some help from Claude on figuring it out. Its entirely possible that its missing some nuance. But it does seem to work.

@paperclover
Copy link
Member

It works because windows reads the bytes at those two offsets and depending on the value, it opens a console or not

@CosmoMyzrailGorynych
Copy link

I was also able to bang up a script in just JS to do that same.

OH MY GOD I love you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bundler Something to do with the bundler enhancement New feature or request good first issue Something that would be good for new contributors windows An issue that is known to occur on Windows
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants