@@ -22,7 +22,10 @@ The `WINDOWS` subsystem is commonly used on windows: desktop applications
22
22
typically do not want to flash up a console window on startup.
23
23
24
24
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
26
29
27
30
A new symbol ` pub extern "system" WinMain(...) ` with specific argument
28
31
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`.
32
35
33
36
The GNU toolchain will accept either entry point.
34
37
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
+
35
45
# Detailed design
36
46
[ design ] : #detailed-design
37
47
@@ -44,52 +54,65 @@ In practice, only two subsystems are very commonly used: `CONSOLE` and
44
54
` WINDOWS ` , and from a user's perspective, they determine whether a console will
45
55
be automatically created when the program is started.
46
56
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.
50
66
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:
52
79
```
53
- pub extern "system" WinMain(
54
- hInstance: HINSTANCE,
55
- hPrevInstance: HINSTANCE,
56
- lpCmdLine: LPSTR,
57
- nCmdShow: i32
58
- ) -> i32;
80
+ mainCRTStartup
81
+ WinMainCRTStartup
59
82
```
60
83
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 .
63
86
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).
72
89
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 `
76
93
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.
81
103
82
104
# Drawbacks
83
105
[ drawbacks ] : #drawbacks
84
106
85
- - Additional platform-specific code .
107
+ - A new platform-specific crate attribute .
86
108
- The difficulty of manually calling the rust initialization code is potentially
87
109
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.
93
116
94
117
# Alternatives
95
118
[ alternatives ] : #alternatives
@@ -132,42 +155,6 @@ whichever linker is actually being used.
132
155
support cross-compiling. If not compiling a binary crate, specifying the
133
156
option is an error regardless of the target.
134
157
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
-
171
158
# Unresolved questions
172
159
[ unresolved ] : #unresolved-questions
173
160
0 commit comments