Skip to content

Commit 8a970a3

Browse files
committed
Rewrite RFC in favour of mainCRTStartup alternative
1 parent b89530e commit 8a970a3

File tree

1 file changed

+57
-70
lines changed

1 file changed

+57
-70
lines changed

text/0000-windows-subsystem.md

Lines changed: 57 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ The `WINDOWS` subsystem is commonly used on windows: desktop applications
2222
typically do not want to flash up a console window on startup.
2323

2424
Currently, using the `WINDOWS` subsystem from rust is undocumented, and the
25-
process is non-trivial when targeting the MSVC toolchain:
25+
process is non-trivial when targeting the MSVC toolchain. There are a couple of
26+
approaches, each with their own downsides:
27+
28+
## Define a WinMain symbol
2629

2730
A new symbol `pub extern "system" WinMain(...)` with specific argument
2831
and return types must be declared, which will become the new entry point for
@@ -32,6 +35,13 @@ This is unsafe, and will skip the initialization code in `libstd`.
3235

3336
The GNU toolchain will accept either entry point.
3437

38+
## Override the entry point via linker options
39+
40+
This uses the same method as will be described in this RFC. However, it will
41+
result in build scripts also being compiled for the windows subsystem, which
42+
can cause additional console windows to pop up during compilation, making the
43+
system unusable while a build is in progress.
44+
3545
# Detailed design
3646
[design]: #detailed-design
3747

@@ -44,52 +54,65 @@ In practice, only two subsystems are very commonly used: `CONSOLE` and
4454
`WINDOWS`, and from a user's perspective, they determine whether a console will
4555
be automatically created when the program is started.
4656

47-
The solution this RFC proposes is to always export both `main` and `WinMain`
48-
symbols from rust executables compiled for windows. The `WinMain` function
49-
will simply delegate to the `main` function.
57+
## New crate attribute
58+
59+
This RFC proposes two changes to solve this problem. The first is adding a
60+
top-level crate attribute to allow specifying which subsystem to use:
61+
62+
`#![windows_subsystem = "windows"]`
63+
64+
Initially, the set of possible values will be `{windows, console}`, but may be
65+
extended in future if desired.
5066

51-
The exact signature is:
67+
The use of this attribute in a non-executable crate will result in a compiler
68+
warning. If compiling for a non-windows target, the attribute will be silently
69+
ignored.
70+
71+
## Additional linker argument
72+
73+
For the GNU toolchain, this will be sufficient. However, for the MSVC toolchain,
74+
the linker will be expecting a `WinMain` symbol, which will not exist.
75+
76+
There is some complexity to the way in which a different entry point is expected
77+
when using the windows subsystem. Firstly, the C-runtime library exports two
78+
symbols designed to be used as an entry point:
5279
```
53-
pub extern "system" WinMain(
54-
hInstance: HINSTANCE,
55-
hPrevInstance: HINSTANCE,
56-
lpCmdLine: LPSTR,
57-
nCmdShow: i32
58-
) -> i32;
80+
mainCRTStartup
81+
WinMainCRTStartup
5982
```
6083

61-
Where `HINSTANCE` is a pointer-sized opaque handle, and `LPSTR` is a C-style
62-
null terminated string.
84+
`LINK.exe` will use the subsystem to determine which of these symbols to use
85+
as the default entry point if not overridden.
6386

64-
All four parameters are either irrelevant or can be obtained easily through
65-
other means:
66-
- `hInstance` - Can be obtained via `GetModuleHandle`.
67-
- `hPrevInstance` - Is always NULL.
68-
- `lpCmdLine` - `libstd` already provides a function to get command line
69-
arguments.
70-
- `nCmdShow` - Can be obtained via `GetStartupInfo`, although it's not actually
71-
needed any more (the OS will automatically hide/show the first window created).
87+
Each one performs some unspecified initialization of the CRT, before calling out
88+
to a symbol defined within the program (`main` or `WinMain` respectively).
7289

73-
The end result is that rust programs will "just work" when the subsystem is
74-
overridden via custom linker arguments, and does not require `rustc` to
75-
parse those linker arguments.
90+
The second part of the solution is to pass an additional linker option when
91+
targeting the MSVC toolchain:
92+
`/ENTRY:mainCRTStartup`
7693

77-
A possible future extension would be to add additional command-line options to
78-
`rustc` (and in turn, `Cargo.toml`) to specify the subsystem directly. `rustc`
79-
would automatically translate this into the correct linker arguments for
80-
whichever linker is actually being used.
94+
This will override the entry point to always be `mainCRTStartup`. For
95+
console-subsystem programs this will have no effect, since it was already the
96+
default, but for windows-subsystem programs, it will eliminate the need for
97+
a `WinMain` symbol to be defined.
98+
99+
This command line option will always be passed to the linker, regardless of the
100+
presence or absence of the `windows_subsystem` crate attribute, except when
101+
the user specifies their own entry point in the linker arguments. This will
102+
require `rustc` to perform some basic parsing of the linker options.
81103

82104
# Drawbacks
83105
[drawbacks]: #drawbacks
84106

85-
- Additional platform-specific code.
107+
- A new platform-specific crate attribute.
86108
- The difficulty of manually calling the rust initialization code is potentially
87109
a more general problem, and this only solves a specific (if common) case.
88-
- This is a breaking change for any crates which already export a `WinMain`
89-
symbol. It is likely that only executable crates would export this symbol,
90-
so the knock-on effect on crate dependencies should be non-existent.
91-
92-
A possible work-around for this is described below.
110+
- The subsystem must be specified earlier than is strictly required: when
111+
compiling C/C++ code only the linker, not the compiler, needs to actually be
112+
aware of the subsystem.
113+
- It is assumed that the initialization performed by the two CRT entry points
114+
is identical. This seems to currently be the case, and is unlikely to change
115+
as this technique appears to be used fairly widely.
93116

94117
# Alternatives
95118
[alternatives]: #alternatives
@@ -132,42 +155,6 @@ whichever linker is actually being used.
132155
support cross-compiling. If not compiling a binary crate, specifying the
133156
option is an error regardless of the target.
134157

135-
- Have `rustc` override the entry point when calling `link.exe`, and tell it to
136-
use `mainCRTStartup` instead of `winMainCRTStartup`. These are the "true"
137-
entry points of windows programs, which first initialize the C runtime
138-
library, and then call `main` or `WinMain` respectively.
139-
140-
This is the simplest solution, and it will not have any serious backwards
141-
compatibility problems, since rust programs are already required to have a
142-
`main` function, even if `WinMain` has been separately defined. However, it
143-
relies on the two CRT functions to be interchangeable, although this does
144-
*appear* to be the case currently.
145-
146-
- Export both entry points as described in this RFC, but also add a `subsystem`
147-
function to `libstd` determine which subsystem was used at runtime.
148-
149-
The `WinMain` function would first set an internal flag, and only then
150-
delegate to the `main` function.
151-
152-
A function would be added to `std::os::windows`:
153-
154-
`fn subsystem() -> &'static str`
155-
156-
This would check the value of the internal flag, and return either `WINDOWS` or
157-
`CONSOLE` depending on which entry point was actually used.
158-
159-
The `subsystem` function could be used to eg. redirect logging to a file if
160-
the program is being run on the `WINDOWS` subsystem. However, it would return
161-
an incorrect value if the initialization was skipped, such as if used as a
162-
library from an executable written in another language.
163-
164-
- Export both entry points as described in this RFC, but use the undocumented
165-
MSVC equivalent to weak symbols to avoid breaking existing code.
166-
167-
The parameter `/alternatename:_WinMain@16=_RustWinMain@16` can be used to
168-
export `WinMain` only if it is not also exported elsewhere. This is completely
169-
undocumented, but is mentioned here: (http://stackoverflow.com/a/11529277).
170-
171158
# Unresolved questions
172159
[unresolved]: #unresolved-questions
173160

0 commit comments

Comments
 (0)