Skip to content

Commit 109686e

Browse files
committed
main(): Take the user's main() function as a function pointer.
This allows us to move the UTF-16 → UTF-8 conversion into the DLL for a dynamic build, and keep the actual definitions of the entry points so small that they don't even need system headers, making them easily expandable for other types of entry points.
1 parent 12787fc commit 109686e

7 files changed

+55
-22
lines changed

README.md

+15-3
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,10 @@ The following functions are wrapped in this way:
5050
* GetFileVersionInfoEx()
5151

5252
### Building ###
53-
Replace all inclusions of `windows.h` or `stdio.h` with `win32_utf8.h` in your existing native Win32 code. The rest differs between static and dynamic linking:
53+
* Replace all inclusions of `windows.h` or `stdio.h` with `win32_utf8.h` in your existing native Win32 code.
54+
* If your program is a standalone executable and not a (static or dynamic) library, see the [Custom `main()` function](#custom-main-function) section for details on how to get UTF-8 command-line parameters in `argv`.
55+
56+
The rest differs between static and dynamic linking:
5457

5558
#### Static linking ####
5659
Make sure that `win32_utf8_build_static.c` is compiled as part of your sources.
@@ -63,9 +66,18 @@ For dynamic linking or other more special use cases, a project file for Visual C
6366
To generate a DLL in a different compiler, simply compile `win32_utf8_build_dynamic.c`.
6467

6568
#### Custom `main()` function ####
66-
When including `win32_utf8.h` (or the smaller `src/entry.h`), a later definition of `main()` is renamed to `win32_utf8_main()`. That function will in turn be called by win32_utf8's own `main()` function, defined in `src/entry.c`, which retrieves the Unicode command line, converts it to UTF-8, and transparently passes that to your actual `main()` function using the classic `argc` / `argv` scheme.
69+
Together with win32_utf8's entry point wrappers, changing the name and parameter list of your `main()` function to
70+
71+
```c
72+
int __cdecl win32_utf8_main(int argc, const char *argv[])
73+
```
74+
75+
guarantees that all strings in `argv` will be in UTF-8. `src/entry.h`, which is included as part of `win32_utf8.h`, redefines `main` as `win32_utf8_main` and thereby eliminates the need for another preprocessor conditional block around `main()` in cross-platform console applications.
76+
77+
You then need to provide the *actual* entry point by compiling the correct `entry_*.c` file from this repository as part of your sources:
78+
* `entry_main.c` if your program runs in the console subsystem.
6779
68-
When linking win32_utf8 statically as described above, `src/entry.c` is automatically compiled in as part of `win32_utf8_build_static.c`, so this should cause no further problems. For dynamic linking, however, `src/entry.c` has to be added to every target project's own sources as well.
80+
It can either be a separate translation unit, or `#include`d into an existing one. Also, make sure that your compiler's subsystem settings match the entry point file.
6981
7082
#### Compiler support ####
7183
**Visual C++** and **MinGW** compile the code just fine. Cygwin is not supported, as [it lacks Unicode versions of certain C runtime functions because they're not part of the POSIX standard](https://www.cygwin.com/ml/cygwin/2006-03/msg00539.html). (Of course, using MinGW's `gcc` through Cygwin works just fine.)

entry_main.c

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* Win32 UTF-8 wrapper
3+
*
4+
* ----
5+
*
6+
* main() entry point.
7+
* Compile or #include this file as part of your sources
8+
* if your program runs in the console subsystem.
9+
*/
10+
11+
#include "src/entry.h"
12+
#undef main
13+
14+
int __cdecl main(void)
15+
{
16+
return win32_utf8_entry(win32_utf8_main);
17+
}

src/entry.c

+5-18
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,19 @@
33
*
44
* ----
55
*
6-
* Wrappers for certain entry points.
6+
* Entry point wrapper, converting the command-line parameters to UTF-8.
77
*/
88

9-
#ifndef WIN32_UTF8_MAIN_UNIT
10-
#include "win32_utf8.h"
11-
#endif
12-
13-
#define main win32_utf8_main
14-
15-
#undef main
16-
17-
// This reference is also why we can't just compile this file for dynamic
18-
// builds as well, as those can't possibly resolve it.
19-
int __cdecl win32_utf8_main(int argc, const char *argv[]);
20-
21-
int __cdecl main(int argc, const char *argv[])
9+
int win32_utf8_entry(main_t *user_main)
2210
{
23-
extern void win32_utf8_init(void);
24-
extern void win32_utf8_exit(void);
25-
2611
int ret;
2712
int argc_w = 0;
2813
char **argv_u = CommandLineToArgvU(GetCommandLineW(), &argc_w);
2914
if(argv_u) {
15+
assert(user_main);
16+
3017
win32_utf8_init();
31-
ret = win32_utf8_main(argc_w, (const char**)argv_u);
18+
ret = user_main(argc_w, (const char**)argv_u);
3219
win32_utf8_exit();
3320
LocalFree(argv_u);
3421
} else {

src/entry.h

+10-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,16 @@
33
*
44
* ----
55
*
6-
* Wrappers for certain entry points.
6+
* Entry point wrapper, converting the command-line parameters to UTF-8.
77
*/
88

9+
// User-defined, subsystem-independent main function. Compile or #include one
10+
// of the entry_*.c files from the top directory, depending on which entry
11+
// point you want to use.
12+
int __cdecl win32_utf8_main(int argc, const char *argv[]);
913
#define main win32_utf8_main
14+
15+
typedef int __cdecl main_t(int argc, const char *argv[]);
16+
17+
// Performs the conversion and calls [user_main] with an UTF-8 argv.
18+
int win32_utf8_entry(main_t *user_main);

win32_utf8.def

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
EXPORTS
22
w32u8_set_fallback_codepage @1
33
w32u8_get_wrapped_functions
4+
win32_utf8_entry
45

56
; Macros
67
; ------

win32_utf8.vcxproj

+1
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@
110110
<ClCompile Include="win32_utf8_build_static.c">
111111
<ExcludedFromBuild Condition="'$(ConfigurationType)'=='DynamicLibrary'">true</ExcludedFromBuild>
112112
</ClCompile>
113+
<None Include="entry_main.c" />
113114
<None Include="src\build.c" />
114115
<None Include="src\entry.c" />
115116
<None Include="src\comdlg32_dll.c" />

win32_utf8_build_dynamic.c

+6
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,9 @@
77
*/
88

99
#include "src/build.c"
10+
11+
// Dynamic builds call these in DllMain().
12+
#define win32_utf8_init()
13+
#define win32_utf8_exit()
14+
15+
#include "src/entry.c"

0 commit comments

Comments
 (0)