Windows Console Window

Hello,

when Syncthing is started it automatically opens a console window on Windows. The problem isn’t new, here the solution.

The image’s (syncthing.exe) PE header subsystem type is set to “IMAGE_SUBSYSTEM_WINDOWS_CUI” (0x3) which references the “Windows Character Subsystem” and automatically opens a console window that contains the process.

It needs to be changed to “IMAGE_SUBSYSTEM_WINDOWS_GUI” (0x2). Once that’s done, it should start w/o the console window.

I’m not the GO expert, but from what I read on the Interwebtubez there’s a way to hand go some ldflags tho achieve this.

As an example, this is supposed to change the image subsystem type to IMAGE_SUBSYSTEM_WINDOWS_GUI:

go build -ldflags "-H windowsgui" ...

Once changed, the console window will disappear.

KR,

Grimeton.

EDIT: Here, a link to Go’s linker documentation, stating the same thing: link command - cmd/link - Go Packages

This is a known issue with the new terminal:

Yeah, they don’t need nifty code or anything. All they have to do is to change the image’s subsystem and the window will be gone.

The fact that it shows up all the time on Windows 11 seems like a bug that has been fixed. As soon as Windows sees IMAGE_SUBSYSTEM_WINDOWS_CUI in the PE header it opens a console window if the new process’ parent isn’t one and connects the process stdin/out/err and other stuff to the console window.

Windows doesn’t even provide a way to write “dynamic code” so that a process can be launched as console or gui process. It’s a header setting and that’s it.

Here an interesting article on the problem from 2009: How do I write a program that can be run either as a console or a GUI application? - The Old New Thing

I also recommend reading “Windows Internals” if you want to know more.

The reason why this change hasn’t been adopted:

Great, I searched for “windowsgui” everywhere, w/o results and now it’s right there in the bug report.

Anyway.

The argument that no console output is possible is not entirely true: AllocConsole function - Windows Console | Microsoft Learn and Golang: Initialize console handlers after AttachConsole or AllocConsole on Windows. · GitHub

The problem I run into here is that I have a console on my desktop no matter how I start SyncThing, even via task planer. The only option left would be to install it as a service and that’s something i’d like to avoid.

I understand that the console part is needed for the cli part of the software, but as the cli part is using a remote connection it could easily by put into another binary.

This is an annoying problem, indeed. In my applications that provide a GUI and a CLI at the same time I’m facing it as well and I haven’t found a satisfying solution yet.

The compromise I’m currently stuck with is to create a GUI executable and then spawn the console manually as needed. That’s basically what has already been suggested here. However, there are caveats to this. As you can see in the linked code you cannot just blindly spawn a new console but also need to take care of the case when there’s already an existing console (so the output appears in the existing console and no new console is spawned). It is still far from optimal because this approach breaks redirections. So I made it optional (one needs to set an environment variable to disable it). If we’d end up adding hacks like this to Syncthing we should definitely document such caveats (like this) or find a better solution.

Not sure whether this compromise makes sense for Syncthing which is in fact primarily a console application.


This is also about --help of the service itself and generally being able to see stdout/stderr.


There’s another option but it comes with its own disadvantages. This option is using a GUI wrapper that allows running Syncthing as part of the GUI process like Syncthing Tray (see the corresponding README section for details how Syncthing Tray handles starting Syncthing). When Syncthing isn’t executed as its own process the whole issue goes away (although Syncthing Tray’s own CLI relies on the aforementioned compromise). Of course this has the disadvantage that automatic updates of Syncthing aren’t possible anymore. You’re basically bound to use the Syncthing version included in Syncthing Tray.

1 Like

Sorry for the late reply, life …

Anyway…

The way I see it, you’re using a monitoring process for syncthing anyway, so you launch the monitor process that then launches syncthing. Where’s the problem in compiling those processes for Windows as a GUI application and for the other things you use a different executable that then offers the console option.

When someone runs syncthing --help you can just call another executable that was built with the console set and output the help from there. You could even build the binary twice, once with console and once with gui and depending on the switches of the starting process then start the console or the gui version.

You could use the syncthing.exe file as a gui binary that is used for routing the commands to the right binary and let it then launch the other binary that is needed.

I really don’t see the problem with this approach as these things are modules anyway and it’s not uncommon to have different build parameters for different platforms and multiple .exe files for different parts of a software suite. Putting everything into one .exe file on the other hand can create all kinds of problems, including the one we’re looking at now.

I also don’t think the Windows behavior is going to change, because when you put a flag in the header of your executable file format so that the LDR in your kernel knows which subsystem to initialize, you won’t remove the console requirement after 40 years all of a sudden.

The main issue here is that Windows is not meant to have programs running that output their information to stdout/err. Not even services should do that and that philosophy is not reflected in the Windows version of the file.

You either write something that sits as an icon in the information area of the taskbar (near the clock) that then syncs stuff in the background or you write it as a service that is then doing its job on a per user basis. But that logic needs to be included in the binary. Windows will not take care of that for you like systemd does on Linux.

The Syncthing Tray is a nice thing, but I don’t want another piece of software from another vendor. All I need is an icon in the taskbar, that opens the browser to the website when I doubleclick on it. The main issue I have with those 3rd party applications is that the developers loose their interest over time or something else happens and I’m out looking for a new software to fix an old problem once again.

This is sadly an issue the open sauce scene is suffering from for the last ten years were people develop new software w/o the commitment to support it and all of a sudden it just stops and that’s it, so I hope that the core project will not suffer that fate.

BTW: I built syncthing as GUI executable myself by using EXTRA_LDFLAGS and it worked like a charm. Only issue is that the binary isn’t signed and I have no incentive on creating my own private signing certificate. I don’t know if a self built binary is fully accepted in the network, so I’m not going to use it either.

KR,

G.

Someone who cares strongly about the Windows experience could contribute this.

An ugly solution, but one that we would not be the only ones to use, is to ship two binaries on Windows. E.g “SyncthingW.exe” and “Syncthing.exe”. One would use the “W” variant for scheduled starts and the non-W variant would be used by default from the command line.

Maintainers of shells such as SyncTrayzor could choose which binary works best for their setup, leaving a lot of the Windows-specific problems outside of Syncthing.

1 Like

The Syncthing Tray is a nice thing, but I don’t want another piece of software from another vendor. All I need is an icon in the taskbar, that opens the browser to the website when I doubleclick on it. The main issue I have with those 3rd party applications is that the developers loose their interest over time or something else happens and I’m out looking for a new software to fix an old problem once again.

That would mean a minimal GUI integration within Syncthing itself. I suppose Syncthing devs wouldn’t generally be against it; it is just that nobody has implemented it so far (I’m not a Syncthing dev myself!). At least for Windows it would make sense. For GNU/Linux it might be better to leave it out to avoid dependencies on graphical frameworks. In any case there could be a -no-gui similar to the current -no-console option.

This is sadly an issue the open sauce scene is suffering from for the last ten years were people develop new software w/o the commitment to support it and all of a sudden it just stops and that’s it, so I hope that the core project will not suffer that fate.

I will continue to support Syncthing Tray in the foreseeable future but can obviously make no guarantees. Since I’m the only developer of that project (besides some smaller contributions by other people) the project would likely die if I’d stop supporting it. Syncthing itself has fortunately a bigger number of developers supporting it so it will very unlikely die in the future.

Someone who cares strongly about the Windows experience could contribute this.

Since I personally use Syncthing Tray, I don’t have a strong opinion. When I’m using Syncthing as CLI tool for some reason I’m using the MSYS2 terminal which “just works”.

The downside of providing multiple executables is that the (statically linked) Go runtime and dependencies would effectively be shipped twice.

Another very ugly solution would be a self-patching executable.

Could Syncthing.exe be Syncthing and SyncthingW.exe be a thin wrapper? (I presume it’s possible to be a GUI-tagged executable and start a console application while not popping a console window…?)

Edit: Looks like even something as high level as Go’s os/exec.Cmd has a HideWindow boolean in the Windows SysProcAttr parameter, which sounds like it might do the trick. So we could ship a fairly small Go executable that is linked as a GUI binary and starts the main Syncthing binary without a console window… (I wouldn’t have the energy to involve this binary in the auto-upgrade process, so the less functionality it has the better…)

That’s the important question, indeed. Unfortunately I don’t know the answer. At least Syncthing Tray currently relies on -no-console (except one uses the built-in library as mentioned before of course). I could try giving boost::process::windows::hide a shot to improve Syncthing Tray in that regard. Not sure what WinAPI feature that uses under the hood but it is certainly something accessible from Golang as well (shouldn’t be hard to search for).

EDIT: Haven’t seen your edit. Looks like you’ve already found the Golang/Windows API to use :slight_smile:

EDIT: Looks like Syncthing Tray already uses boost::process::windows::create_no_window (not …::hide what I’ve searched for before). I have already totally forgotten about it because it wasn’t a big deal. A user confirmed that it worked (see Flag for not creating console window on Windows · Issue #108 · Martchus/syncthingtray · GitHub). So it is possible to hide the console effectively (at least it helped in the case of that user).

By the way, the mentioned Boost option is basically CREATE_NO_WINDOW from Process Creation Flags (WinBase.h) - Win32 apps | Microsoft Learn.

1 Like

Already done: See Syncthing Windows Setup.

If you install as as service: No console window (of course; it’s a service).

If you install for the current user: There’s a launcher script that starts syncthing.exe in a hidden window.

There’s also a shortcut that opens the web GUI. Simple.

1 Like

Multiple times: e.g. the mentioned Syncthing Tray project and presumably also SyncTrayzor can avoid showing the console window.

However, the point in @Grimeton was that he’d like to have a minimal solution that’s at best also be provided by Syncthing itself to avoid relying on yet another project¹.

Maybe a simple wrapper executable would be more fitting for this use case. It would e.g. be a simple C program (to avoid shipping the Go runtime twice) that simply invokes syncthing.exe in the same directory without console (e.g. using plain WinAPI functions to keep it really small and simple). It would be provided by Syncthing itself (part of the Windows release tarball).


¹ Note that when using Syncthing Tray you can actually use any version of Syncthing you’d like with it (unlike stated before) as you can simply use the external launcher after all (and not only the built-in version). When I stated before that you had to use the build-in version I thought the external launcher could not avoid the console window but it is possible after all (see my previous message). So maybe then using a 3rd party tool appears less problematic as you can still upgrade Syncthing itself independently of it.

It would e.g. be a simple C program (to avoid shipping the Go runtime twice) that simply invokes syncthing.exe in the same directory without console (e.g. using plain WinAPI functions to keep it really small and simple). It would be provided by Syncthing itself (part of the Windows release tarball).

It doesn’t need to be written in C. It doesn’t even need to be an executable. It can be a simple WSH script launched using wscript.exe, which has no console window. Here’s one example:

One potential problem with this is that WScript is often blocked in corporate/education environment. Also, Windows Defender may not like such scripts either.

One potential problem with this is that WScript is often blocked in corporate/education environment.

The problem here is educating system administrators about the difference between a program and a security boundary. In Windows, an executable (e.g., wscript.exe) is a program; it is not a security boundary. Just because a user can run a specific executable (even one designed to run scripts) does not mean that said user can somehow bypass security. Blocking wscript.exe does nothing whatsoever to increase security.

Windows Defender may not like such scripts either.

The whitelisting follies I experienced in getting Syncthing Windows Setup executable whitelisted suggests to me that this concern is not really valid. The various vendors’ anti-malware apps were all blocking the installer due to the use of NSSM, not wscript.exe or the WSH scripts therein.

For what it’s worth, just putting that .js file on my Windows box and double clicking it resulted in opening it in Visual Studio Code. After changing it to run under wscript.exe it appears to do what it’s supposed to… Perhaps it’s possible to ship a .lnk file that specifies wscript as the opener, and also allows setting a custom icon etc?

To address this issue more directly, I wrote a launcher using FPC. The launcher is StartSyncthing.exe and all it does is spawn syncthing.exe using the Windows ShellExecuteExW API function. It has zero dependencies and all parameters are passed to the spawned copy of syncthing.exe as-is. (The i386 executable is only 135KB and the x86_64 executable is only 148KB. These sizes could be reduced a bit with a smaller icon file, but I figured 135KB/148KB was not too bad.)

Attached contains source code and executables (MPL license). Would be great if anyone could test.

StartSyncthing-0.0.1.zip (116.1 KB)

For what it’s wort, I’m fine with adding some StartSyncthing / SyncthingW type file to our build. It could for example be some sort of non-compiled code like Javascript or Powershell or a link file with magic attributes or a combo. It could also be a small Go executable.

However I’m extremely reluctant to add some C thing or, as much as it takes me back to my formative years, some Pascal thing. As in, that’s not going to happen.