├── .gitattributes
├── .gitignore
├── Debug.props
├── Dynamic.props
├── README.md
├── Release.props
├── Static.props
├── UNLICENSE.txt
├── entry_main.c
├── entry_winmain.c
├── os_compat.png
├── src
├── build.c
├── comdlg32_dll.c
├── comdlg32_dll.h
├── dsound_dll.c
├── dsound_dll.h
├── entry.c
├── entry.h
├── gdi32_dll.c
├── gdi32_dll.h
├── kernel32_dll.c
├── kernel32_dll.h
├── macros.c
├── macros.h
├── message_enum.h
├── msvcrt_dll.c
├── msvcrt_dll.h
├── psapi_dll.c
├── psapi_dll.h
├── shell32_dll.c
├── shell32_dll.h
├── shlwapi_dll.c
├── shlwapi_dll.h
├── user32_dll.c
├── user32_dll.h
├── utf.c
├── utf.h
├── version_dll.c
├── version_dll.h
├── win32_utf8.c
├── wininet_dll.c
├── wininet_dll.h
├── wrappers.c
└── wrappers.h
├── win32_utf8.def
├── win32_utf8.h
├── win32_utf8.vcxproj
├── win32_utf8_build_dynamic.c
└── win32_utf8_build_static.c
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Set default behaviour, in case users don't have core.autocrlf set.
2 | * text eol=lf
3 |
4 | # Explicitly declare text files we want to always be normalized and converted
5 | # to native line endings on checkout.
6 | *.c text
7 | *.cpp text
8 | *.h text
9 |
10 | # Denote all files that are truly binary and should not be modified.
11 | *.png binary
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # This file includes:
2 | # -------------------
3 | # C.gitignore
4 | # VisualStudio.gitignore
5 | # -------------------
6 |
7 | # C.gitignore
8 | # -----------
9 | # Prerequisites
10 | *.d
11 |
12 | # Object files
13 | *.o
14 | *.ko
15 | *.obj
16 | *.elf
17 |
18 | # Linker output
19 | *.ilk
20 | *.map
21 | *.exp
22 |
23 | # Precompiled Headers
24 | *.gch
25 | *.pch
26 |
27 | # Libraries
28 | *.lib
29 | *.a
30 | *.la
31 | *.lo
32 |
33 | # Shared objects (inc. Windows DLLs)
34 | *.dll
35 | *.so
36 | *.so.*
37 | *.dylib
38 |
39 | # Executables
40 | *.exe
41 | *.out
42 | *.app
43 | *.i*86
44 | *.x86_64
45 | *.hex
46 |
47 | # Debug files
48 | *.dSYM/
49 | *.su
50 | *.idb
51 | *.pdb
52 |
53 | # Kernel Module Compile Results
54 | *.mod*
55 | *.cmd
56 | .tmp_versions/
57 | modules.order
58 | Module.symvers
59 | Mkfile.old
60 | dkms.conf
61 | # -----------
62 |
63 | # VisualStudio.gitignore
64 | # ----------------------
65 | ## Ignore Visual Studio temporary files, build results, and
66 | ## files generated by popular Visual Studio add-ons.
67 | ##
68 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
69 |
70 | # User-specific files
71 | *.rsuser
72 | *.suo
73 | *.user
74 | *.userosscache
75 | *.sln.docstates
76 |
77 | # User-specific files (MonoDevelop/Xamarin Studio)
78 | *.userprefs
79 |
80 | # Mono auto generated files
81 | mono_crash.*
82 |
83 | # Build results
84 | [Dd]ebug/
85 | [Dd]ebugPublic/
86 | [Rr]elease/
87 | [Rr]eleases/
88 | x64/
89 | x86/
90 | [Ww][Ii][Nn]32/
91 | [Aa][Rr][Mm]/
92 | [Aa][Rr][Mm]64/
93 | bld/
94 | [Bb]in/
95 | [Oo]bj/
96 | [Ll]og/
97 | [Ll]ogs/
98 |
99 | # Visual Studio 2015/2017 cache/options directory
100 | .vs/
101 | # Uncomment if you have tasks that create the project's static files in wwwroot
102 | #wwwroot/
103 |
104 | # Visual Studio 2017 auto generated files
105 | Generated\ Files/
106 |
107 | # MSTest test Results
108 | [Tt]est[Rr]esult*/
109 | [Bb]uild[Ll]og.*
110 |
111 | # NUnit
112 | *.VisualState.xml
113 | TestResult.xml
114 | nunit-*.xml
115 |
116 | # Build Results of an ATL Project
117 | [Dd]ebugPS/
118 | [Rr]eleasePS/
119 | dlldata.c
120 |
121 | # Benchmark Results
122 | BenchmarkDotNet.Artifacts/
123 |
124 | # .NET Core
125 | project.lock.json
126 | project.fragment.lock.json
127 | artifacts/
128 |
129 | # ASP.NET Scaffolding
130 | ScaffoldingReadMe.txt
131 |
132 | # StyleCop
133 | StyleCopReport.xml
134 |
135 | # Files built by Visual Studio
136 | *_i.c
137 | *_p.c
138 | *_h.h
139 | *.ilk
140 | *.meta
141 | *.obj
142 | *.iobj
143 | *.pch
144 | *.pdb
145 | *.ipdb
146 | *.pgc
147 | *.pgd
148 | *.rsp
149 | *.sbr
150 | *.tlb
151 | *.tli
152 | *.tlh
153 | *.tmp
154 | *.tmp_proj
155 | *_wpftmp.csproj
156 | *.log
157 | *.vspscc
158 | *.vssscc
159 | .builds
160 | *.pidb
161 | *.svclog
162 | *.scc
163 |
164 | # Chutzpah Test files
165 | _Chutzpah*
166 |
167 | # Visual C++ cache files
168 | ipch/
169 | *.aps
170 | *.ncb
171 | *.opendb
172 | *.opensdf
173 | *.sdf
174 | *.cachefile
175 | *.VC.db
176 | *.VC.VC.opendb
177 |
178 | # Visual Studio profiler
179 | *.psess
180 | *.vsp
181 | *.vspx
182 | *.sap
183 |
184 | # Visual Studio Trace Files
185 | *.e2e
186 |
187 | # TFS 2012 Local Workspace
188 | $tf/
189 |
190 | # Guidance Automation Toolkit
191 | *.gpState
192 |
193 | # ReSharper is a .NET coding add-in
194 | _ReSharper*/
195 | *.[Rr]e[Ss]harper
196 | *.DotSettings.user
197 |
198 | # TeamCity is a build add-in
199 | _TeamCity*
200 |
201 | # DotCover is a Code Coverage Tool
202 | *.dotCover
203 |
204 | # AxoCover is a Code Coverage Tool
205 | .axoCover/*
206 | !.axoCover/settings.json
207 |
208 | # Coverlet is a free, cross platform Code Coverage Tool
209 | coverage*.json
210 | coverage*.xml
211 | coverage*.info
212 |
213 | # Visual Studio code coverage results
214 | *.coverage
215 | *.coveragexml
216 |
217 | # NCrunch
218 | _NCrunch_*
219 | .*crunch*.local.xml
220 | nCrunchTemp_*
221 |
222 | # MightyMoose
223 | *.mm.*
224 | AutoTest.Net/
225 |
226 | # Web workbench (sass)
227 | .sass-cache/
228 |
229 | # Installshield output folder
230 | [Ee]xpress/
231 |
232 | # DocProject is a documentation generator add-in
233 | DocProject/buildhelp/
234 | DocProject/Help/*.HxT
235 | DocProject/Help/*.HxC
236 | DocProject/Help/*.hhc
237 | DocProject/Help/*.hhk
238 | DocProject/Help/*.hhp
239 | DocProject/Help/Html2
240 | DocProject/Help/html
241 |
242 | # Click-Once directory
243 | publish/
244 |
245 | # Publish Web Output
246 | *.[Pp]ublish.xml
247 | *.azurePubxml
248 | # Note: Comment the next line if you want to checkin your web deploy settings,
249 | # but database connection strings (with potential passwords) will be unencrypted
250 | *.pubxml
251 | *.publishproj
252 |
253 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
254 | # checkin your Azure Web App publish settings, but sensitive information contained
255 | # in these scripts will be unencrypted
256 | PublishScripts/
257 |
258 | # NuGet Packages
259 | *.nupkg
260 | # NuGet Symbol Packages
261 | *.snupkg
262 | # The packages folder can be ignored because of Package Restore
263 | **/[Pp]ackages/*
264 | # except build/, which is used as an MSBuild target.
265 | !**/[Pp]ackages/build/
266 | # Uncomment if necessary however generally it will be regenerated when needed
267 | #!**/[Pp]ackages/repositories.config
268 | # NuGet v3's project.json files produces more ignorable files
269 | *.nuget.props
270 | *.nuget.targets
271 |
272 | # Microsoft Azure Build Output
273 | csx/
274 | *.build.csdef
275 |
276 | # Microsoft Azure Emulator
277 | ecf/
278 | rcf/
279 |
280 | # Windows Store app package directories and files
281 | AppPackages/
282 | BundleArtifacts/
283 | Package.StoreAssociation.xml
284 | _pkginfo.txt
285 | *.appx
286 | *.appxbundle
287 | *.appxupload
288 |
289 | # Visual Studio cache files
290 | # files ending in .cache can be ignored
291 | *.[Cc]ache
292 | # but keep track of directories ending in .cache
293 | !?*.[Cc]ache/
294 |
295 | # Others
296 | ClientBin/
297 | ~$*
298 | *~
299 | *.dbmdl
300 | *.dbproj.schemaview
301 | *.jfm
302 | *.pfx
303 | *.publishsettings
304 | orleans.codegen.cs
305 |
306 | # Including strong name files can present a security risk
307 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
308 | #*.snk
309 |
310 | # Since there are multiple workflows, uncomment next line to ignore bower_components
311 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
312 | #bower_components/
313 |
314 | # RIA/Silverlight projects
315 | Generated_Code/
316 |
317 | # Backup & report files from converting an old project file
318 | # to a newer Visual Studio version. Backup files are not needed,
319 | # because we have git ;-)
320 | _UpgradeReport_Files/
321 | Backup*/
322 | UpgradeLog*.XML
323 | UpgradeLog*.htm
324 | ServiceFabricBackup/
325 | *.rptproj.bak
326 |
327 | # SQL Server files
328 | *.mdf
329 | *.ldf
330 | *.ndf
331 |
332 | # Business Intelligence projects
333 | *.rdl.data
334 | *.bim.layout
335 | *.bim_*.settings
336 | *.rptproj.rsuser
337 | *- [Bb]ackup.rdl
338 | *- [Bb]ackup ([0-9]).rdl
339 | *- [Bb]ackup ([0-9][0-9]).rdl
340 |
341 | # Microsoft Fakes
342 | FakesAssemblies/
343 |
344 | # GhostDoc plugin setting file
345 | *.GhostDoc.xml
346 |
347 | # Node.js Tools for Visual Studio
348 | .ntvs_analysis.dat
349 | node_modules/
350 |
351 | # Visual Studio 6 build log
352 | *.plg
353 |
354 | # Visual Studio 6 workspace options file
355 | *.opt
356 |
357 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
358 | *.vbw
359 |
360 | # Visual Studio LightSwitch build output
361 | **/*.HTMLClient/GeneratedArtifacts
362 | **/*.DesktopClient/GeneratedArtifacts
363 | **/*.DesktopClient/ModelManifest.xml
364 | **/*.Server/GeneratedArtifacts
365 | **/*.Server/ModelManifest.xml
366 | _Pvt_Extensions
367 |
368 | # Paket dependency manager
369 | .paket/paket.exe
370 | paket-files/
371 |
372 | # FAKE - F# Make
373 | .fake/
374 |
375 | # CodeRush personal settings
376 | .cr/personal
377 |
378 | # Python Tools for Visual Studio (PTVS)
379 | __pycache__/
380 | *.pyc
381 |
382 | # Cake - Uncomment if you are using it
383 | # tools/**
384 | # !tools/packages.config
385 |
386 | # Tabs Studio
387 | *.tss
388 |
389 | # Telerik's JustMock configuration file
390 | *.jmconfig
391 |
392 | # BizTalk build output
393 | *.btp.cs
394 | *.btm.cs
395 | *.odx.cs
396 | *.xsd.cs
397 |
398 | # OpenCover UI analysis results
399 | OpenCover/
400 |
401 | # Azure Stream Analytics local run output
402 | ASALocalRun/
403 |
404 | # MSBuild Binary and Structured Log
405 | *.binlog
406 |
407 | # NVidia Nsight GPU debugger configuration file
408 | *.nvuser
409 |
410 | # MFractors (Xamarin productivity tool) working folder
411 | .mfractor/
412 |
413 | # Local History for Visual Studio
414 | .localhistory/
415 |
416 | # BeatPulse healthcheck temp database
417 | healthchecksdb
418 |
419 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
420 | MigrationBackup/
421 |
422 | # Ionide (cross platform F# VS Code tools) working folder
423 | .ionide/
424 |
425 | # Fody - auto-generated XML schema
426 | FodyWeavers.xsd
427 | # ----------------------
428 |
--------------------------------------------------------------------------------
/Debug.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | _debug
5 | true
6 | true
7 |
8 |
9 |
10 | Disabled
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Dynamic.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | DynamicLibrary
5 | dynamic
6 |
7 |
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Win32 UTF-8 wrapper
2 | -------------------
3 |
4 | ### Why a wrapper? ###
5 |
6 | This library evolved from the need of the [Touhou Community Reliant Automatic Patcher](https://github.com/thpatch/thcrap) to hack Unicode functionality for the Win32 API into games using the ANSI functions.
7 |
8 | By simply including `win32_utf8.h` and linking to this library, you automatically have Unicode compatibility in applications using the native Win32 APIs, usually without requiring changes to existing code using char strings.
9 |
10 | ### Extended functionality ###
11 |
12 | In addition, this library also adds new useful functionality to some original Windows functions.
13 |
14 | ###### kernel32.dll ######
15 |
16 | * `CreateDirectoryU()` works recursively - the function creates all necessary directories to form the given path.
17 | * `LoadLibraryExU()` can be safely and unconditionally used with the search path flags introduced in [KB2533623](https://support.microsoft.com/help/2533623/). If this update is not installed on a user's system, these flags are cleared out automatically.
18 | * `GetModuleFileNameU()` returns the necessary length of a buffer to hold the module file name if NULL is passed for `nSize` or `lpFilename`, similar to what `GetCurrentDirectory()` can do by default.
19 | * `ReadFileU()/WriteFileU()` does not crash when `lpNumberOfBytesRead/Written` and `lpOverlapped` are both NULL. Windows 8+ already has this fix. This can be used to add Windows 7 compatibility to applications which rely on this fix.
20 |
21 | ###### shell32.dll ######
22 |
23 | * `SHBrowseForFolderU()` always displays an edit box and shows a resizable window if the active thread's COM settings allow it.
24 |
25 | ###### shlwapi.dll ######
26 |
27 | * `PathRemoveFileSpecU()` correctly works as intended for paths containing forward slashes
28 |
29 | #### UTF-8 versions of functions that originally only have UTF-16 versions
30 |
31 | * `LPSTR* WINAPI CommandLineToArgvU(LPCWSTR lpCmdLine, int* pNumArgs)`
32 |
33 | Splits a UTF-16 command-line string (returned by e.g.`GetCommandLineW()`) into an UTF-8 `argv` array, and returns the number of arguments (`argc`) in `pNumArgs`. The caller has to free the returned array using `LocalFree()`.
34 |
35 | * `HRESULT WINAPI SHParseDisplayNameU(LPCSTR pszName, IBindCtx *pbc, LPITEMIDLIST *ppidl, SFGAOF sfgaoIn, SFGAOF *psfgaoOut)`
36 |
37 | Converts a path (`pszName`) to a `ITEMLIST` pointer (`ppidl`), required for a number of shell functions. Unlike the original function, this wrapper also works as expected for paths containing forward slashes.
38 |
39 | ### OS compatibility
40 | win32_utf8 it meant to require at least Windows XP - that is, it statically references only Windows functions that were available on XP. Wrappers for functions that were introduced in later Windows versions load their original functions dynamically using GetProcAddress().
41 |
42 | As a result, these wrappers themselves are not tied to the minimum required OS of the function they wrap. This means that applications which call these wrappers will actually *start* on Windows XP and *not* abort with the classic *"The procedure entry point X could not be located in the dynamic link library Y.DLL."* error on startup. (Unless, of course, if your compiler would target a newer Windows version anyway.) However, the wrappers will show this message box on every call:
43 |
44 | 
45 |
46 | This is arguably preferable over the three other options for dealing with this issue (just crashing by calling a NULL pointer, silently doing nothing, or aborting on startup with the aforementioned error). It should still run the majority of the program, and it provides a helpful message during testing - which can even be left in shipping builds as a sort of nag screen for end users that still use old Windows versions.
47 |
48 | The following functions are wrapped in this way:
49 |
50 | * version.dll
51 | * GetFileVersionInfoSizeEx()
52 | * GetFileVersionInfoEx()
53 |
54 | ### Building ###
55 | * Replace all inclusions of `windows.h` or `stdio.h` with `win32_utf8.h` in your existing native Win32 code.
56 | * 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`.
57 |
58 | The rest differs between static and dynamic linking:
59 |
60 | #### Static linking ####
61 | Make sure that `win32_utf8_build_static.c` is compiled as part of your sources.
62 |
63 | If your codebase doesn't necessarily need to use all of the wrapped functions (which is probably the case for most programs), you may additionally `#define WIN32_UTF8_NO_API`. This macro removes the `w32u8_get_wrapped_functions()` function, which references all wrapped functions that are part of win32_utf8, from your build. This aids the elimination of unused wrapper functions (and their DLL references) with some compilers, particularly Visual C++.
64 |
65 | #### Dynamic linking ####
66 | For dynamic linking or other more special use cases, a project file for Visual C++ is provided. The default configuration requires the Visual Studio 2013 platform toolset with Windows XP targeting support, but the project should generally build under every version since Visual C++ 2010 Express after changing the platform toolset (*Project → Properties → General → Platform Toolset*) to a supported option.
67 |
68 | To generate a DLL in a different compiler, simply compile `win32_utf8_build_dynamic.c`.
69 |
70 | #### Custom `main()` function ####
71 | Together with win32_utf8's entry point wrappers, changing the name and parameter list of your `main()` or `WinMain()` function to
72 |
73 | ```c
74 | int __cdecl win32_utf8_main(int argc, const char *argv[])
75 | ```
76 |
77 | 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.
78 |
79 | You then need to provide the *actual* entry point by compiling the correct `entry_*.c` file from this repository as part of your sources:
80 | * `entry_main.c` if your program runs in the console subsystem.
81 | * `entry_winmain.c` if your program runs in the graphical Windows subsystem and should not open a console window. Requires `-mwindows` on GCC.
82 |
83 | 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.
84 |
85 | #### Compiler support ####
86 | **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.)
87 |
--------------------------------------------------------------------------------
/Release.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | false
5 | false
6 | true
7 |
8 |
9 |
10 | true
11 |
12 | NDEBUG;%(PreprocessorDefinitions)
13 | MaxSpeed
14 | true
15 | AnySuitable
16 | true
17 | true
18 | true
19 | true
20 | true
21 | StreamingSIMDExtensions2
22 | false
23 | /Qfast_transcendentals /Zo /Gw %(AdditionalOptions)
24 | OldStyle
25 |
26 |
27 | true
28 | true
29 | UseLinkTimeCodeGeneration
30 | MachineX86
31 | MachineX64
32 |
33 |
34 | true
35 | MachineX86
36 | MachineX64
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/Static.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | StaticLibrary
5 | static
6 |
7 |
8 |
--------------------------------------------------------------------------------
/UNLICENSE.txt:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/entry_main.c:
--------------------------------------------------------------------------------
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 wmain(void)
15 | {
16 | return win32_utf8_entry(win32_utf8_main);
17 | }
18 |
19 | // If both main() and wmain() are defined...
20 | // • Visual Studio (or more specifically, LINK.EXE) defaults to main()
21 | // • Pelles C defaults to wmain(), without even printing a "ambiguous entry
22 | // point" warning
23 | // • MinGW/GCC doesn't care, and expects wmain() if you specify -municode,
24 | // and main() by default.
25 | // Thus, we keep main() as a convenience fallback for GCC.
26 |
27 | #ifndef _MSC_VER
28 |
29 | int __cdecl main(void)
30 | {
31 | return win32_utf8_entry(win32_utf8_main);
32 | }
33 |
34 | #endif
35 |
--------------------------------------------------------------------------------
/entry_winmain.c:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * WinMain() entry point.
7 | * Compile or #include this file as part of your sources if your program runs
8 | * in the graphical Windows subsystem and should not open a console window.
9 | */
10 |
11 | #include "src/entry.h"
12 |
13 | int __stdcall wWinMain(
14 | // Avoid redefinition errors if wWinMain() was declared before, but don't
15 | // depend on it to keep compilation times low.
16 | #ifdef _WINBASE_
17 | HINSTANCE hInstance,
18 | HINSTANCE hNull,
19 | LPWSTR lpCmdLine,
20 | #else
21 | void *hInstance,
22 | void *hNull,
23 | unsigned short *lpCmdLine,
24 | #endif
25 | int nCmdShow
26 | )
27 | {
28 | (void)hInstance;
29 | (void)hNull;
30 | (void)lpCmdLine;
31 | (void)nCmdShow;
32 | return win32_utf8_entry(win32_utf8_main);
33 | }
34 |
35 | // If both WinMain() and wWinMain() are defined...
36 | // • Visual Studio (or more specifically, LINK.EXE) defaults to WinMain()
37 | // • Pelles C defaults to wWinMain(), without even printing a "ambiguous
38 | // entry point" warning
39 | // • MinGW/GCC doesn't care, and expects wWinMain() if you specify -municode
40 | // and -mwindows, and WinMain() if you only specify -mwindows.
41 | // Thus, we keep WinMain() as a convenience fallback for GCC.
42 |
43 | #ifndef _MSC_VER
44 |
45 | int __stdcall WinMain(
46 | // Avoid redefinition errors if WinMain() was declared before, but don't
47 | // depend on it to keep compilation times low.
48 | #ifdef _WINBASE_
49 | HINSTANCE hInstance,
50 | HINSTANCE hNull,
51 | LPSTR lpCmdLine,
52 | #else
53 | void *hInstance,
54 | void *hNull,
55 | const char *lpCmdLine,
56 | #endif
57 | int nCmdShow
58 | )
59 | {
60 | (void)hInstance;
61 | (void)hNull;
62 | (void)lpCmdLine;
63 | (void)nCmdShow;
64 | return win32_utf8_entry(win32_utf8_main);
65 | }
66 |
67 | #endif
68 |
--------------------------------------------------------------------------------
/os_compat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thpatch/win32_utf8/b363f8c9f46982781b307a18c3fa261c190d4abe/os_compat.png
--------------------------------------------------------------------------------
/src/build.c:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * Code shared between static and dynamic builds.
7 | */
8 |
9 | #pragma warning(error: 4028)
10 |
11 | // We're targeting older C runtime versions, too.
12 | #ifndef _CRT_SECURE_NO_WARNINGS
13 | # define _CRT_SECURE_NO_WARNINGS
14 | #endif
15 |
16 | #define PSAPI_VERSION 1
17 | #define CINTERFACE
18 | #define COBJMACROS
19 |
20 | #define WIN32_UTF8_MAIN_UNIT
21 |
22 | #pragma comment(lib, "comdlg32.lib")
23 | #pragma comment(lib, "dsound.lib")
24 | #pragma comment(lib, "gdi32.lib")
25 | #pragma comment(lib, "ole32.lib")
26 | #pragma comment(lib, "psapi.lib")
27 | #pragma comment(lib, "shell32.lib")
28 | #pragma comment(lib, "shlwapi.lib")
29 | #pragma comment(lib, "user32.lib")
30 | #pragma comment(lib, "version.lib")
31 | #pragma comment(lib, "wininet.lib")
32 |
33 | // Headers
34 | #include "../win32_utf8.h"
35 | #include "wrappers.h"
36 | #include
37 |
38 | // Helper functions
39 | #include "macros.c"
40 | #include "utf.c"
41 | #include "win32_utf8.c"
42 | #include "wrappers.c"
43 |
44 | // Wrappers
45 | #include "comdlg32_dll.c"
46 | #include "dsound_dll.c"
47 | #include "gdi32_dll.c"
48 | #include "kernel32_dll.c"
49 | #include "msvcrt_dll.c"
50 | #include "psapi_dll.c"
51 | #include "shell32_dll.c"
52 | #include "shlwapi_dll.c"
53 | #include "user32_dll.c"
54 | #include "version_dll.c"
55 | #include "wininet_dll.c"
56 |
--------------------------------------------------------------------------------
/src/comdlg32_dll.c:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * comdlg32.dll functions.
7 | */
8 |
9 | const w32u8_pair_t comdlg32_pairs[] = {
10 | {"GetOpenFileNameA", GetOpenFileNameU},
11 | {"GetSaveFileNameA", GetSaveFileNameU},
12 | { NULL }
13 | };
14 |
15 | /// Wrappers
16 | /// --------
17 | typedef BOOL WINAPI WrapOFNFunc_t(
18 | LPOPENFILENAMEW ofn_w
19 | );
20 |
21 | static BOOL WrapOFN(
22 | WrapOFNFunc_t *func,
23 | LPOPENFILENAMEA ofn_a
24 | )
25 | {
26 | if (ofn_a) {
27 | size_t lpstrFilter_len = ofn_a->lpstrFilter ? zzstrlen(ofn_a->lpstrFilter) + 2 : 0;
28 | size_t lpstrInitialDir_len = ofn_a->lpstrInitialDir ? strlen(ofn_a->lpstrInitialDir) + 1 : 0;
29 | size_t lpstrTitle_len = ofn_a->lpstrTitle ? strlen(ofn_a->lpstrTitle) + 1 : 0;
30 | size_t lpstrDefExt_len = ofn_a->lpstrDefExt ? strlen(ofn_a->lpstrDefExt) + 1 : 0;
31 | size_t lpTemplateName_len = RESID_IS_STR(ofn_a->lpTemplateName) ? strlen(ofn_a->lpTemplateName) + 1 : 0;
32 | size_t lpstrCustomFilter_len = ofn_a->lpstrCustomFilter ? ofn_a->nMaxCustFilter : 0;
33 | size_t lpstrFileTitle_len = ofn_a->lpstrFileTitle ? ofn_a->nMaxFileTitle : 0;
34 |
35 | size_t total_len = lpstrFilter_len + lpstrCustomFilter_len + ofn_a->nMaxFile + lpstrFileTitle_len + lpstrInitialDir_len + lpstrTitle_len + lpstrDefExt_len + lpTemplateName_len;
36 | VLA(wchar_t, param_buffers, total_len);
37 | wchar_t* param_buffer_write = param_buffers;
38 |
39 | VLA(BYTE, ofn_w_raw, ofn_a->lStructSize);
40 | OPENFILENAMEW* ofn_w = (OPENFILENAMEW*)ofn_w_raw;
41 | memcpy(ofn_w, ofn_a, ofn_a->lStructSize);
42 |
43 | if (ofn_a->lpstrFilter) {
44 | size_t written = StringToUTF16(param_buffer_write, ofn_a->lpstrFilter, lpstrFilter_len);
45 | ofn_w->lpstrFilter = param_buffer_write;
46 | param_buffer_write += written;
47 | }
48 | if (ofn_a->lpstrCustomFilter) {
49 | size_t written = StringToUTF16(param_buffer_write, ofn_a->lpstrCustomFilter, ofn_a->nMaxCustFilter);
50 | ofn_w->lpstrCustomFilter = param_buffer_write;
51 | param_buffer_write += written;
52 | }
53 | if (ofn_a->lpstrFileTitle) {
54 | size_t written = StringToUTF16(param_buffer_write, ofn_a->lpstrFileTitle, ofn_a->nMaxFileTitle);
55 | ofn_w->lpstrFileTitle = param_buffer_write;
56 | param_buffer_write += written;
57 | }
58 | if (ofn_a->lpstrInitialDir) {
59 | size_t written = StringToUTF16(param_buffer_write, ofn_a->lpstrInitialDir, lpstrInitialDir_len);
60 | ofn_w->lpstrInitialDir = param_buffer_write;
61 | param_buffer_write += written;
62 | }
63 | if (ofn_a->lpstrTitle) {
64 | size_t written = StringToUTF16(param_buffer_write, ofn_a->lpstrTitle, lpstrTitle_len);
65 | ofn_w->lpstrTitle = param_buffer_write;
66 | param_buffer_write += written;
67 | }
68 | if (ofn_a->lpstrDefExt) {
69 | size_t written = StringToUTF16(param_buffer_write, ofn_a->lpstrDefExt, lpstrDefExt_len);
70 | ofn_w->lpstrDefExt = param_buffer_write;
71 | param_buffer_write += written;
72 | }
73 | if (RESID_IS_STR(ofn_a->lpTemplateName)) {
74 | size_t written = StringToUTF16(param_buffer_write, ofn_a->lpTemplateName, lpTemplateName_len);
75 | ofn_w->lpstrDefExt = param_buffer_write;
76 | param_buffer_write += written;
77 | }
78 |
79 | StringToUTF16(param_buffer_write, ofn_a->lpstrFile, ofn_a->nMaxFile);
80 | ofn_w->lpstrFile = param_buffer_write;
81 |
82 | BOOL ret = func(ofn_w);
83 |
84 | if (ret) {
85 | StringToUTF8(ofn_a->lpstrFile, ofn_w->lpstrFile, ofn_a->nMaxFile);
86 | ofn_a->nFileOffset = WideCharToMultiByte(
87 | CP_UTF8, 0, ofn_w->lpstrFile, ofn_w->nFileOffset, NULL, 0, NULL, NULL
88 | );
89 | ofn_a->nFileExtension = ofn_w->nFileExtension ? WideCharToMultiByte(
90 | CP_UTF8, 0, ofn_w->lpstrFile, ofn_w->nFileExtension, NULL, 0, NULL, NULL
91 | ) : 0;
92 | } else {
93 | *(WORD*)ofn_a->lpstrFile = *(WORD*)ofn_w->lpstrFile;
94 | }
95 | if (ofn_a->lpstrCustomFilter) {
96 | StringToUTF8(ofn_a->lpstrCustomFilter, ofn_w->lpstrCustomFilter, ofn_a->nMaxCustFilter);
97 | }
98 | if (ofn_a->lpstrFileTitle) {
99 | StringToUTF8(ofn_a->lpstrFileTitle, ofn_w->lpstrFileTitle, ofn_a->nMaxFileTitle);
100 | }
101 |
102 | VLA_FREE(ofn_w_raw);
103 | VLA_FREE(param_buffers);
104 | return ret;
105 | }
106 | return func(NULL);
107 | }
108 | /// --------
109 |
110 | BOOL WINAPI GetOpenFileNameU(
111 | LPOPENFILENAMEA ofn_a
112 | )
113 | {
114 | return WrapOFN(GetOpenFileNameW, ofn_a);
115 | }
116 |
117 | BOOL WINAPI GetSaveFileNameU(
118 | LPOPENFILENAMEA ofn_a
119 | )
120 | {
121 | return WrapOFN(GetSaveFileNameW, ofn_a);
122 | }
123 |
--------------------------------------------------------------------------------
/src/comdlg32_dll.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * comdlg32.dll functions.
7 | */
8 |
9 | #pragma once
10 |
11 | WRAPPER_DEC(BOOL WINAPI, GetOpenFileName,
12 | LPOPENFILENAMEA ofn_a
13 | );
14 | #undef GetOpenFileName
15 | #define GetOpenFileName GetOpenFileNameU
16 |
17 | WRAPPER_DEC(BOOL WINAPI, GetSaveFileName,
18 | LPOPENFILENAMEA ofn_a
19 | );
20 | #undef GetSaveFileName
21 | #define GetSaveFileName GetSaveFileNameU
22 |
--------------------------------------------------------------------------------
/src/dsound_dll.c:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * dsound.dll functions.
7 | */
8 |
9 | const w32u8_pair_t dsound_pairs[] = {
10 | {"DirectSoundCaptureEnumerateA", DirectSoundCaptureEnumerateU},
11 | {"DirectSoundEnumerateA", DirectSoundEnumerateU},
12 | { NULL }
13 | };
14 |
15 | typedef struct {
16 | LPDSENUMCALLBACKA lpOrigProc;
17 | LPVOID lOrigParam;
18 | } DSEnumParam;
19 |
20 | BOOL CALLBACK DSEnumCallbackWrap(
21 | LPGUID lpGuid,
22 | LPCWSTR lpcstrDescription,
23 | LPCWSTR lpcstrModule,
24 | DSEnumParam *wrap_param
25 | )
26 | {
27 | BOOL ret;
28 | UTF8_DEC(lpcstrDescription);
29 | UTF8_DEC(lpcstrModule);
30 | UTF8_CONV(lpcstrDescription);
31 | UTF8_CONV(lpcstrModule);
32 | ret = wrap_param->lpOrigProc(
33 | lpGuid, lpcstrDescription_utf8, lpcstrModule_utf8, wrap_param->lOrigParam
34 | );
35 | UTF8_FREE(lpcstrDescription);
36 | UTF8_FREE(lpcstrModule);
37 | return ret;
38 | }
39 |
40 | HRESULT WINAPI DirectSoundCaptureEnumerateU(
41 | LPDSENUMCALLBACKA pDSEnumCallback,
42 | LPVOID pContext
43 | )
44 | {
45 | DSEnumParam wrap_param = {pDSEnumCallback, pContext};
46 | return DirectSoundCaptureEnumerateW(
47 | (LPDSENUMCALLBACKW)DSEnumCallbackWrap, &wrap_param
48 | );
49 | }
50 |
51 | HRESULT WINAPI DirectSoundEnumerateU(
52 | LPDSENUMCALLBACKA pDSEnumCallback,
53 | LPVOID pContext
54 | )
55 | {
56 | DSEnumParam wrap_param = {pDSEnumCallback, pContext};
57 | return DirectSoundEnumerateW(
58 | (LPDSENUMCALLBACKW)DSEnumCallbackWrap, &wrap_param
59 | );
60 | }
61 |
--------------------------------------------------------------------------------
/src/dsound_dll.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * dsound.dll functions.
7 | */
8 |
9 | #pragma once
10 |
11 | WRAPPER_DEC(HRESULT WINAPI, DirectSoundCaptureEnumerate,
12 | LPDSENUMCALLBACKA pDSEnumCallback,
13 | LPVOID pContext
14 | );
15 | #undef DirectSoundCaptureEnumerate
16 | #define DirectSoundCaptureEnumerate DirectSoundCaptureEnumerateU
17 |
18 | WRAPPER_DEC(HRESULT WINAPI, DirectSoundEnumerate,
19 | LPDSENUMCALLBACKA pDSEnumCallback,
20 | LPVOID pContext
21 | );
22 | #undef DirectSoundEnumerate
23 | #define DirectSoundEnumerate DirectSoundEnumerateU
24 |
--------------------------------------------------------------------------------
/src/entry.c:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * Entry point wrapper, converting the command-line parameters to UTF-8.
7 | */
8 |
9 | int win32_utf8_entry(main_t *user_main)
10 | {
11 | int ret;
12 | int argc_w = 0;
13 | char **argv_u = CommandLineToArgvU(GetCommandLineW(), &argc_w);
14 | if(argv_u) {
15 | assert(user_main);
16 |
17 | win32_utf8_init();
18 | ret = user_main(argc_w, (const char**)argv_u);
19 | win32_utf8_exit();
20 | LocalFree(argv_u);
21 | } else {
22 | ret = GetLastError();
23 | fprintf(stderr, "win32_utf8: Error converting the command line!?\n");
24 | }
25 | return ret;
26 | }
27 |
--------------------------------------------------------------------------------
/src/entry.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * Entry point wrapper, converting the command-line parameters to UTF-8.
7 | */
8 |
9 | // The entry points themselves only need this header, and could be #included
10 | // from C++ source files, so we need to do this here again.
11 | #ifdef __cplusplus
12 | extern "C" {
13 | #endif
14 |
15 | // User-defined, subsystem-independent main function. Compile or #include one
16 | // of the entry_*.c files from the top directory, depending on which entry
17 | // point you want to use.
18 | int __cdecl win32_utf8_main(int argc, const char *argv[]);
19 | #define main win32_utf8_main
20 |
21 | typedef int __cdecl main_t(int argc, const char *argv[]);
22 |
23 | // Performs the conversion and calls [user_main] with an UTF-8 argv.
24 | int win32_utf8_entry(main_t *user_main);
25 |
26 | #ifdef __cplusplus
27 | }
28 | #endif
29 |
--------------------------------------------------------------------------------
/src/gdi32_dll.c:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * gdi32.dll functions.
7 | */
8 |
9 | const w32u8_pair_t gdi32_pairs[] = {
10 | {"AddFontResourceExA", AddFontResourceExU},
11 | {"CreateFontA", CreateFontU},
12 | {"CreateFontIndirectA", CreateFontIndirectU},
13 | {"CreateFontIndirectExA", CreateFontIndirectExU},
14 | {"EnumFontsA", EnumFontFamiliesU},
15 | {"EnumFontFamiliesA", EnumFontFamiliesU},
16 | {"EnumFontFamiliesExA", EnumFontFamiliesExU},
17 | {"ExtTextOutA", ExtTextOutU},
18 | {"GetGlyphOutlineA", GetGlyphOutlineU},
19 | {"GetTextExtentPoint32A", GetTextExtentPoint32U},
20 | {"RemoveFontResourceExA", RemoveFontResourceExU},
21 | {"TextOutA", TextOutU},
22 | {"PolyTextOutA", PolyTextOutU},
23 | { NULL }
24 | };
25 |
26 | /// Font conversion helpers
27 | /// -----------------------
28 | static LOGFONTA* LogfontWToA(LOGFONTA *a, const LOGFONTW *w)
29 | {
30 | if(w) {
31 | memcpy(a, w, offsetof(LOGFONT, lfFaceName));
32 | StringToUTF8(a->lfFaceName, w->lfFaceName, LF_FACESIZE);
33 | }
34 | return w ? a : NULL;
35 | }
36 |
37 | static LOGFONTW* LogfontAToW(LOGFONTW *w, const LOGFONTA *a)
38 | {
39 | if(a) {
40 | // Limit the number of converted bytes to the actual length of
41 | // the face name, as any garbage past the 0 byte would cause
42 | // MultiByteToWideChar() with MB_ERR_INVALID_CHARS to fail.
43 | // This should maybe be done in StringToUTF16(), but I'm not
44 | // sure whether that would break something else...
45 | // (Fixes udm Self Extract Updater.)
46 | size_t facename_len = strnlen(a->lfFaceName, LF_FACESIZE) + 1;
47 | memcpy(w, a, offsetof(LOGFONT, lfFaceName));
48 | StringToUTF16(w->lfFaceName, a->lfFaceName, facename_len);
49 | }
50 | return a ? w : NULL;
51 | }
52 |
53 | static ENUMLOGFONTEXDVA* EnumLogfontExDVWToA(ENUMLOGFONTEXDVA *a, const ENUMLOGFONTEXDVW *w)
54 | {
55 | if(w) {
56 | const ENUMLOGFONTEXW *elfe_w = &w->elfEnumLogfontEx;
57 | ENUMLOGFONTEXA *elfe_a = &a->elfEnumLogfontEx;
58 | // WinGDI.h says: "The actual size of the DESIGNVECTOR and
59 | // ENUMLOGFONTEXDV structures is determined by dvNumAxes,
60 | // MM_MAX_NUMAXES only detemines the maximal size allowed"
61 | DWORD dv_sizediff = (
62 | MM_MAX_NUMAXES - min(w->elfDesignVector.dvNumAxes, MM_MAX_NUMAXES)
63 | ) * sizeof(LONG);
64 |
65 | LogfontWToA(&elfe_a->elfLogFont, &elfe_w->elfLogFont);
66 | StringToUTF8((char*)elfe_a->elfFullName, elfe_w->elfFullName, LF_FULLFACESIZE);
67 | StringToUTF8((char*)elfe_a->elfStyle, elfe_w->elfStyle, LF_FACESIZE);
68 | StringToUTF8((char*)elfe_a->elfScript, elfe_w->elfScript, LF_FACESIZE);
69 | memcpy(&a->elfDesignVector, &w->elfDesignVector, sizeof(DESIGNVECTOR) - dv_sizediff);
70 | }
71 | return w ? a : NULL;
72 | }
73 |
74 | static ENUMLOGFONTEXDVW* EnumLogfontExDVAToW(ENUMLOGFONTEXDVW *w, const ENUMLOGFONTEXDVA *a)
75 | {
76 | if(w) {
77 | const ENUMLOGFONTEXA *elfe_a = &a->elfEnumLogfontEx;
78 | ENUMLOGFONTEXW *elfe_w = &w->elfEnumLogfontEx;
79 | DWORD dv_sizediff = (
80 | MM_MAX_NUMAXES - min(a->elfDesignVector.dvNumAxes, MM_MAX_NUMAXES)
81 | ) * sizeof(LONG);
82 |
83 | LogfontAToW(&elfe_w->elfLogFont, &elfe_a->elfLogFont);
84 | StringToUTF16(elfe_w->elfFullName, (char*)elfe_a->elfFullName, LF_FULLFACESIZE);
85 | StringToUTF16(elfe_w->elfStyle, (char*)elfe_a->elfStyle, LF_FACESIZE);
86 | StringToUTF16(elfe_w->elfScript, (char*)elfe_a->elfScript, LF_FACESIZE);
87 | memcpy(&w->elfDesignVector, &a->elfDesignVector, sizeof(DESIGNVECTOR) - dv_sizediff);
88 | }
89 | return a ? w : NULL;
90 | }
91 |
92 | static ENUMTEXTMETRICA* EnumTextmetricWToA(ENUMTEXTMETRICA *a, const ENUMTEXTMETRICW *w)
93 | {
94 | if(w) {
95 | const NEWTEXTMETRICW *ntm_w = &w->etmNewTextMetricEx.ntmTm;
96 | NEWTEXTMETRICA *ntm_a = &a->etmNewTextMetricEx.ntmTm;
97 | DWORD i = 0;
98 |
99 | memcpy(ntm_a, ntm_w, offsetof(NEWTEXTMETRIC, tmFirstChar));
100 | memcpy(&ntm_a->tmItalic, &ntm_w->tmItalic, sizeof(NEWTEXTMETRIC) - offsetof(NEWTEXTMETRIC, tmItalic));
101 | ntm_a->tmFirstChar = min(0xff, ntm_w->tmFirstChar);
102 | ntm_a->tmLastChar = min(0xff, ntm_w->tmLastChar);
103 | ntm_a->tmDefaultChar = min(0xff, ntm_w->tmDefaultChar);
104 | ntm_a->tmBreakChar = min(0xff, ntm_w->tmBreakChar);
105 | memcpy(&a->etmNewTextMetricEx.ntmFontSig, &w->etmNewTextMetricEx.ntmFontSig, sizeof(FONTSIGNATURE));
106 | memcpy(&a->etmAxesList, &w->etmAxesList, offsetof(AXESLIST, axlAxisInfo));
107 | for(i = 0; i < w->etmAxesList.axlNumAxes; i++) {
108 | const AXISINFOW *ai_w = &w->etmAxesList.axlAxisInfo[i];
109 | AXISINFOA *ai_a = &a->etmAxesList.axlAxisInfo[i];
110 | memcpy(ai_a, ai_w, offsetof(AXISINFO, axAxisName));
111 | StringToUTF8((char*)ai_a->axAxisName, ai_w->axAxisName, MM_MAX_AXES_NAMELEN);
112 | }
113 | }
114 | return w ? a : NULL;
115 | }
116 | /// -----------------------
117 |
118 | /// Promotion wrappers
119 | /// ------------------
120 | HFONT WINAPI lower_CreateFontA(
121 | CreateFontIndirectA_type *down_func,
122 | int cHeight,
123 | int cWidth,
124 | int cEscapement,
125 | int cOrientation,
126 | int cWeight,
127 | DWORD bItalic,
128 | DWORD bUnderline,
129 | DWORD bStrikeOut,
130 | DWORD iCharSet,
131 | DWORD iOutPrecision,
132 | DWORD iClipPrecision,
133 | DWORD iQuality,
134 | DWORD iPitchAndFamily,
135 | LPCSTR pszFaceName
136 | )
137 | {
138 | LOGFONTA lf_a = {
139 | cHeight, cWidth, cEscapement, cOrientation, cWeight, (BYTE)bItalic,
140 | (BYTE)bUnderline, (BYTE)bStrikeOut, (BYTE)iCharSet, (BYTE)iOutPrecision,
141 | (BYTE)iClipPrecision, (BYTE)iQuality, (BYTE)iPitchAndFamily, ""
142 | };
143 | // Yes, Windows does the same internally. CreateFont() is *not* a way
144 | // to pass a face name longer than 32 characters.
145 | if(pszFaceName) {
146 | strncpy(lf_a.lfFaceName, pszFaceName, sizeof(lf_a.lfFaceName));
147 | }
148 | return down_func(&lf_a);
149 | }
150 |
151 | HFONT WINAPI lower_CreateFontIndirectA(
152 | CreateFontIndirectExA_type *down_func,
153 | CONST LOGFONTA *lplf
154 | )
155 | {
156 | ENUMLOGFONTEXDVA elfedv_a;
157 | const size_t elfedv_lf_diff =
158 | sizeof(ENUMLOGFONTEXDVA) - offsetof(ENUMLOGFONTEXDVA, elfEnumLogfontEx.elfFullName)
159 | ;
160 | if(!lplf) {
161 | return NULL;
162 | }
163 | memcpy(&elfedv_a.elfEnumLogfontEx.elfLogFont, lplf, sizeof(LOGFONTA));
164 | ZeroMemory(&elfedv_a.elfEnumLogfontEx.elfFullName, elfedv_lf_diff);
165 | return down_func(&elfedv_a);
166 | }
167 |
168 | int WINAPI lower_EnumFontFamiliesA(
169 | EnumFontFamiliesExA_type *down_func,
170 | HDC hdc,
171 | LPCSTR pszFaceName,
172 | FONTENUMPROCA lpProc,
173 | LPARAM lParam
174 | )
175 | {
176 | LOGFONTA lf;
177 | LOGFONTA *plf = NULL;
178 |
179 | if(pszFaceName) {
180 | if(!pszFaceName[0]) {
181 | return 1;
182 | }
183 | strncpy(lf.lfFaceName, pszFaceName, sizeof(lf.lfFaceName));
184 | lf.lfCharSet = DEFAULT_CHARSET;
185 | lf.lfPitchAndFamily = 0;
186 | plf = &lf;
187 | }
188 | return down_func(hdc, plf, lpProc, lParam, 0);
189 | }
190 | ///
191 |
192 | /// Promotion wrappers (Unicode)
193 | ///
194 | HFONT WINAPI lower_CreateFontW(
195 | CreateFontIndirectW_type* down_func,
196 | int cHeight,
197 | int cWidth,
198 | int cEscapement,
199 | int cOrientation,
200 | int cWeight,
201 | DWORD bItalic,
202 | DWORD bUnderline,
203 | DWORD bStrikeOut,
204 | DWORD iCharSet,
205 | DWORD iOutPrecision,
206 | DWORD iClipPrecision,
207 | DWORD iQuality,
208 | DWORD iPitchAndFamily,
209 | LPCWSTR pszFaceName
210 | )
211 | {
212 | LOGFONTW lf_w = {
213 | cHeight, cWidth, cEscapement, cOrientation, cWeight, (BYTE)bItalic,
214 | (BYTE)bUnderline, (BYTE)bStrikeOut, (BYTE)iCharSet, (BYTE)iOutPrecision,
215 | (BYTE)iClipPrecision, (BYTE)iQuality, (BYTE)iPitchAndFamily, L""
216 | };
217 | // Yes, Windows does the same internally. CreateFont() is *not* a way
218 | // to pass a face name longer than 32 characters.
219 | if (pszFaceName) {
220 | wcsncpy(lf_w.lfFaceName, pszFaceName, elementsof(lf_w.lfFaceName));
221 | }
222 | return down_func(&lf_w);
223 | }
224 |
225 | HFONT WINAPI lower_CreateFontIndirectW(
226 | CreateFontIndirectExW_type* down_func,
227 | CONST LOGFONTW* lplf
228 | )
229 | {
230 | ENUMLOGFONTEXDVW elfedv_w;
231 | const size_t elfedv_lf_diff =
232 | sizeof(ENUMLOGFONTEXDVW) - offsetof(ENUMLOGFONTEXDVW, elfEnumLogfontEx.elfFullName)
233 | ;
234 | if (!lplf) {
235 | return NULL;
236 | }
237 | memcpy(&elfedv_w.elfEnumLogfontEx.elfLogFont, lplf, sizeof(LOGFONTW));
238 | ZeroMemory(&elfedv_w.elfEnumLogfontEx.elfFullName, elfedv_lf_diff);
239 | return down_func(&elfedv_w);
240 | }
241 |
242 | int WINAPI lower_EnumFontFamiliesW(
243 | EnumFontFamiliesExW_type* down_func,
244 | HDC hdc,
245 | LPCWSTR pszFaceName,
246 | FONTENUMPROCW lpProc,
247 | LPARAM lParam
248 | )
249 | {
250 | LOGFONTW lf;
251 | LOGFONTW* plf = NULL;
252 |
253 | if (pszFaceName) {
254 | if (!pszFaceName[0]) {
255 | return 1;
256 | }
257 | wcsncpy(lf.lfFaceName, pszFaceName, elementsof(lf.lfFaceName));
258 | lf.lfCharSet = DEFAULT_CHARSET;
259 | lf.lfPitchAndFamily = 0;
260 | plf = &lf;
261 | }
262 | return down_func(hdc, plf, lpProc, lParam, 0);
263 | }
264 | /// ---
265 |
266 | HFONT WINAPI CreateFontU(
267 | int cHeight,
268 | int cWidth,
269 | int cEscapement,
270 | int cOrientation,
271 | int cWeight,
272 | DWORD bItalic,
273 | DWORD bUnderline,
274 | DWORD bStrikeOut,
275 | DWORD iCharSet,
276 | DWORD iOutPrecision,
277 | DWORD iClipPrecision,
278 | DWORD iQuality,
279 | DWORD iPitchAndFamily,
280 | LPCSTR pszFaceName
281 | )
282 | {
283 | return lower_CreateFontA(CreateFontIndirectU,
284 | cHeight, cWidth, cEscapement, cOrientation, cWeight, bItalic,
285 | bUnderline, bStrikeOut, iCharSet, iOutPrecision,
286 | iClipPrecision, iQuality, iPitchAndFamily, pszFaceName
287 | );
288 | }
289 |
290 | HFONT WINAPI CreateFontIndirectU(
291 | CONST LOGFONTA *lplf
292 | )
293 | {
294 | return lower_CreateFontIndirectA(CreateFontIndirectExU, lplf);
295 | }
296 |
297 | HFONT WINAPI CreateFontIndirectExU(
298 | CONST ENUMLOGFONTEXDVA *lpelfe
299 | )
300 | {
301 | ENUMLOGFONTEXDVW elfedv_w;
302 | return CreateFontIndirectExW(EnumLogfontExDVAToW(&elfedv_w, lpelfe));
303 | }
304 |
305 | int WINAPI AddFontResourceExU(
306 | LPCSTR name,
307 | DWORD fl,
308 | PVOID res
309 | )
310 | {
311 | int ret;
312 | WCHAR_T_DEC(name);
313 | WCHAR_T_CONV(name);
314 | ret = AddFontResourceExW(name_w, fl, res);
315 | WCHAR_T_FREE(name);
316 | return ret;
317 | }
318 |
319 | int WINAPI RemoveFontResourceExU(
320 | LPCSTR name,
321 | DWORD fl,
322 | PVOID res
323 | )
324 | {
325 | int ret;
326 | WCHAR_T_DEC(name);
327 | WCHAR_T_CONV(name);
328 | ret = RemoveFontResourceExW(name_w, fl, res);
329 | WCHAR_T_FREE(name);
330 | return ret;
331 | }
332 |
333 | typedef struct {
334 | FONTENUMPROCA lpOrigProc;
335 | LPARAM lOrigParam;
336 | } EnumFontFamExParam;
337 |
338 | static int CALLBACK EnumFontFamExProcWrap(
339 | const ENUMLOGFONTEXDVW *lpelfe,
340 | const ENUMTEXTMETRICW *lpntme,
341 | DWORD FontType,
342 | EnumFontFamExParam *wrap_param
343 | )
344 | {
345 | if(wrap_param && wrap_param->lpOrigProc) {
346 | ENUMLOGFONTEXDVA elfedv_a;
347 | ENUMTEXTMETRICA etm_a;
348 | ENUMLOGFONTEXDVA *elfedv_a_ptr = EnumLogfontExDVWToA(&elfedv_a, lpelfe);
349 | ENUMTEXTMETRICA *etm_a_ptr = EnumTextmetricWToA(&etm_a, lpntme);
350 | return wrap_param->lpOrigProc(
351 | (LOGFONTA*)elfedv_a_ptr, (TEXTMETRICA*)etm_a_ptr, FontType, wrap_param->lOrigParam
352 | );
353 | }
354 | return 0;
355 | }
356 |
357 | int WINAPI EnumFontFamiliesU(
358 | HDC hdc,
359 | LPCSTR pszFaceName,
360 | FONTENUMPROCA lpProc,
361 | LPARAM lParam
362 | )
363 | {
364 | return lower_EnumFontFamiliesA(EnumFontFamiliesExU,
365 | hdc, pszFaceName, lpProc, lParam
366 | );
367 | }
368 |
369 | int WINAPI EnumFontFamiliesExU(
370 | HDC hdc,
371 | LPLOGFONTA lpLogfont,
372 | FONTENUMPROCA lpProc,
373 | LPARAM lParam,
374 | DWORD dwFlags
375 | )
376 | {
377 | EnumFontFamExParam wrap_param = {lpProc, lParam};
378 | LOGFONTW lf_w;
379 | LOGFONTW *lf_w_ptr = LogfontAToW(&lf_w, lpLogfont);
380 | return EnumFontFamiliesExW(
381 | hdc, lf_w_ptr, (FONTENUMPROCW)EnumFontFamExProcWrap, (LPARAM)&wrap_param, dwFlags
382 | );
383 | }
384 |
385 | BOOL WINAPI ExtTextOutU(
386 | HDC hdc,
387 | int x,
388 | int y,
389 | UINT options,
390 | CONST RECT * lprect,
391 | LPCSTR lpString,
392 | UINT c,
393 | CONST INT * lpDx
394 | )
395 | {
396 | BOOL ret;
397 | FixedLengthStringConvert(lpString, c);
398 | ret = ExtTextOutW(hdc, x, y, options, lprect, lpString_w, lpString_w_len, lpDx);
399 | WCHAR_T_FREE(lpString);
400 | return ret;
401 | }
402 |
403 | DWORD WINAPI GetGlyphOutlineU(
404 | HDC hdc,
405 | UINT uChar,
406 | UINT fuFormat,
407 | LPGLYPHMETRICS lpgm,
408 | DWORD cjBuffer,
409 | LPVOID pvBuffer,
410 | CONST MAT2 *lpmat2
411 | )
412 | {
413 | return GetGlyphOutlineW(
414 | hdc, CharToUTF16(uChar), fuFormat, lpgm, cjBuffer, pvBuffer, lpmat2
415 | );
416 | }
417 |
418 | BOOL APIENTRY GetTextExtentPoint32U(
419 | HDC hdc,
420 | LPCSTR lpString,
421 | int c,
422 | LPSIZE psizl
423 | )
424 | {
425 | BOOL ret;
426 | FixedLengthStringConvert(lpString, c);
427 | ret = GetTextExtentPoint32W(hdc, lpString_w, lpString_w_len, psizl);
428 | WCHAR_T_FREE(lpString);
429 | return ret;
430 | }
431 |
432 | BOOL WINAPI TextOutU(
433 | HDC hdc,
434 | int x,
435 | int y,
436 | LPCSTR lpString,
437 | int c
438 | )
439 | {
440 | return ExtTextOutU(hdc, x, y, 0, NULL, lpString, c, NULL);
441 | }
442 |
443 | BOOL WINAPI PolyTextOutU(
444 | HDC hdc,
445 | const POLYTEXTA* ppt,
446 | int nstrings
447 | )
448 | {
449 | BOOL ret = TRUE;
450 | for (int i = 0; i < nstrings; i++) {
451 | if (!ExtTextOutU(hdc, ppt[i].x, ppt[i].y, ppt[i].uiFlags, &ppt[i].rcl, ppt[i].lpstr, ppt[i].n, ppt[i].pdx)) {
452 | ret = FALSE;
453 | }
454 | }
455 | return ret;
456 | }
457 |
--------------------------------------------------------------------------------
/src/gdi32_dll.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * gdi32.dll functions.
7 | */
8 |
9 | #pragma once
10 |
11 | WRAPPER_DEC(int WINAPI, AddFontResourceEx,
12 | LPCSTR name,
13 | DWORD fl,
14 | PVOID res
15 | );
16 | #undef AddFontResourceEx
17 | #define AddFontResourceEx AddFontResourceExU
18 |
19 | WRAPPER_DEC(HFONT WINAPI, CreateFont,
20 | int cHeight,
21 | int cWidth,
22 | int cEscapement,
23 | int cOrientation,
24 | int cWeight,
25 | DWORD bItalic,
26 | DWORD bUnderline,
27 | DWORD bStrikeOut,
28 | DWORD iCharSet,
29 | DWORD iOutPrecision,
30 | DWORD iClipPrecision,
31 | DWORD iQuality,
32 | DWORD iPitchAndFamily,
33 | LPCSTR pszFaceName
34 | );
35 | #undef CreateFont
36 | #define CreateFont CreateFontU
37 |
38 | WRAPPER_DEC(HFONT WINAPI, CreateFontIndirect,
39 | CONST LOGFONTA *lplf
40 | );
41 | typedef HFONT WINAPI CreateFontIndirectW_type(
42 | const LOGFONTW *lplf
43 | );
44 | #undef CreateFontIndirect
45 | #define CreateFontIndirect CreateFontIndirectU
46 |
47 | WRAPPER_DEC(HFONT WINAPI, CreateFontIndirectEx,
48 | CONST ENUMLOGFONTEXDVA *lpelfe
49 | );
50 | typedef HFONT WINAPI CreateFontIndirectExW_type(
51 | CONST ENUMLOGFONTEXDVW *lpelfe
52 | );
53 | #undef CreateFontIndirectEx
54 | #define CreateFontIndirectEx CreateFontIndirectExU
55 |
56 | #undef EnumFonts
57 | #define EnumFonts EnumFontFamiliesU
58 |
59 | WRAPPER_DEC(int WINAPI, EnumFontFamilies,
60 | HDC hdc,
61 | LPCSTR pszFaceName,
62 | FONTENUMPROCA lpProc,
63 | LPARAM lParam
64 | );
65 | typedef int WINAPI EnumFontFamiliesW_type(
66 | HDC hdc,
67 | LPCWSTR pszFaceName,
68 | FONTENUMPROCW lpProc,
69 | LPARAM lParam
70 | );
71 | #undef EnumFontFamilies
72 | #define EnumFontFamilies EnumFontFamiliesU
73 |
74 | WRAPPER_DEC(int WINAPI, EnumFontFamiliesEx,
75 | HDC hdc,
76 | LPLOGFONTA lpLogfont,
77 | FONTENUMPROCA lpProc,
78 | LPARAM lParam,
79 | DWORD dwFlags
80 | );
81 | typedef int WINAPI EnumFontFamiliesExW_type(
82 | HDC hdc,
83 | LPLOGFONTW lpLogfont,
84 | FONTENUMPROCW lpProc,
85 | LPARAM lParam,
86 | DWORD dwFlags
87 | );
88 | #undef EnumFontFamiliesEx
89 | #define EnumFontFamiliesEx EnumFontFamiliesExU
90 |
91 | WRAPPER_DEC(BOOL WINAPI, ExtTextOut,
92 | HDC hdc,
93 | int x,
94 | int y,
95 | UINT options,
96 | CONST RECT * lprect,
97 | LPCSTR lpString,
98 | UINT c,
99 | CONST INT * lpDx
100 | );
101 | #undef ExtTextOut
102 | #define ExtTextOut ExtTextOutU
103 |
104 | WRAPPER_DEC(DWORD WINAPI, GetGlyphOutline,
105 | HDC hdc,
106 | UINT uChar,
107 | UINT fuFormat,
108 | LPGLYPHMETRICS lpgm,
109 | DWORD cjBuffer,
110 | LPVOID pvBuffer,
111 | CONST MAT2 *lpmat2
112 | );
113 | #undef GetGlyphOutline
114 | #define GetGlyphOutline GetGlyphOutlineU
115 |
116 | WRAPPER_DEC(BOOL APIENTRY, GetTextExtentPoint32,
117 | HDC hdc,
118 | LPCSTR lpString,
119 | int c,
120 | LPSIZE psizl
121 | );
122 | #undef GetTextExtentPoint32
123 | #define GetTextExtentPoint32 GetTextExtentPoint32U
124 |
125 | WRAPPER_DEC(int WINAPI, RemoveFontResourceEx,
126 | LPCSTR name,
127 | DWORD fl,
128 | PVOID res
129 | );
130 | #undef RemoveFontResourceEx
131 | #define RemoveFontResourceEx RemoveFontResourceExU
132 |
133 | WRAPPER_DEC(BOOL WINAPI, TextOut,
134 | HDC hdc,
135 | int x,
136 | int y,
137 | LPCSTR lpString,
138 | int c
139 | );
140 | #undef TextOut
141 | #define TextOut TextOutU
142 |
143 | WRAPPER_DEC(BOOL WINAPI, PolyTextOut,
144 | HDC hdc,
145 | const POLYTEXTA* ppt,
146 | int nstrings
147 | );
148 | #undef PolyTextOut
149 | #define PolyTextOut PolyTextOutU
150 |
151 | /// Promotion wrappers
152 | /// ------------------
153 | HFONT WINAPI lower_CreateFontA(
154 | CreateFontIndirectA_type *down_func,
155 | int cHeight,
156 | int cWidth,
157 | int cEscapement,
158 | int cOrientation,
159 | int cWeight,
160 | DWORD bItalic,
161 | DWORD bUnderline,
162 | DWORD bStrikeOut,
163 | DWORD iCharSet,
164 | DWORD iOutPrecision,
165 | DWORD iClipPrecision,
166 | DWORD iQuality,
167 | DWORD iPitchAndFamily,
168 | LPCSTR pszFaceName
169 | );
170 |
171 | HFONT WINAPI lower_CreateFontIndirectA(
172 | CreateFontIndirectExA_type *down_func,
173 | CONST LOGFONTA *lplf
174 | );
175 |
176 | int WINAPI lower_EnumFontFamiliesA(
177 | EnumFontFamiliesExA_type *down_func,
178 | HDC hdc,
179 | LPCSTR lpLogfont,
180 | FONTENUMPROCA lpProc,
181 | LPARAM lParam
182 | );
183 |
184 | HFONT WINAPI lower_CreateFontW(
185 | CreateFontIndirectW_type *down_func,
186 | int cHeight,
187 | int cWidth,
188 | int cEscapement,
189 | int cOrientation,
190 | int cWeight,
191 | DWORD bItalic,
192 | DWORD bUnderline,
193 | DWORD bStrikeOut,
194 | DWORD iCharSet,
195 | DWORD iOutPrecision,
196 | DWORD iClipPrecision,
197 | DWORD iQuality,
198 | DWORD iPitchAndFamily,
199 | LPCWSTR pszFaceName
200 | );
201 |
202 | HFONT WINAPI lower_CreateFontIndirectW(
203 | CreateFontIndirectExW_type *down_func,
204 | CONST LOGFONTW *lplf
205 | );
206 |
207 | int WINAPI lower_EnumFontFamiliesW(
208 | EnumFontFamiliesExW_type *down_func,
209 | HDC hdc,
210 | LPCWSTR lpLogfont,
211 | FONTENUMPROCW lpProc,
212 | LPARAM lParam
213 | );
214 | /// ------------------
215 |
--------------------------------------------------------------------------------
/src/kernel32_dll.c:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * kernel32.dll functions.
7 | */
8 |
9 | const w32u8_pair_t kernel32_pairs[] = {
10 | {"CopyFileA", CopyFileU},
11 | {"CopyFileExA", CopyFileExU},
12 | {"CreateDirectoryA", CreateDirectoryU},
13 | {"CreateFileA", CreateFileU},
14 | {"CreateFileMappingA", CreateFileMappingU},
15 | {"CreateProcessA", CreateProcessU},
16 | {"DeleteFileA", DeleteFileU},
17 | {"FindFirstFileA", FindFirstFileU},
18 | {"FindNextFileA", FindNextFileU},
19 | {"FormatMessageA", FormatMessageU},
20 | {"GetCurrentDirectoryA", GetCurrentDirectoryU},
21 | {"GetEnvironmentVariableA", GetEnvironmentVariableU},
22 | {"GetFileAttributesA", GetFileAttributesU},
23 | {"GetFileAttributesExA", GetFileAttributesExU},
24 | {"GetFullPathNameA", GetFullPathNameU},
25 | {"GetCommandLineA", GetCommandLineU},
26 | {"GetModuleFileNameA", GetModuleFileNameU},
27 | {"GetModuleHandleExA", GetModuleHandleExU},
28 | {"GetPrivateProfileIntA", GetPrivateProfileIntU},
29 | {"GetPrivateProfileStringA", GetPrivateProfileStringU},
30 | {"GetStartupInfoA", GetStartupInfoU},
31 | {"GetTempFileNameA", GetTempFileNameU},
32 | {"GetTempPathA", GetTempPathU},
33 | {"IsDBCSLeadByte", IsDBCSLeadByteFB},
34 | {"LoadLibraryA", LoadLibraryU},
35 | {"MoveFileA", MoveFileU},
36 | {"MoveFileExA", MoveFileExU},
37 | {"MoveFileWithProgressA", MoveFileWithProgressU},
38 | {"MultiByteToWideChar", MultiByteToWideCharU},
39 | {"OpenFileMappingA", OpenFileMappingU},
40 | {"ReadFile", ReadFileU},
41 | {"RemoveDirectoryA", RemoveDirectoryU},
42 | {"SetCurrentDirectoryA", SetCurrentDirectoryU},
43 | {"SetEnvironmentVariableA", SetEnvironmentVariableU},
44 | {"WideCharToMultiByte", WideCharToMultiByteU},
45 | {"WriteFile", WriteFileU},
46 | {"WritePrivateProfileStringA", WritePrivateProfileStringU},
47 | { NULL }
48 | };
49 |
50 | // GetStartupInfo
51 | // --------------
52 | static char *startupinfo_desktop = NULL;
53 | static char *startupinfo_title = NULL;
54 | // --------------
55 |
56 | // INI conversion
57 | // --------------
58 | static BOOL EnsurePrivateProfileUTF16(LPCWSTR fn)
59 | {
60 | // These are all supported encodings, at least according to Wine.
61 | const BYTE BOM_UTF16_LE[] = {0xFF, 0xFE};
62 | const BYTE BOM_UTF16_BE[] = {0xFE, 0xFF};
63 | const BYTE BOM_UTF8[] = {0xEF, 0xBB, 0xBF};
64 |
65 | BOOL ret = 0;
66 | DWORD byte_ret;
67 | LARGE_INTEGER file_size;
68 | HANDLE hHeap = GetProcessHeap();
69 | size_t cont_a_len;
70 | size_t cont_w_len;
71 | LPSTR cont_a = NULL;
72 | LPWSTR cont_w = NULL;
73 | HANDLE hFile = CreateFileW(
74 | fn, GENERIC_READ | GENERIC_WRITE, 0, NULL,
75 | OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL
76 | );
77 | if(hFile == INVALID_HANDLE_VALUE) {
78 | return 0;
79 | }
80 | if(
81 | GetFileSizeEx(hFile, &file_size)
82 | && file_size.QuadPart > sizeof(BOM_UTF8)
83 | ) {
84 | BYTE file_bom[3];
85 | cont_a_len = (size_t)file_size.QuadPart;
86 | if(!ReadFile(hFile, file_bom, sizeof(file_bom), &byte_ret, NULL)) {
87 | goto end;
88 | }
89 | // Nothing to do if we're UTF-16, but seek back if we're ANSI
90 | if(
91 | !memcmp(file_bom, BOM_UTF16_LE, sizeof(BOM_UTF16_LE))
92 | || !memcmp(file_bom, BOM_UTF16_BE, sizeof(BOM_UTF16_BE))
93 | ) {
94 | ret = 1;
95 | goto end;
96 | } else if(!memcmp(file_bom, BOM_UTF8, sizeof(BOM_UTF8))) {
97 | cont_a_len -= sizeof(BOM_UTF8);
98 | } else {
99 | SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
100 | }
101 | // OK, we have to do a full file conversion
102 | cont_a = HeapAlloc(hHeap, 0, cont_a_len * sizeof(char));
103 | cont_w = HeapAlloc(hHeap, 0, cont_a_len * sizeof(wchar_t));
104 | if(!cont_a || !cont_w) {
105 | goto end;
106 | }
107 | if(!ReadFile(hFile, cont_a, cont_a_len, &byte_ret, NULL)) {
108 | goto end;
109 | }
110 | cont_w_len = StringToUTF16(cont_w, cont_a, cont_a_len);
111 | }
112 | SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
113 | WriteFile(hFile, BOM_UTF16_LE, sizeof(BOM_UTF16_LE), &byte_ret, NULL);
114 | if(cont_w && cont_w_len) {
115 | WriteFile(hFile, cont_w, cont_w_len * sizeof(wchar_t), &byte_ret, NULL);
116 | }
117 | SetEndOfFile(hFile);
118 | end:
119 | if(cont_a) {
120 | HeapFree(hHeap, 0, cont_a);
121 | }
122 | if(cont_w) {
123 | HeapFree(hHeap, 0, cont_w);
124 | }
125 | CloseHandle(hFile);
126 | return ret;
127 | }
128 | // --------------
129 |
130 | BOOL WINAPI CopyFileU(
131 | LPCSTR lpExistingFileName,
132 | LPCSTR lpNewFileName,
133 | BOOL bFailIfExists
134 | )
135 | {
136 | return CopyFileExU(
137 | lpExistingFileName, lpNewFileName, NULL, NULL, NULL,
138 | bFailIfExists ? COPY_FILE_FAIL_IF_EXISTS : 0
139 | );
140 | }
141 |
142 | BOOL WINAPI CopyFileExU(
143 | LPCSTR lpExistingFileName,
144 | LPCSTR lpNewFileName,
145 | LPPROGRESS_ROUTINE lpProgressRoutine,
146 | LPVOID lpData,
147 | LPBOOL pbCancel,
148 | DWORD dwCopyFlags
149 | )
150 | {
151 | BOOL ret;
152 | WCHAR_T_DEC(lpExistingFileName);
153 | WCHAR_T_DEC(lpNewFileName);
154 | WCHAR_T_CONV(lpExistingFileName);
155 | WCHAR_T_CONV(lpNewFileName);
156 | ret = CopyFileExW(
157 | lpExistingFileName_w, lpNewFileName_w,
158 | lpProgressRoutine, lpData, pbCancel, dwCopyFlags
159 | );
160 | WCHAR_T_FREE(lpExistingFileName);
161 | WCHAR_T_FREE(lpNewFileName);
162 | return ret;
163 | }
164 |
165 | BOOL WINAPI CreateDirectoryU(
166 | LPCSTR lpPathName,
167 | LPSECURITY_ATTRIBUTES lpSecurityAttributes
168 | )
169 | {
170 | // Hey, let's make this recursive while we're at it.
171 | BOOL ret;
172 | size_t i;
173 | size_t lpPathName_w_len;
174 | WCHAR_T_DEC(lpPathName);
175 | WCHAR_T_CONV(lpPathName);
176 |
177 | // no, this isn't optimized away
178 | lpPathName_w_len = wcslen(lpPathName_w);
179 | // If the last character is a \\ or a /, the directory will be created
180 | // by the final CreateDirectory, and we don't want to create it here.
181 | // So we don't check for the last character.
182 | for(i = 0; i < lpPathName_w_len - 1; i++) {
183 | if(lpPathName_w[i] == L'\\' || lpPathName_w[i] == L'/') {
184 | wchar_t old_c = lpPathName_w[i + 1];
185 | lpPathName_w[i + 1] = L'\0';
186 | lpPathName_w[i] = L'/';
187 | ret = CreateDirectoryW(lpPathName_w, lpSecurityAttributes);
188 | lpPathName_w[i + 1] = old_c;
189 | }
190 | }
191 | // Final directory
192 | ret = CreateDirectoryW(lpPathName_w, lpSecurityAttributes);
193 | WCHAR_T_FREE(lpPathName);
194 | return ret;
195 | }
196 |
197 | HANDLE WINAPI CreateFileU(
198 | LPCSTR lpFileName,
199 | DWORD dwDesiredAccess,
200 | DWORD dwShareMode,
201 | LPSECURITY_ATTRIBUTES lpSecurityAttributes,
202 | DWORD dwCreationDisposition,
203 | DWORD dwFlagsAndAttributes,
204 | HANDLE hTemplateFile
205 | )
206 | {
207 | HANDLE ret;
208 | WCHAR_T_DEC(lpFileName);
209 | WCHAR_T_CONV(lpFileName);
210 | ret = CreateFileW(
211 | lpFileName_w, dwDesiredAccess, dwShareMode | FILE_SHARE_READ, lpSecurityAttributes,
212 | dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile
213 | );
214 | WCHAR_T_FREE(lpFileName);
215 | return ret;
216 | }
217 |
218 | HANDLE WINAPI CreateFileMappingU(
219 | HANDLE hFile,
220 | LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
221 | DWORD flProtect,
222 | DWORD dwMaximumSizeHigh,
223 | DWORD dwMaximumSizeLow,
224 | LPCSTR lpName
225 | )
226 | {
227 | size_t name_length = lpName ? strlen(lpName) + 1 : 0;
228 | VLA(wchar_t, name_w, name_length);
229 | if (lpName) {
230 | StringToUTF16(name_w, lpName, name_length);
231 | lpName = (LPCSTR)name_w;
232 | }
233 | HANDLE ret = CreateFileMappingW(
234 | hFile, lpFileMappingAttributes, flProtect,
235 | dwMaximumSizeHigh, dwMaximumSizeLow, (LPCWSTR)lpName
236 | );
237 | VLA_FREE(name_w);
238 | return ret;
239 | }
240 |
241 | BOOL WINAPI CreateProcessU(
242 | LPCSTR lpAppName,
243 | LPSTR lpCmdLine,
244 | LPSECURITY_ATTRIBUTES lpProcessAttributes,
245 | LPSECURITY_ATTRIBUTES lpThreadAttributes,
246 | BOOL bInheritHandles,
247 | DWORD dwCreationFlags,
248 | LPVOID lpEnvironment,
249 | LPCSTR lpCurrentDirectory,
250 | LPSTARTUPINFOA lpSI,
251 | LPPROCESS_INFORMATION lpProcessInformation
252 | )
253 | {
254 | STARTUPINFOW lpSI_w;
255 | // At least the structure sizes are identical here
256 | memcpy(&lpSI_w, lpSI, sizeof(STARTUPINFOW));
257 | lpSI_w.lpReserved = NULL;
258 |
259 | size_t app_name_len = lpAppName ? strlen(lpAppName) + 1 : 0;
260 | size_t cmd_line_len = lpCmdLine ? strlen(lpCmdLine) + 1 : 0;
261 | size_t cur_dir_len = lpCurrentDirectory ? strlen(lpCurrentDirectory) + 1 : 0;
262 | size_t desktop_len = lpSI_w.lpDesktop ? strlen((char*)lpSI_w.lpDesktop) + 1 : 0;
263 | size_t title_len = lpSI_w.lpTitle ? strlen((char*)lpSI_w.lpTitle) + 1 : 0;
264 |
265 | size_t total_len = app_name_len + cmd_line_len + cur_dir_len + desktop_len + title_len;
266 | VLA(wchar_t, param_buffers, total_len);
267 | wchar_t* param_buffer_write = param_buffers;
268 | if (lpAppName) {
269 | size_t written = StringToUTF16(param_buffer_write, lpAppName, app_name_len);
270 | lpAppName = (LPCSTR)param_buffer_write;
271 | param_buffer_write += written;
272 | }
273 | if (lpCmdLine) {
274 | size_t written = StringToUTF16(param_buffer_write, lpCmdLine, cmd_line_len);
275 | lpCmdLine = (LPSTR)param_buffer_write;
276 | param_buffer_write += written;
277 | }
278 | if (lpCurrentDirectory) {
279 | size_t written = StringToUTF16(param_buffer_write, lpCurrentDirectory, cur_dir_len);
280 | lpCurrentDirectory = (LPCSTR)param_buffer_write;
281 | param_buffer_write += written;
282 | }
283 | if (lpSI_w.lpDesktop) {
284 | size_t written = StringToUTF16(param_buffer_write, (char*)lpSI_w.lpDesktop, desktop_len);
285 | lpSI_w.lpDesktop = param_buffer_write;
286 | param_buffer_write += written;
287 | }
288 | if (lpSI_w.lpTitle) {
289 | StringToUTF16(param_buffer_write, (char*)lpSI_w.lpTitle, title_len);
290 | lpSI_w.lpTitle = param_buffer_write;
291 | }
292 |
293 | BOOL ret = CreateProcessW(
294 | (LPCWSTR)lpAppName,
295 | (LPWSTR)lpCmdLine,
296 | lpProcessAttributes,
297 | lpThreadAttributes,
298 | bInheritHandles,
299 | dwCreationFlags,
300 | lpEnvironment,
301 | (LPCWSTR)lpCurrentDirectory,
302 | &lpSI_w,
303 | lpProcessInformation
304 | );
305 |
306 | VLA_FREE(param_buffers);
307 | return ret;
308 | }
309 |
310 | BOOL WINAPI DeleteFileU(
311 | LPCSTR lpFileName
312 | )
313 | {
314 | BOOL ret;
315 | WCHAR_T_DEC(lpFileName);
316 | WCHAR_T_CONV(lpFileName);
317 | ret = DeleteFileW(lpFileName_w);
318 | WCHAR_T_FREE(lpFileName);
319 | return ret;
320 | }
321 |
322 | static void CopyFindDataWToA(
323 | LPWIN32_FIND_DATAA w32fd_a,
324 | LPWIN32_FIND_DATAW w32fd_w
325 | )
326 | {
327 | w32fd_a->dwFileAttributes = w32fd_w->dwFileAttributes;
328 | w32fd_a->ftCreationTime = w32fd_w->ftCreationTime;
329 | w32fd_a->ftLastAccessTime = w32fd_w->ftLastAccessTime;
330 | w32fd_a->ftLastWriteTime = w32fd_w->ftLastWriteTime;
331 | w32fd_a->nFileSizeHigh = w32fd_w->nFileSizeHigh;
332 | w32fd_a->nFileSizeLow = w32fd_w->nFileSizeLow;
333 | w32fd_a->dwReserved0 = w32fd_w->dwReserved0;
334 | w32fd_a->dwReserved1 = w32fd_w->dwReserved1;
335 | StringToUTF8(w32fd_a->cFileName, w32fd_w->cFileName, sizeof(w32fd_a->cFileName));
336 | StringToUTF8(w32fd_a->cAlternateFileName, w32fd_w->cAlternateFileName, sizeof(w32fd_a->cAlternateFileName));
337 | #ifdef _MAC
338 | w32fd_a->dwFileType = w32fd_w->dwReserved1;
339 | w32fd_a->dwCreatorType = w32fd_w->dwCreatorType;
340 | w32fd_a->wFinderFlags = w32fd_w->wFinderFlags;
341 | #endif
342 | }
343 |
344 | HANDLE WINAPI FindFirstFileU(
345 | LPCSTR lpFileName,
346 | LPWIN32_FIND_DATAA lpFindFileData
347 | )
348 | {
349 | HANDLE ret;
350 | DWORD last_error;
351 | WIN32_FIND_DATAW lpFindFileDataW;
352 |
353 | WCHAR_T_DEC(lpFileName);
354 | WCHAR_T_CONV(lpFileName);
355 | ret = FindFirstFileW(lpFileName_w, &lpFindFileDataW);
356 | last_error = GetLastError();
357 | CopyFindDataWToA(lpFindFileData, &lpFindFileDataW);
358 | SetLastError(last_error);
359 | WCHAR_T_FREE(lpFileName);
360 | return ret;
361 | }
362 |
363 | BOOL WINAPI FindNextFileU(
364 | HANDLE hFindFile,
365 | LPWIN32_FIND_DATAA lpFindFileData
366 | )
367 | {
368 | BOOL ret;
369 | DWORD last_error;
370 | WIN32_FIND_DATAW lpFindFileDataW;
371 |
372 | ret = FindNextFileW(hFindFile, &lpFindFileDataW);
373 | last_error = GetLastError();
374 | CopyFindDataWToA(lpFindFileData, &lpFindFileDataW);
375 | SetLastError(last_error);
376 | return ret;
377 | }
378 |
379 | typedef enum {
380 | VOA_VA,
381 | VOA_ARRAY
382 | } va_or_array_t;
383 |
384 | static void** voa_arg(va_list *va, unsigned int n, va_or_array_t type)
385 | {
386 | if(type == VOA_VA) {
387 | va_list vacopy = *va;
388 | unsigned int i;
389 | for(i = 0; i < n; i++) {
390 | va_arg(vacopy, void*);
391 | }
392 | return (void**)vacopy;
393 | }
394 | return (void**)((char*)va + (n * sizeof(void*)));
395 | }
396 |
397 | DWORD WINAPI FormatMessageU(
398 | DWORD dwFlags,
399 | LPCVOID lpSource,
400 | DWORD dwMessageId,
401 | DWORD dwLanguageId,
402 | LPSTR lpBuffer,
403 | DWORD nSize,
404 | va_list *Arguments
405 | )
406 | {
407 | DWORD ret = 0;
408 | LPSTR* lppBuffer = (LPSTR*)lpBuffer;
409 | int allocating = dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER;
410 | size_t lpBuffer_w_len;
411 | wchar_t *lpBuffer_w = NULL;
412 | wchar_t *source_w = NULL;
413 | int i = 0;
414 | int inserts_used = 0;
415 | wchar_t *inserts_w[99] = {0};
416 | va_or_array_t voa = (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
417 | ? VOA_ARRAY
418 | : VOA_VA;
419 |
420 | if(lpSource && dwFlags & FORMAT_MESSAGE_FROM_STRING) {
421 | WCHAR_T_DECA(lpSource);
422 | WCHAR_T_CONV(lpSource);
423 |
424 | if(dwFlags & ~FORMAT_MESSAGE_IGNORE_INSERTS && Arguments) {
425 | const char *p = lpSource;
426 | /** Whenever a width and/or precision format specifications are
427 | * used together with the va_list form, any following insert
428 | * numbers must be decremented in the argument string. To cite
429 | * the example on the MSDN page, a format specification that is
430 | * meant to return " Bi Bob Bill" with the argument list
431 | * [4, 2, "Bill", "Bob", 6, "Bill"] would have to be
432 | *
433 | * "%1!*.*s! %3 %4!*s!"
434 | *
435 | * for a va_list and
436 | *
437 | * "%1!*.*s! %4 %5!*s!"
438 | *
439 | * for a FORMAT_MESSAGE_ARGUMENT_ARRAY. Therefore, we need to add
440 | * that drift value to the insert number ourselves in the former
441 | * case.
442 | */
443 | int insert_drift = 0;
444 | while(*p) {
445 | printf_format_t fmt;
446 | int insert;
447 |
448 | // Skip characters before '%'
449 | for(; *p && *p != '%'; p++);
450 | if(!*p) {
451 | break;
452 | }
453 | // *p == '%' here
454 | p++;
455 |
456 | // Single characters
457 | if(strchr("% .!nrt", *p)) {
458 | p++;
459 | continue;
460 | // Insert number
461 | } else if(*p >= '1' && *p <= '9') {
462 | insert = (*p++) - '0';
463 | if(*p >= '0' && *p <= '9') {
464 | insert = (insert * 10) + (*p++) - '0';
465 | }
466 | insert--;
467 |
468 | // printf format
469 | if(*p == '!') {
470 | p = printf_format_parse(&fmt, p + 1);
471 | if(*(p++) != '!') {
472 | // Something has to be wrong with the input string
473 | SetLastError(ERROR_INVALID_PARAMETER);
474 | goto end;
475 | }
476 | } else {
477 | fmt.argc_before_type = 0;
478 | fmt.type_size_in_ints = 1;
479 | fmt.type = 's';
480 | }
481 | insert += fmt.argc_before_type + insert_drift;
482 | if((fmt.type == 's' || fmt.type == 'S') && inserts_w[insert] == NULL) {
483 | void **argptr = voa_arg(Arguments, insert, voa);
484 | const char *src = *argptr;
485 | WCHAR_T_DECA(src);
486 | WCHAR_T_CONV(src);
487 | inserts_w[insert] = src_w;
488 | inserts_used = max(insert + 1, inserts_used);
489 | *argptr = inserts_w[insert];
490 | }
491 | if(fmt.argc_before_type && voa == VOA_VA) {
492 | insert_drift++;
493 | }
494 | }
495 | }
496 | }
497 | source_w = lpSource_w;
498 | lpSource = lpSource_w;
499 | }
500 |
501 | ret = FormatMessageW(
502 | (nSize ? FORMAT_MESSAGE_ALLOCATE_BUFFER : 0) | dwFlags,
503 | lpSource, dwMessageId, dwLanguageId,
504 | (!lpBuffer && allocating) ? NULL : (LPWSTR)&lpBuffer_w,
505 | nSize, Arguments
506 | );
507 | if(!ret) {
508 | if(allocating && lpBuffer) {
509 | *lppBuffer = NULL;
510 | }
511 | goto end;
512 | } else if(allocating) {
513 | nSize = max(ret * sizeof(char) * UTF8_MUL, nSize);
514 | *lppBuffer = LocalAlloc(0, nSize);
515 | lpBuffer = *lppBuffer;
516 | }
517 | // Apparently, we're only supposed to either put all or nothing into
518 | // the output buffer.
519 | lpBuffer_w_len = wcslen(lpBuffer_w) + 1;
520 | ret = WideCharToMultiByte(CP_UTF8, 0,
521 | lpBuffer_w, lpBuffer_w_len, NULL, 0, NULL, NULL
522 | );
523 | if(ret <= nSize) {
524 | WideCharToMultiByte(CP_UTF8, 0,
525 | lpBuffer_w, lpBuffer_w_len, lpBuffer, nSize, NULL, NULL
526 | );
527 | ret--; // excluding the terminating NULL character...
528 | } else {
529 | SetLastError(ERROR_INSUFFICIENT_BUFFER);
530 | ret = 0;
531 | }
532 | end:
533 | for(i = 0; i < inserts_used; i++) {
534 | w32u8_freea(inserts_w[i]);
535 | }
536 | w32u8_freea(source_w);
537 | LocalFree(lpBuffer_w);
538 | return ret;
539 | }
540 |
541 | LPSTR WINAPI GetCommandLineU(
542 | VOID
543 | )
544 | {
545 | static char *command_line = NULL;
546 | if(!command_line) {
547 | const wchar_t *command_line_w = GetCommandLineW();
548 | WCSLEN_DEC(command_line_w);
549 | command_line = (char*)malloc(command_line_w_len);
550 | StringToUTF8(command_line, command_line_w, command_line_w_len);
551 | }
552 | return command_line;
553 | }
554 |
555 | DWORD WINAPI GetCurrentDirectoryU(
556 | DWORD nBufferLength,
557 | LPSTR lpBuffer
558 | )
559 | {
560 | return WrapGetString(GetCurrentDirectoryW, nBufferLength, lpBuffer);
561 | }
562 |
563 | DWORD WINAPI GetEnvironmentVariableU(
564 | LPCSTR lpName,
565 | LPSTR lpBuffer,
566 | DWORD nSize
567 | )
568 | {
569 | DWORD ret;
570 | WCHAR_T_DEC(lpName);
571 | VLA(wchar_t, lpBuffer_w, nSize);
572 | WCHAR_T_CONV(lpName);
573 |
574 | GetEnvironmentVariableW(lpName_w, lpBuffer_w, nSize);
575 | // Return the converted size (!)
576 | ret = StringToUTF8(lpBuffer, lpBuffer_w, nSize);
577 | VLA_FREE(lpBuffer_w);
578 | WCHAR_T_FREE(lpName);
579 | return ret;
580 | }
581 |
582 | DWORD WINAPI GetFileAttributesU(
583 | LPCSTR lpFileName
584 | )
585 | {
586 | DWORD ret;
587 | WCHAR_T_DEC(lpFileName);
588 | WCHAR_T_CONV(lpFileName);
589 | ret = GetFileAttributesW(lpFileName_w);
590 | WCHAR_T_FREE(lpFileName);
591 | return ret;
592 | }
593 |
594 | BOOL WINAPI GetFileAttributesExU(
595 | LPCSTR lpFileName,
596 | GET_FILEEX_INFO_LEVELS fInfoLevelId,
597 | LPVOID lpFileInformation
598 | )
599 | {
600 | BOOL ret;
601 | WCHAR_T_DEC(lpFileName);
602 | WCHAR_T_CONV(lpFileName);
603 | ret = GetFileAttributesExW(lpFileName_w, fInfoLevelId, lpFileInformation);
604 | WCHAR_T_FREE(lpFileName);
605 | return ret;
606 | }
607 |
608 | DWORD WINAPI GetFullPathNameU(
609 | LPCSTR lpFileName,
610 | DWORD nBufferLength,
611 | LPSTR lpBuffer,
612 | LPSTR *lpFilePart
613 | )
614 | {
615 | LPWSTR lpwFilePart;
616 | DWORD ret;
617 | VLA(wchar_t, lpBuffer_w, nBufferLength);
618 | WCHAR_T_DEC(lpFileName);
619 | WCHAR_T_CONV(lpFileName);
620 |
621 | if (lpFilePart) {
622 | *lpFilePart = NULL;
623 | }
624 | if (!lpBuffer) {
625 | VLA_FREE(lpBuffer_w);
626 | }
627 | ret = GetFullPathNameW(lpFileName_w, nBufferLength, lpBuffer_w, &lpwFilePart);
628 | if (lpBuffer) {
629 | StringToUTF8(lpBuffer, lpBuffer_w, nBufferLength);
630 | if (lpFilePart && lpwFilePart) {
631 | *lpFilePart = lpBuffer + strlen(lpBuffer) - 1;
632 | while (*lpFilePart >= lpBuffer && **lpFilePart != '\\' && **lpFilePart != '/') {
633 | (*lpFilePart)--;
634 | }
635 | (*lpFilePart)++;
636 | }
637 | }
638 | else {
639 | // Hey, let's be nice and return the _actual_ length.
640 | VLA(wchar_t, lpBufferReal_w, ret);
641 | GetFullPathNameW(lpFileName_w, ret, lpBufferReal_w, NULL);
642 | ret = StringToUTF8(NULL, lpBufferReal_w, 0) + 1;
643 | VLA_FREE(lpBufferReal_w);
644 | }
645 | WCHAR_T_FREE(lpFileName);
646 | VLA_FREE(lpBuffer_w);
647 | return ret;
648 | }
649 |
650 | DWORD WINAPI GetModuleFileNameU(
651 | HMODULE hModule,
652 | LPSTR lpFilename,
653 | DWORD nSize
654 | )
655 | {
656 | /**
657 | * And here we are, the most stupid Win32 API function I've seen so far.
658 | *
659 | * This wrapper adds the "GetCurrentDirectory functionality" the original
660 | * function unfortunately lacks. Pass NULL for [lpFilename] or [nSize] to
661 | * get the size required for a buffer to hold the module name in UTF-8.
662 | *
663 | * ... and unless there is any alternative function I don't know of, the
664 | * only way to actually calculate this size is to repeatedly increase a
665 | * buffer and to check whether that has been enough.
666 | *
667 | * In practice though, this length should never exceed MAX_PATH. I failed to
668 | * create any test case where the path would be larger. But just in case it
669 | * is or this becomes more frequent some day, the code is here.
670 | */
671 |
672 | // Only real VLAs can safely change size in a loop. Doing this with _malloca
673 | // or _alloca just continually expands the stack and never deallocates.
674 |
675 | DWORD wide_len = nSize ? nSize : MAX_PATH;
676 | #if !VLA_SUPPORT
677 | wchar_t* lpFilename_w = (wchar_t*)malloc(wide_len * sizeof(wchar_t));
678 | #endif
679 | for (;;) {
680 | #if VLA_SUPPORT
681 | wchar_t lpFilename_w[wide_len];
682 | #endif
683 |
684 | DWORD ret = GetModuleFileNameW(hModule, lpFilename_w, wide_len);
685 | if (ret) {
686 | if (ret == wide_len) {
687 | wide_len += MAX_PATH;
688 | #if !VLA_SUPPORT
689 | lpFilename_w = (wchar_t*)realloc(lpFilename_w, wide_len * sizeof(wchar_t));
690 | #endif
691 | continue;
692 | }
693 | // The last error and return value will be set correctly
694 | // by the WideCharToMultiByte call inside StringToUTF8
695 | ret = StringToUTF8(lpFilename, lpFilename_w, nSize);
696 | #if !VLA_SUPPORT
697 | free(lpFilename_w);
698 | #endif
699 | }
700 | return ret;
701 | }
702 | }
703 |
704 | BOOL WINAPI GetModuleHandleExU(
705 | DWORD dwFlags,
706 | LPCSTR lpFilename,
707 | HMODULE* hModule
708 | )
709 | {
710 | BOOL ret;
711 | // When GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS is set, [lpFilename] is an arbitrary address and not a string.
712 | if (!(dwFlags & GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS) && lpFilename != NULL) {
713 | WCHAR_T_DEC(lpFilename);
714 | WCHAR_T_CONV(lpFilename);
715 | ret = GetModuleHandleExW(dwFlags, lpFilename_w, hModule);
716 | WCHAR_T_FREE(lpFilename);
717 | } else {
718 | ret = GetModuleHandleExW(dwFlags, (void*)lpFilename, hModule);
719 | }
720 | return ret;
721 | }
722 |
723 | #define INI_MACRO_EXPAND(macro) \
724 | macro(lpAppName); \
725 | macro(lpKeyName); \
726 | macro(lpFileName)
727 |
728 | UINT WINAPI GetPrivateProfileIntU(
729 | LPCSTR lpAppName,
730 | LPCSTR lpKeyName,
731 | INT nDefault,
732 | LPCSTR lpFileName
733 | )
734 | {
735 | UINT ret;
736 | INI_MACRO_EXPAND(WCHAR_T_DEC);
737 | INI_MACRO_EXPAND(WCHAR_T_CONV);
738 | ret = GetPrivateProfileIntW(lpAppName_w, lpKeyName_w, nDefault, lpFileName_w);
739 | INI_MACRO_EXPAND(WCHAR_T_FREE);
740 | return ret;
741 | }
742 |
743 | UINT WINAPI GetPrivateProfileStringU(
744 | LPCSTR lpAppName,
745 | LPCSTR lpKeyName,
746 | LPCSTR lpDefault,
747 | LPSTR lpReturnedString,
748 | DWORD nSize,
749 | LPCSTR lpFileName
750 | )
751 | {
752 | size_t app_name_len = lpAppName ? strlen(lpAppName) + 1 : 0;
753 | size_t key_name_len = lpKeyName ? strlen(lpKeyName) + 1 : 0;
754 | size_t default_len = lpDefault ? strlen(lpDefault) + 1 : 0;
755 | size_t file_name_len = strlen(lpFileName) + 1;
756 |
757 | size_t total_len = app_name_len + key_name_len + default_len + file_name_len + nSize;
758 | VLA(wchar_t, param_buffers, total_len);
759 | wchar_t* param_buffer_write = param_buffers;
760 |
761 | if (lpAppName) {
762 | size_t written = StringToUTF16(param_buffer_write, lpAppName, app_name_len);
763 | lpAppName = (LPCSTR)param_buffer_write;
764 | param_buffer_write += written;
765 | }
766 | if (lpKeyName) {
767 | size_t written = StringToUTF16(param_buffer_write, lpKeyName, key_name_len);
768 | lpKeyName = (LPCSTR)param_buffer_write;
769 | param_buffer_write += written;
770 | }
771 |
772 | // Yes, we can't just ignore it, pass NULL for [lpDefault] to the function
773 | // and memcpy() it ourselves if necessary. If [lpDefault] is NULL,
774 | // GetPrivateProfileString() just uses the empty string instead, and
775 | // there's no way of telling when it *did* use the default string.
776 | if (lpDefault) {
777 | size_t written = StringToUTF16(param_buffer_write, lpDefault, default_len);
778 | lpDefault = (LPCSTR)param_buffer_write;
779 | param_buffer_write += written;
780 | }
781 |
782 | size_t written = StringToUTF16(param_buffer_write, lpFileName, file_name_len);
783 | lpFileName = (LPCSTR)param_buffer_write;
784 | param_buffer_write += written;
785 |
786 | wchar_t* lpReturnedString_w = param_buffer_write;
787 |
788 | // Windows crashes in this case as well. Just like GetModuleFileName(),
789 | // this function can't retrieve the full length of the string anyway,
790 | // since it always null-terminates any truncated version of it. That only
791 | // leaves repeated checks with growing buffers... or a full-on custom
792 | // implementation of the functionality.
793 | assert(lpReturnedString);
794 |
795 | EnsurePrivateProfileUTF16((LPCWSTR)lpFileName);
796 | DWORD ret_w = GetPrivateProfileStringW(
797 | (LPCWSTR)lpAppName, (LPCWSTR)lpKeyName, (LPCWSTR)lpDefault,
798 | lpReturnedString_w, nSize, (LPCWSTR)lpFileName
799 | );
800 |
801 | UINT ret = 0;
802 |
803 | if(nSize) {
804 | ret = StringToMBFixed(
805 | lpReturnedString, lpReturnedString_w, nSize - 1, ret_w
806 | );
807 | lpReturnedString[ret] = 0;
808 | if((!lpAppName || !lpKeyName) && (ret + 1) == (nSize - 1)) {
809 | lpReturnedString[ret + 1] = 0;
810 | }
811 | }
812 |
813 | VLA_FREE(param_buffers);
814 | return ret;
815 | }
816 |
817 | VOID WINAPI GetStartupInfoU(
818 | LPSTARTUPINFOA lpSI
819 | )
820 | {
821 | STARTUPINFOW si_w;
822 | GetStartupInfoW(&si_w);
823 |
824 | // I would have put this code into kernel32_init, but apparently
825 | // GetStartupInfoW is "not safe to be called inside DllMain".
826 | // So unsafe in fact that Wine segfaults when I tried it
827 | if(!startupinfo_desktop) {
828 | size_t lpDesktop_len = wcslen(si_w.lpDesktop) + 1;
829 | startupinfo_desktop = (char*)malloc(lpDesktop_len * UTF8_MUL * sizeof(char));
830 | StringToUTF8(startupinfo_desktop, si_w.lpDesktop, lpDesktop_len);
831 | }
832 | if(!startupinfo_title) {
833 | size_t lpTitle_len = wcslen(si_w.lpTitle) + 1;
834 | startupinfo_title = (char*)malloc(lpTitle_len * UTF8_MUL * sizeof(char));
835 | StringToUTF8(startupinfo_title, si_w.lpTitle, lpTitle_len);
836 | }
837 | memcpy(lpSI, &si_w, sizeof(STARTUPINFOA));
838 | lpSI->lpDesktop = startupinfo_desktop;
839 | lpSI->lpTitle = startupinfo_title;
840 | }
841 |
842 | UINT WINAPI GetTempFileNameU(
843 | LPCSTR lpPathName,
844 | LPCSTR lpPrefixString,
845 | UINT uUnique,
846 | LPSTR lpTempFileName
847 | )
848 | {
849 | UINT ret;
850 | WCHAR_T_DEC(lpPathName);
851 | WCHAR_T_DEC(lpPrefixString);
852 | VLA(wchar_t, lpFilename_w, MAX_PATH);
853 |
854 | WCHAR_T_CONV(lpPathName);
855 | WCHAR_T_CONV(lpPrefixString);
856 | ret = GetTempFileNameW(lpPathName_w, lpPrefixString_w, uUnique, lpFilename_w);
857 | if(ret) {
858 | StringToUTF8(lpTempFileName, lpFilename_w, MAX_PATH);
859 | if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
860 | ret = 0;
861 | }
862 | }
863 | VLA_FREE(lpFilename_w);
864 | WCHAR_T_FREE(lpPathName);
865 | WCHAR_T_FREE(lpPrefixString);
866 | return ret;
867 | }
868 |
869 | DWORD WINAPI GetTempPathU(
870 | DWORD nBufferLength,
871 | LPSTR lpBuffer
872 | )
873 | {
874 | return WrapGetString(GetTempPathW, nBufferLength, lpBuffer);
875 | }
876 |
877 | BOOL WINAPI IsDBCSLeadByteFB(
878 | BYTE TestChar
879 | )
880 | {
881 | extern UINT fallback_codepage;
882 | return IsDBCSLeadByteEx(fallback_codepage, TestChar);
883 | }
884 |
885 | HMODULE WINAPI LoadLibraryU(
886 | LPCSTR lpLibFileName
887 | )
888 | {
889 | return LoadLibraryExU(lpLibFileName, NULL, 0);
890 | }
891 |
892 | HMODULE WINAPI LoadLibraryExU(
893 | LPCSTR lpLibFileName,
894 | HANDLE hFile,
895 | DWORD dwFlags
896 | )
897 | {
898 | static int have_kb2533623 = -1;
899 | HMODULE ret;
900 | WCHAR_T_DEC(lpLibFileName);
901 | WCHAR_T_CONV(lpLibFileName);
902 |
903 | // Remove the flags that aren't supported without KB2533623.
904 | if(have_kb2533623 == -1) {
905 | have_kb2533623 = GetProcAddress(
906 | GetModuleHandleA("kernel32.dll"), "SetDefaultDllDirectories"
907 | ) != 0;
908 | }
909 | if(!have_kb2533623) {
910 | dwFlags &= ~(
911 | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR |
912 | LOAD_LIBRARY_SEARCH_APPLICATION_DIR |
913 | LOAD_LIBRARY_SEARCH_USER_DIRS |
914 | LOAD_LIBRARY_SEARCH_SYSTEM32 |
915 | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS
916 | );
917 | }
918 |
919 | ret = LoadLibraryExW(lpLibFileName_w, hFile, dwFlags);
920 | WCHAR_T_FREE(lpLibFileName);
921 | return ret;
922 | }
923 |
924 | BOOL WINAPI MoveFileU(
925 | LPCSTR lpExistingFileName,
926 | LPCSTR lpNewFileName
927 | )
928 | {
929 | return MoveFileEx(lpExistingFileName, lpNewFileName, MOVEFILE_COPY_ALLOWED);
930 | }
931 |
932 | BOOL WINAPI MoveFileExU(
933 | LPCSTR lpExistingFileName,
934 | LPCSTR lpNewFileName,
935 | DWORD dwFlags
936 | )
937 | {
938 | return MoveFileWithProgress(
939 | lpExistingFileName, lpNewFileName, NULL, NULL, dwFlags
940 | );
941 | }
942 |
943 | BOOL WINAPI MoveFileWithProgressU(
944 | LPCSTR lpExistingFileName,
945 | LPCSTR lpNewFileName,
946 | LPPROGRESS_ROUTINE lpProgressRoutine,
947 | LPVOID lpData,
948 | DWORD dwFlags
949 | )
950 | {
951 | BOOL ret;
952 | WCHAR_T_DEC(lpExistingFileName);
953 | WCHAR_T_DEC(lpNewFileName);
954 | WCHAR_T_CONV(lpExistingFileName);
955 | WCHAR_T_CONV(lpNewFileName);
956 | ret = MoveFileWithProgressW(
957 | lpExistingFileName_w, lpNewFileName_w, lpProgressRoutine, lpData, dwFlags
958 | );
959 | WCHAR_T_FREE(lpExistingFileName);
960 | WCHAR_T_FREE(lpNewFileName);
961 | return ret;
962 | }
963 |
964 | int WINAPI MultiByteToWideCharU(
965 | UINT CodePage,
966 | DWORD dwFlags,
967 | LPCSTR lpMultiByteStr,
968 | int cbMultiByte,
969 | LPWSTR lpWideCharStr,
970 | int cchWideChar
971 | )
972 | {
973 | (void)CodePage;
974 | (void)dwFlags;
975 |
976 | int ret = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
977 | lpMultiByteStr, cbMultiByte, lpWideCharStr, cchWideChar
978 | );
979 | if(!ret && GetLastError() == ERROR_NO_UNICODE_TRANSLATION) {
980 | extern UINT fallback_codepage;
981 | if(lpMultiByteStr[cbMultiByte - 1] != 0) {
982 | // The previous conversion attempt still lingers in [lpMultiByteStr].
983 | // If we don't clear it, garbage may show up at the end of the
984 | // converted string if the original string wasn't null-terminated...
985 | ZeroMemory(lpWideCharStr, cchWideChar * sizeof(wchar_t));
986 | }
987 | ret = MultiByteToWideChar(fallback_codepage, MB_PRECOMPOSED,
988 | lpMultiByteStr, cbMultiByte, lpWideCharStr, cchWideChar
989 | );
990 | }
991 | return ret;
992 | }
993 |
994 | HANDLE WINAPI OpenFileMappingU(
995 | DWORD dwDesiredAccess,
996 | BOOL bInheritHandle,
997 | LPCSTR lpName
998 | )
999 | {
1000 | HANDLE ret;
1001 | WCHAR_T_DEC(lpName);
1002 | WCHAR_T_CONV(lpName);
1003 | ret = OpenFileMappingW(dwDesiredAccess, bInheritHandle, lpName_w);
1004 | WCHAR_T_FREE(lpName);
1005 | return ret;
1006 | }
1007 |
1008 | BOOL WINAPI RemoveDirectoryU(
1009 | LPCSTR lpPathName
1010 | )
1011 | {
1012 | BOOL ret;
1013 | WCHAR_T_DEC(lpPathName);
1014 | WCHAR_T_CONV(lpPathName);
1015 | ret = RemoveDirectoryW(lpPathName_w);
1016 | WCHAR_T_FREE(lpPathName);
1017 | return ret;
1018 | }
1019 |
1020 | BOOL WINAPI SetCurrentDirectoryU(
1021 | LPCSTR lpPathName
1022 | )
1023 | {
1024 | BOOL ret;
1025 | WCHAR_T_DEC(lpPathName);
1026 | WCHAR_T_CONV(lpPathName);
1027 | ret = SetCurrentDirectoryW(lpPathName_w);
1028 | WCHAR_T_FREE(lpPathName);
1029 | return ret;
1030 | }
1031 |
1032 | BOOL WINAPI SetEnvironmentVariableU(
1033 | LPCSTR lpName,
1034 | LPCSTR lpValue
1035 | )
1036 | {
1037 | BOOL ret;
1038 | WCHAR_T_DEC(lpName);
1039 | WCHAR_T_DEC(lpValue);
1040 | WCHAR_T_CONV(lpName);
1041 | WCHAR_T_CONV(lpValue);
1042 | ret = (BOOL)SetEnvironmentVariableW(lpName_w, lpValue_w);
1043 | WCHAR_T_FREE(lpName);
1044 | WCHAR_T_FREE(lpValue);
1045 | return ret;
1046 | }
1047 |
1048 | int WINAPI WideCharToMultiByteU(
1049 | UINT CodePage,
1050 | DWORD dwFlags,
1051 | LPCWSTR lpWideCharStr,
1052 | int cchWideChar,
1053 | LPSTR lpMultiByteStr,
1054 | int cbMultiByte,
1055 | LPCSTR lpDefaultChar,
1056 | LPBOOL lpUsedDefaultChar
1057 | )
1058 | {
1059 | (void)CodePage;
1060 | (void)dwFlags;
1061 | (void)lpDefaultChar;
1062 | (void)lpUsedDefaultChar;
1063 |
1064 | return WideCharToMultiByte(
1065 | CP_UTF8, 0, lpWideCharStr, cchWideChar,
1066 | lpMultiByteStr, cbMultiByte, NULL, NULL
1067 | );
1068 | }
1069 |
1070 | BOOL WINAPI WritePrivateProfileStringU(
1071 | LPCSTR lpAppName,
1072 | LPCSTR lpKeyName,
1073 | LPCSTR lpString,
1074 | LPCSTR lpFileName
1075 | )
1076 | {
1077 | BOOL ret;
1078 | INI_MACRO_EXPAND(WCHAR_T_DEC);
1079 | WCHAR_T_DEC(lpString);
1080 | INI_MACRO_EXPAND(WCHAR_T_CONV);
1081 | WCHAR_T_CONV(lpString);
1082 | EnsurePrivateProfileUTF16(lpFileName_w);
1083 | ret = WritePrivateProfileStringW(
1084 | lpAppName_w, lpKeyName_w, lpString_w, lpFileName_w
1085 | );
1086 | INI_MACRO_EXPAND(WCHAR_T_FREE);
1087 | WCHAR_T_CONV(lpString);
1088 | return ret;
1089 | }
1090 |
1091 | BOOL WINAPI ReadFileU(
1092 | HANDLE hFile,
1093 | LPVOID lpBuffer,
1094 | DWORD nNumberOfBytesToRead,
1095 | LPDWORD lpNumberOfBytesRead,
1096 | LPOVERLAPPED lpOverlapped
1097 | )
1098 | {
1099 | DWORD temp;
1100 | if (!lpNumberOfBytesRead && !lpOverlapped) {
1101 | lpNumberOfBytesRead = &temp;
1102 | }
1103 | return ReadFile(hFile, lpBuffer, nNumberOfBytesToRead,
1104 | lpNumberOfBytesRead, lpOverlapped);
1105 | }
1106 |
1107 | BOOL WINAPI WriteFileU(
1108 | HANDLE hFile,
1109 | LPCVOID lpBuffer,
1110 | DWORD nNumberOfBytesToWrite,
1111 | LPDWORD lpNumberOfBytesWritten,
1112 | LPOVERLAPPED lpOverlapped
1113 | )
1114 | {
1115 | DWORD temp;
1116 | if (!lpNumberOfBytesWritten && !lpOverlapped) {
1117 | lpNumberOfBytesWritten = &temp;
1118 | }
1119 | return WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite,
1120 | lpNumberOfBytesWritten, lpOverlapped);
1121 | }
1122 |
1123 | // Cleanup
1124 | void kernel32_exit(void)
1125 | {
1126 | SAFE_FREE(startupinfo_desktop);
1127 | SAFE_FREE(startupinfo_title);
1128 | }
1129 |
--------------------------------------------------------------------------------
/src/kernel32_dll.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * kernel32.dll functions.
7 | */
8 |
9 | #pragma once
10 |
11 | WRAPPER_DEC(BOOL WINAPI, CopyFile,
12 | LPCSTR lpExistingFileName,
13 | LPCSTR lpNewFileName,
14 | BOOL bFailIfExists
15 | );
16 | #undef CopyFile
17 | #define CopyFile CopyFileU
18 |
19 | WRAPPER_DEC(BOOL WINAPI, CopyFileEx,
20 | LPCSTR lpExistingFileName,
21 | LPCSTR lpNewFileName,
22 | LPPROGRESS_ROUTINE lpProgressRoutine,
23 | LPVOID lpData,
24 | LPBOOL pbCancel,
25 | DWORD dwCopyFlags
26 | );
27 | #undef CopyFileEx
28 | #define CopyFileEx CopyFileExU
29 |
30 | WRAPPER_DEC(BOOL WINAPI, CreateDirectory,
31 | LPCSTR lpPathName,
32 | LPSECURITY_ATTRIBUTES lpSecurityAttributes
33 | );
34 | #undef CreateDirectory
35 | #define CreateDirectory CreateDirectoryU
36 |
37 | WRAPPER_DEC(HANDLE WINAPI, CreateFile,
38 | LPCSTR lpFileName,
39 | DWORD dwDesiredAccess,
40 | DWORD dwShareMode,
41 | LPSECURITY_ATTRIBUTES lpSecurityAttributes,
42 | DWORD dwCreationDisposition,
43 | DWORD dwFlagsAndAttributes,
44 | HANDLE hTemplateFile
45 | );
46 | #undef CreateFile
47 | #define CreateFile CreateFileU
48 |
49 | WRAPPER_DEC(HANDLE WINAPI, CreateFileMapping,
50 | HANDLE hFile,
51 | LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
52 | DWORD flProtect,
53 | DWORD dwMaximumSizeHigh,
54 | DWORD dwMaximumSizeLow,
55 | LPCSTR lpName
56 | );
57 | #undef CreateFileMapping
58 | #define CreateFileMapping CreateFileMappingU
59 |
60 | WRAPPER_DEC(BOOL WINAPI, CreateProcess,
61 | LPCSTR lpApplicationName,
62 | LPSTR lpCommandLine,
63 | PSECURITY_ATTRIBUTES lpProcessAttributes,
64 | LPSECURITY_ATTRIBUTES lpThreadAttributes,
65 | BOOL bInheritHandles,
66 | DWORD dwCreationFlags,
67 | LPVOID lpEnvironment,
68 | LPCSTR lpCurrentDirectory,
69 | LPSTARTUPINFOA lpStartupInfo,
70 | LPPROCESS_INFORMATION lpProcessInformation
71 | );
72 | #undef CreateProcess
73 | #define CreateProcess CreateProcessU
74 |
75 | WRAPPER_DEC(BOOL WINAPI, DeleteFile,
76 | LPCSTR lpFileName
77 | );
78 | #undef DeleteFile
79 | #define DeleteFile DeleteFileU
80 |
81 | WRAPPER_DEC(HANDLE WINAPI, FindFirstFile,
82 | LPCSTR lpFileName,
83 | LPWIN32_FIND_DATAA lpFindFileData
84 | );
85 | #undef FindFirstFile
86 | #define FindFirstFile FindFirstFileU
87 |
88 | WRAPPER_DEC(BOOL WINAPI, FindNextFile,
89 | HANDLE hFindFile,
90 | LPWIN32_FIND_DATAA lpFindFileData
91 | );
92 | #undef FindNextFile
93 | #define FindNextFile FindNextFileU
94 |
95 | WRAPPER_DEC(DWORD WINAPI, FormatMessage,
96 | DWORD dwFlags,
97 | LPCVOID lpSource,
98 | DWORD dwMessageId,
99 | DWORD dwLanguageId,
100 | LPSTR lpBuffer,
101 | DWORD nSize,
102 | va_list *Arguments
103 | );
104 | #undef FormatMessage
105 | #define FormatMessage FormatMessageU
106 |
107 | WRAPPER_DEC(LPSTR WINAPI, GetCommandLine,
108 | VOID
109 | );
110 | #undef GetCommandLine
111 | #define GetCommandLine GetCommandLineU
112 |
113 | WRAPPER_DEC(DWORD WINAPI, GetCurrentDirectory,
114 | DWORD nBufferLength,
115 | LPSTR lpBuffer
116 | );
117 | #undef GetCurrentDirectory
118 | #define GetCurrentDirectory GetCurrentDirectoryU
119 |
120 | WRAPPER_DEC(DWORD WINAPI, GetEnvironmentVariable,
121 | LPCSTR lpName,
122 | LPSTR lpBuffer,
123 | DWORD nSize
124 | );
125 | #undef GetEnvironmentVariable
126 | #define GetEnvironmentVariable GetEnvironmentVariableU
127 |
128 | WRAPPER_DEC(DWORD WINAPI, GetFileAttributes,
129 | LPCSTR lpFileName
130 | );
131 | #undef GetFileAttributes
132 | #define GetFileAttributes GetFileAttributesU
133 |
134 | WRAPPER_DEC(BOOL WINAPI, GetFileAttributesEx,
135 | LPCSTR lpFileName,
136 | GET_FILEEX_INFO_LEVELS fInfoLevelId,
137 | LPVOID lpFileInformation
138 | );
139 | #undef GetFileAttributesEx
140 | #define GetFileAttributesEx GetFileAttributesExU
141 |
142 | WRAPPER_DEC(DWORD WINAPI, GetFullPathName,
143 | LPCSTR lpFileName,
144 | DWORD nBufferLength,
145 | LPSTR lpBuffer,
146 | LPSTR *lpFilePart
147 | );
148 | #undef GetFullPathName
149 | #define GetFullPathName GetFullPathNameU
150 |
151 | WRAPPER_DEC(DWORD WINAPI, GetModuleFileName,
152 | HMODULE hModule,
153 | LPSTR lpFilename,
154 | DWORD nSize
155 | );
156 | #undef GetModuleFileName
157 | #define GetModuleFileName GetModuleFileNameU
158 |
159 | WRAPPER_DEC(BOOL WINAPI, GetModuleHandleEx,
160 | DWORD dwFlags,
161 | LPCSTR lpFilename,
162 | HMODULE* hModule
163 | );
164 | #undef GetModuleHandleEx
165 | #define GetModuleHandleEx GetModuleHandleExU
166 |
167 | WRAPPER_DEC(UINT WINAPI, GetPrivateProfileInt,
168 | LPCSTR lpAppName,
169 | LPCSTR lpKeyName,
170 | INT nDefault,
171 | LPCSTR lpFileName
172 | );
173 | #undef GetPrivateProfileInt
174 | #define GetPrivateProfileInt GetPrivateProfileIntU
175 |
176 | WRAPPER_DEC(UINT WINAPI, GetPrivateProfileString,
177 | LPCSTR lpAppName,
178 | LPCSTR lpKeyName,
179 | LPCSTR lpDefault,
180 | LPSTR lpReturnedString,
181 | DWORD nSize,
182 | LPCSTR lpFileName
183 | );
184 | #undef GetPrivateProfileString
185 | #define GetPrivateProfileString GetPrivateProfileStringU
186 |
187 | WRAPPER_DEC(VOID WINAPI, GetStartupInfo,
188 | LPSTARTUPINFOA lpStartupInfo
189 | );
190 | #undef GetStartupInfo
191 | #define GetStartupInfo GetStartupInfoU
192 |
193 | WRAPPER_DEC(UINT WINAPI, GetTempFileName,
194 | LPCSTR lpPathName,
195 | LPCSTR lpPrefixString,
196 | UINT uUnique,
197 | LPSTR lpTempFileName
198 | );
199 | #undef GetTempFileName
200 | #define GetTempFileName GetTempFileNameU
201 |
202 | WRAPPER_DEC(DWORD WINAPI, GetTempPath,
203 | DWORD nBufferLength,
204 | LPSTR lpBuffer
205 | );
206 | #undef GetTempPath
207 | #define GetTempPath GetTempPathU
208 |
209 | // Only implemented using the fallback codepage, since UTF-8 has
210 | // no way to differentiate between continuation bytes and end bytes.
211 | BOOL WINAPI IsDBCSLeadByteFB(
212 | BYTE TestChar
213 | );
214 | #undef IsDBCSLeadByte
215 | #define IsDBCSLeadByte IsDBCSLeadByteFB
216 |
217 | WRAPPER_DEC(HMODULE WINAPI, LoadLibrary,
218 | LPCSTR lpLibFileName
219 | );
220 | #undef LoadLibrary
221 | #define LoadLibrary LoadLibraryU
222 |
223 | // These will not be defined on any Visual Studio toolset that targets Windows
224 | // XP; after all, if KB2533623 is not installed, LoadLibraryEx() with any of
225 | // the search path flags would return NULL with GetLastError() == 87
226 | // (ERROR_INVALID_PARAMETER). Therefore, LoadLibraryExU() clears them out
227 | // automatically if KB2533623 isn't installed.
228 | #undef LOAD_WITH_ALTERED_SEARCH_PATH
229 | #define LOAD_WITH_ALTERED_SEARCH_PATH 0x00000008
230 | #undef LOAD_IGNORE_CODE_AUTHZ_LEVEL
231 | #define LOAD_IGNORE_CODE_AUTHZ_LEVEL 0x00000010
232 | #undef LOAD_LIBRARY_AS_IMAGE_RESOURCE
233 | #define LOAD_LIBRARY_AS_IMAGE_RESOURCE 0x00000020
234 | #undef LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
235 | #define LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE 0x00000040
236 | #undef LOAD_LIBRARY_REQUIRE_SIGNED_TARGET
237 | #define LOAD_LIBRARY_REQUIRE_SIGNED_TARGET 0x00000080
238 | #undef LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR
239 | #define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR 0x00000100
240 | #undef LOAD_LIBRARY_SEARCH_APPLICATION_DIR
241 | #define LOAD_LIBRARY_SEARCH_APPLICATION_DIR 0x00000200
242 | #undef LOAD_LIBRARY_SEARCH_USER_DIRS
243 | #define LOAD_LIBRARY_SEARCH_USER_DIRS 0x00000400
244 | #undef LOAD_LIBRARY_SEARCH_SYSTEM32
245 | #define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800
246 | #undef LOAD_LIBRARY_SEARCH_DEFAULT_DIRS
247 | #define LOAD_LIBRARY_SEARCH_DEFAULT_DIRS 0x00001000
248 |
249 | WRAPPER_DEC(HMODULE WINAPI, LoadLibraryEx,
250 | LPCSTR lpLibFileName,
251 | HANDLE hFile,
252 | DWORD dwFlags
253 | );
254 | #undef LoadLibraryEx
255 | #define LoadLibraryEx LoadLibraryExU
256 |
257 | WRAPPER_DEC(BOOL WINAPI, MoveFile,
258 | LPCSTR lpExistingFileName,
259 | LPCSTR lpNewFileName
260 | );
261 | #undef MoveFile
262 | #define MoveFile MoveFileU
263 |
264 | WRAPPER_DEC(BOOL WINAPI, MoveFileEx,
265 | LPCSTR lpExistingFileName,
266 | LPCSTR lpNewFileName,
267 | DWORD dwFlags
268 | );
269 | #undef MoveFileEx
270 | #define MoveFileEx MoveFileExU
271 |
272 | WRAPPER_DEC(BOOL WINAPI, MoveFileWithProgress,
273 | LPCSTR lpExistingFileName,
274 | LPCSTR lpNewFileName,
275 | LPPROGRESS_ROUTINE lpProgressRoutine,
276 | LPVOID lpData,
277 | DWORD dwFlags
278 | );
279 | #undef MoveFileWithProgress
280 | #define MoveFileWithProgress MoveFileWithProgressU
281 |
282 | WRAPPER_DEC(int WINAPI, MultiByteToWideChar,
283 | UINT CodePage,
284 | DWORD dwFlags,
285 | LPCSTR lpMultiByteStr,
286 | int cbMultiByte,
287 | LPWSTR lpWideCharStr,
288 | int cchWideChar
289 | );
290 |
291 | WRAPPER_DEC(HANDLE WINAPI, OpenFileMapping,
292 | DWORD dwDesiredAccess,
293 | BOOL bInheritHandle,
294 | LPCSTR lpName
295 | );
296 | #undef OpenFileMapping
297 | #define OpenFileMapping OpenFileMappingU
298 |
299 | WRAPPER_DEC(BOOL WINAPI, ReadFile,
300 | HANDLE hFile,
301 | LPVOID lpBuffer,
302 | DWORD nNumberOfBytesToRead,
303 | LPDWORD lpNumberOfBytesRead,
304 | LPOVERLAPPED lpOverlapped
305 | );
306 |
307 | WRAPPER_DEC(BOOL WINAPI, RemoveDirectory,
308 | LPCSTR lpPathName
309 | );
310 | #undef RemoveDirectory
311 | #define RemoveDirectory RemoveDirectoryU
312 |
313 | WRAPPER_DEC(BOOL WINAPI, SetCurrentDirectory,
314 | LPCSTR lpPathName
315 | );
316 | #undef SetCurrentDirectory
317 | #define SetCurrentDirectory SetCurrentDirectoryU
318 |
319 | WRAPPER_DEC(BOOL WINAPI, SetEnvironmentVariable,
320 | LPCSTR lpName,
321 | LPCSTR lpValue
322 | );
323 | #undef SetEnvironmentVariable
324 | #define SetEnvironmentVariable SetEnvironmentVariableU
325 |
326 | WRAPPER_DEC(int WINAPI, WideCharToMultiByte,
327 | UINT CodePage,
328 | DWORD dwFlags,
329 | LPCWSTR lpWideCharStr,
330 | int cchWideChar,
331 | LPSTR lpMultiByteStr,
332 | int cbMultiByte,
333 | LPCSTR lpDefaultChar,
334 | LPBOOL lpUsedDefaultChar
335 | );
336 |
337 | WRAPPER_DEC(BOOL WINAPI, WriteFile,
338 | HANDLE hFile,
339 | LPCVOID lpBuffer,
340 | DWORD nNumberOfBytesToWrite,
341 | LPDWORD lpNumberOfBytesWritten,
342 | LPOVERLAPPED lpOverlapped
343 | );
344 |
345 | WRAPPER_DEC(BOOL WINAPI, WritePrivateProfileString,
346 | LPCSTR lpAppName,
347 | LPCSTR lpKeyName,
348 | LPCSTR lpString,
349 | LPCSTR lpFileName
350 | );
351 | #undef WritePrivateProfileString
352 | #define WritePrivateProfileString WritePrivateProfileStringU
353 |
354 | // Cleanup
355 | void kernel32_exit(void);
356 |
--------------------------------------------------------------------------------
/src/macros.c:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * Random macros and utility functions.
7 | */
8 |
9 | size_t zzstrlen(const char *str)
10 | {
11 | size_t len = 0;
12 | if(str) {
13 | for(; str[0] != 0 || str[1] != 0; str++, len++);
14 | }
15 | return len;
16 | }
17 |
18 | // Returns whether the numeric specifier at [p] consumes an argument or not,
19 | // and advances [p] past it.
20 | static int printf_num_parse(const char **p)
21 | {
22 | assert(p && *p);
23 | if(**p == '*') {
24 | // width specified in next variable argument
25 | (*p)++;
26 | return 1;
27 | } else while(isdigit(**p)) {
28 | (*p)++;
29 | }
30 | return 0;
31 | }
32 |
33 | // Fills [fmt] with information about the printf format specification
34 | // starting at [p]. [p] is assumed to start after the initial % character,
35 | // and is returned after the final character denoting the type.
36 | // Based on Wine's implementation for msvcrt.dll (dlls/msvcrt/printf.h).
37 | const char* printf_format_parse(printf_format_t *fmt, const char *p)
38 | {
39 | int flag_double = 0;
40 |
41 | assert(fmt);
42 | assert(p && (*(p - 1) == '%' || *(p - 1) == '!'));
43 |
44 | memset(fmt, 0, sizeof(printf_format_t));
45 |
46 | if(!(*p)) {
47 | return p;
48 | }
49 |
50 | // Skip flags. From left to right:
51 | // prefix sign, prefix space, left-align, zero padding, alternate
52 | while(strchr("+ -0#", *p)) {
53 | p++;
54 | }
55 |
56 | // Width
57 | fmt->argc_before_type += printf_num_parse(&p);
58 |
59 | // Precision
60 | if(*p == '.') {
61 | p++;
62 | fmt->argc_before_type += printf_num_parse(&p);
63 | }
64 |
65 | // Argument size modifier
66 | while(*p) {
67 | if(*p=='l' && *(p+1)=='l') {
68 | flag_double = 1;
69 | p += 2;
70 | } else if(*p=='h' || *p=='l' || *p=='L') {
71 | p++;
72 | } else if(*p == 'I') {
73 | if(*(p+1)=='6' && *(p+2)=='4') {
74 | flag_double = 1;
75 | p += 3;
76 | } else if(*(p+1)=='3' && *(p+2)=='2') {
77 | p += 3;
78 | } else if(isdigit(*(p+1)) || !*(p+1)) {
79 | break;
80 | } else {
81 | p++;
82 | }
83 | } else if(*p == 'w' || *p == 'F') {
84 | p++;
85 | } else {
86 | break;
87 | }
88 | }
89 | fmt->type = *(p++);
90 | if(strchr("aeEfgG", fmt->type)) {
91 | // Two surprises here:
92 | // • Yes, floats are always coerced to doubles.
93 | // • Yes, flag_double is ignored here because Microsoft has no support
94 | // for printing long doubles in either of their printf implementations.
95 | fmt->type_size_in_ints = sizeof(double) / sizeof(int);
96 | } else if(strchr("sScCpndiouxX", fmt->type)) {
97 | fmt->type_size_in_ints = flag_double + 1;
98 | }
99 | return p;
100 | }
101 |
102 | const char* windows_version(void)
103 | {
104 | static char version[64];
105 | if(version[0] != '\0') {
106 | return version;
107 | }
108 |
109 | char *p = version;
110 | size_t rem = sizeof(version) / sizeof(version[0]);
111 |
112 | // This function uses _snprintf to retain the previous behavior
113 | // of wnsprintfA while still upgrading the buffer size parameter
114 | // to a size_t to suppress type warnings on x64.
115 | #define snprintf_cat(fmt, ...) \
116 | if(rem > 0) { \
117 | int chars_printed = _snprintf(p, rem, fmt, __VA_ARGS__); \
118 | if (chars_printed >= 0) { \
119 | p += chars_printed; \
120 | rem -= chars_printed; \
121 | } else { \
122 | p += rem; \
123 | rem = 0; \
124 | } \
125 | }
126 |
127 | // Don't need to depend on the entire Driver Development Kit just for
128 | // ntddk.h.
129 | typedef struct _OSVERSIONINFOW {
130 | ULONG dwOSVersionInfoSize;
131 | ULONG dwMajorVersion;
132 | ULONG dwMinorVersion;
133 | ULONG dwBuildNumber;
134 | ULONG dwPlatformId;
135 | WCHAR szCSDVersion[128];
136 | USHORT wServicePackMajor;
137 | USHORT wServicePackMinor;
138 | USHORT wSuiteMask;
139 | UCHAR wProductType;
140 | UCHAR wReserved;
141 | } RTL_OSVERSIONINFOEXW, *PRTL_OSVERSIONINFOEXW;
142 |
143 | // Or ntoskrnl.lib.
144 | typedef LONG WINAPI RtlGetVersion_type(RTL_OSVERSIONINFOEXW *lpVersionInformation);
145 | typedef const char *wine_get_version_type(void);
146 |
147 | RtlGetVersion_type* RtlGetVersion;
148 | wine_get_version_type *wine_get_version;
149 |
150 | HMODULE hNTDLL = GetModuleHandleA("ntdll.dll");
151 | if(!hNTDLL) {
152 | snprintf_cat("%s", "unknown operating system");
153 | return version;
154 | }
155 |
156 | // Wine
157 | // ----
158 | wine_get_version = (wine_get_version_type*)GetProcAddress(hNTDLL, "wine_get_version");
159 | if(wine_get_version) {
160 | const char* wine_ver = wine_get_version();
161 | if(!wine_ver) {
162 | wine_ver = "";
163 | }
164 | snprintf_cat("Wine/%s", wine_ver);
165 | return version;
166 | }
167 | // ----
168 |
169 | RtlGetVersion = (RtlGetVersion_type*)GetProcAddress(hNTDLL, "RtlGetVersion");
170 | RTL_OSVERSIONINFOEXW ver_info = {0};
171 | ver_info.dwOSVersionInfoSize = sizeof(ver_info);
172 | RtlGetVersion(&ver_info);
173 |
174 | const char *winver = NULL;
175 | ULONG major = ver_info.dwMajorVersion;
176 | ULONG minor = ver_info.dwMinorVersion;
177 | UCHAR product = ver_info.wProductType;
178 | USHORT suite = ver_info.wSuiteMask;
179 |
180 | // As per https://msdn.microsoft.com/en-us/library/windows/hardware/ff563620(v=vs.85).aspx
181 | if(major == 10) {
182 | // Windows 11 also use major = 10 and minor = 0
183 | if(ver_info.dwBuildNumber >= 22000) {
184 | winver = "11";
185 | } else {
186 | winver = "10";
187 | }
188 | } else if(major == 6 && minor == 3) {
189 | winver = "8.1";
190 | } else if(major == 6 && minor == 2 && product == VER_NT_WORKSTATION) {
191 | winver = "8";
192 | } else if(major == 6 && minor == 2 && product != VER_NT_WORKSTATION) {
193 | winver = "Server 2012";
194 | } else if(major == 6 && minor == 1 && product == VER_NT_WORKSTATION) {
195 | winver = "7";
196 | } else if(major == 6 && minor == 1 && product != VER_NT_WORKSTATION) {
197 | winver = "Server 2008 R2";
198 | } else if(major == 6 && minor == 0 && product == VER_NT_WORKSTATION) {
199 | winver = "Vista";
200 | } else if(major == 6 && minor == 0 && product != VER_NT_WORKSTATION) {
201 | winver = "Server 2008";
202 | } else if(major == 5 && minor == 2 && suite == VER_SUITE_WH_SERVER) {
203 | winver = "Home Server";
204 | } else if(major == 5 && minor == 2) {
205 | winver = "Server 2003";
206 | } else if(major == 5 && minor == 1) {
207 | winver = "XP";
208 | } else if(major == 5 && minor == 0) {
209 | winver = "2000";
210 | }
211 |
212 | if(winver) {
213 | snprintf_cat("Windows %s", winver);
214 | } else {
215 | snprintf_cat("Windows %u.%u", major, minor);
216 | }
217 |
218 | // szCSDVersion can be localized, see
219 | // https://channel9.msdn.com/forums/Coffeehouse/542106-The-Service-Pack-string/
220 | // So...
221 | if(ver_info.wServicePackMajor != 0) {
222 | snprintf_cat(", Service Pack %hu", ver_info.wServicePackMajor);
223 | if(ver_info.wServicePackMinor != 0) {
224 | snprintf_cat(".%hu", ver_info.wServicePackMinor);
225 | }
226 | }
227 |
228 | // If Windows 10 really will be the "last Windows", we better add this too.
229 | if(ver_info.dwBuildNumber != 0) {
230 | snprintf_cat(", Build %u", ver_info.dwBuildNumber);
231 | }
232 | #undef snprintf_cat
233 | return version;
234 | }
235 |
--------------------------------------------------------------------------------
/src/macros.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * Random macros and utility functions.
7 | */
8 |
9 | #pragma once
10 |
11 | // C versions
12 | #ifdef __STDC__
13 | # ifndef C89
14 | # define C89 1
15 | # endif
16 | # ifndef C90
17 | # define C90 1
18 | # endif
19 | # if defined(__STDC_VERSION__)
20 | # if (__STDC_VERSION__ >= 199409L)
21 | # ifndef C94
22 | # define C94 1
23 | # endif
24 | // The proper name for this version is C95,
25 | // but keep the C94 macro around just incase
26 | # ifndef C95
27 | # define C95 1
28 | # endif
29 | # endif
30 | # if !defined(C99) && (__STDC_VERSION__ >= 199901L)
31 | # define C99 1
32 | # endif
33 | # if !defined(C11) && (__STDC_VERSION__ >= 201112L)
34 | # define C11 1
35 | # endif
36 | # if !defined(C17) && (__STDC_VERSION__ >= 201710L)
37 | # define C17 1
38 | # endif
39 | # if !defined(C2X) && (__STDC_VERSION__ > 201710L)
40 | # define C2X 1
41 | # endif
42 | # endif
43 | #endif
44 |
45 | // Clang and GCC both support VLAs regardless of C/C++
46 | #if __GNUC__ || __clang__ || C99 || (C11 && (defined(__STDC_NO_VLA__) && __STDC_NO_VLA__ != 1))
47 | #define VLA_SUPPORT 1
48 | #else
49 | #define VLA_SUPPORT 0
50 | #endif
51 |
52 | #if defined(_WIN32) && defined(_DEBUG)
53 | #define _CRTDBG_MAP_ALLOC
54 | #include
55 | #elif !VLA_SUPPORT
56 | #include
57 | #endif
58 |
59 | #include
60 |
61 | #ifndef MACRO_WRAP
62 | #define MACRO_WRAP(...) \
63 | do { \
64 | __VA_ARGS__ \
65 | } while (0)
66 | #endif
67 |
68 | /**
69 | * Resource identifier type. Either a multi-byte or wide string or, if the
70 | * high word is 0, an integer. Therefore, pretending that these are just
71 | * mere strings (as both their variable name *and* their Hungarian notation
72 | * prefix would suggest) is not only misleading, but downright harmful.
73 | *
74 | * I thought about using the completely type-safe approach of defining a
75 | * union for both string types. This would throw a compile error when a RESID
76 | * is accessed as if it were a string. However, there are pretty much only
77 | * drawbacks to that approach:
78 | * • This would exactly require to redeclare *all* structures to use the
79 | * (W)RESID unions where applicable.
80 | * • Pretty much all of the A and W structures are typedef'd and thus can't
81 | * easily be re-#define'd like the functions. This means that, in the case
82 | * of static linking, these new declarations then wouldn't even propagate
83 | * to existing application code.
84 | * • It would break compilation of existing, perfectly fine code as all
85 | * function calls that pass a RESID union in one of their parameters or
86 | * structures would also throw a compiler error.
87 | * • Finally, even if we only used the RESID union internally ourselves, it
88 | * only adds complication to the conversion macros, which would need to
89 | * validly convert the input "strings" into RESID unions.
90 | * So, it's just down to a mere typedef alias.
91 | */
92 |
93 | typedef const char* RESID;
94 | typedef const wchar_t* WRESID;
95 |
96 | // Most Win32 API functions return TRUE on success and FALSE on failure,
97 | // requiring a separate call to GetLastError() to get the actual error code.
98 | // This macro wraps these function calls to use the opposite, more sensible
99 | // scheme, returning FALSE on success and automatically calling GetLastError()
100 | // on failure.
101 | #define W32_ERR_WRAP(x) \
102 | ((x) ? 0 : GetLastError())
103 |
104 | // Freeing
105 | #define SAFE_CLEANUP(func, x) MACRO_WRAP(\
106 | if (x) { \
107 | (void)func(x), (x) = 0; \
108 | } \
109 | )
110 | #define SAFE_FREE(x) SAFE_CLEANUP(free, x)
111 |
112 | #define elementsof(x) (sizeof(x) / sizeof((x)[0]))
113 |
114 | #if !WINUTF8_FAST_VLA
115 | #define w32u8_alloca(type, size) ((type*)_malloca((size) * sizeof(type)))
116 | #define w32u8_freea(name) SAFE_CLEANUP(_freea, name)
117 | #else
118 | #define w32u8_alloca(type, size) ((type*)_alloca((size) * sizeof(type)))
119 | #define w32u8_freea(name) do; while(0) /* require a semi-colon */
120 | #endif
121 |
122 | // Variable length arrays
123 | #if VLA_SUPPORT
124 | # define VLA(type, name, size) \
125 | type name##_vla[(size)]; \
126 | type *name = name##_vla /* to ensure that [name] is a modifiable lvalue */
127 | # define VLA_FREE(name) \
128 | do; while(0) /* require a semi-colon */
129 | #else
130 | # define VLA(type, name, size) \
131 | type *name = w32u8_alloca(type, size)
132 | # define VLA_FREE(name) \
133 | w32u8_freea(name)
134 | #endif
135 |
136 | // Returns the length of a double-null-terminated string, not including the
137 | // terminating two 0 bytes.
138 | size_t zzstrlen(const char *str);
139 |
140 | /// Convenient wchar_t conversion macros
141 | /// ------------------------------------
142 | #define STRLEN_DEC(src_char) \
143 | size_t src_char##_len = (strlen(src_char) + 1)
144 |
145 | #define WCSLEN_DEC(src_wchar) \
146 | size_t src_wchar##_len = ((wcslen(src_wchar) * UTF8_MUL) + 1)
147 |
148 | // "create-wchar_t-from-strlen"
149 | #define WCHAR_T_DEC(src_char) \
150 | STRLEN_DEC(src_char); \
151 | VLA(wchar_t, src_char##_w, src_char##_len)
152 |
153 | #define WCHAR_T_DECA(src_char) \
154 | STRLEN_DEC(src_char); \
155 | wchar_t* src_char##_w = w32u8_alloca(wchar_t, src_char##_len); \
156 |
157 | #define WCHAR_T_CONV(src_char) \
158 | StringToUTF16(src_char##_w, src_char, src_char##_len)
159 |
160 | #define WCHAR_T_FREE(src_char) \
161 | VLA_FREE(src_char##_w)
162 |
163 | #define WCHAR_T_FREEA(src_char) \
164 | w32u8_freea(src_char##_w)
165 |
166 | // "create-UTF-8-from-wchar_t"
167 | #define UTF8_DEC(src_wchar) \
168 | WCSLEN_DEC(src_wchar); \
169 | VLA(char, src_wchar##_utf8, src_wchar##_len)
170 |
171 | #define UTF8_DECA(src_wchar) \
172 | WCSLEN_DEC(src_wchar); \
173 | char* src_wchar##_utf8 = w32_alloca(char, src_wchar##_len)
174 |
175 | #define UTF8_CONV(src_wchar) \
176 | StringToUTF8(src_wchar##_utf8, src_wchar, src_wchar##_len)
177 |
178 | #define UTF8_FREE(src_wchar) \
179 | VLA_FREE(src_wchar##_utf8)
180 |
181 | #define UTF8_FREEA(src_wchar) \
182 | w32u8_freea(src_wchar##_utf8)
183 | /// ------------------------------------
184 |
185 | // Declare a wrapping function together with a corresponding typedef
186 | #define WRAPPER_DEC(rettype, name, ...) \
187 | typedef rettype name##A_type(__VA_ARGS__); \
188 | rettype name##U(__VA_ARGS__)
189 |
190 | /// Convenient dynamic binding for functions not available before Vista
191 | /// -------------------------------------------------------------------
192 | #define DLL_FUNC(dll, func) \
193 | dll##_##func
194 |
195 | #define DLL_FUNC_TYPE(dll, func) \
196 | dll##_##func##_t /* GCC doesn't accept DLL_FUNC(dll_func)##_t */
197 |
198 | #define DLL_FUNC_DEF(dll, func) \
199 | DLL_FUNC_TYPE(dll, func) *DLL_FUNC(dll, func) = NULL
200 |
201 | #define DLL_FUNC_GET(dll, func) \
202 | DLL_FUNC(dll, func) = (DLL_FUNC_TYPE(dll, func)*)GetProcAddress(dll, #func)
203 |
204 | #define DLL_FUNC_CALL(dll, func, ...) MACRO_WRAP(\
205 | if(DLL_FUNC(dll, func)) { \
206 | ret = DLL_FUNC(dll, func)(__VA_ARGS__); \
207 | } else { \
208 | MessageBoxU(NULL, \
209 | "Tried to call "#func"() from "#dll".dll, " \
210 | "which is not available on this Windows version.", \
211 | "Win32 UTF-8 wrapper", \
212 | MB_OK | MB_ICONEXCLAMATION \
213 | ); \
214 | SetLastError(ERROR_CALL_NOT_IMPLEMENTED); \
215 | ret = 0; \
216 | } \
217 | )
218 | /// -------------------------------------------------------------------
219 |
220 | /// IUnknown implementation for COM proxy classes
221 | /// ---------------------------------------------
222 | // Also comes with its own reference counting, in case someone needs to wrap
223 | // a NULL instance.
224 | #define IUNKNOWN_DEC(prefix, x) \
225 | protected: \
226 | x *pOrig; \
227 | ULONG fallback_ref; \
228 | \
229 | public: \
230 | prefix##_##x(x *_pOrig) : pOrig(_pOrig), fallback_ref(1) {} \
231 | \
232 | HRESULT __stdcall QueryInterface(REFIID riid, LPVOID * ppvObj); \
233 | ULONG __stdcall AddRef(); \
234 | ULONG __stdcall Release();
235 |
236 | #define IUNKNOWN_DEF(x, QueryInterface_ReplaceCondition) \
237 | HRESULT x::QueryInterface(REFIID riid, void** ppvObj) \
238 | { \
239 | if(!ppvObj) { \
240 | return E_POINTER; \
241 | } \
242 | if(!pOrig) { \
243 | return E_NOINTERFACE; \
244 | } \
245 | *ppvObj = NULL; \
246 | \
247 | HRESULT hRes = pOrig->QueryInterface(riid, ppvObj); \
248 | if(hRes == NOERROR && (QueryInterface_ReplaceCondition)) { \
249 | *ppvObj = this; \
250 | } \
251 | return hRes; \
252 | } \
253 | \
254 | ULONG x::AddRef() \
255 | { \
256 | return !pOrig ? ++fallback_ref : pOrig->AddRef(); \
257 | } \
258 | \
259 | ULONG x::Release() \
260 | { \
261 | ULONG count = !pOrig ? --fallback_ref : pOrig->Release(); \
262 | if(count == 0) { \
263 | delete this; \
264 | } \
265 | return count; \
266 | }
267 | /// ---------------------------------------------
268 |
269 | // Define Visual C++ warnings away
270 | #if (_MSC_VER >= 1600) && \
271 | ((( defined _CRT_DECLARE_NONSTDC_NAMES && _CRT_DECLARE_NONSTDC_NAMES) || \
272 | (!defined _CRT_DECLARE_NONSTDC_NAMES && !__STDC__ )) && \
273 | !(defined _CRT_NONSTDC_NO_DEPRECATE && !defined _CRT_NONSTDC_NO_WARNINGS))
274 | // char* itoa(int _Value, char *_Buffer, int _Radix)
275 | # define itoa _itoa
276 | // void* memccpy(void *_Dst, const void *_Src, int _Val, size_t _MaxCount)
277 | # define memccpy _memccpy
278 | // int strnicmp(const char *_String1, const char *_String2, size_t _MaxCount)
279 | # define strnicmp _strnicmp
280 | // int stricmp(const char *_String1, const char *_String2)
281 | # define stricmp _stricmp
282 | // char* strlwer(char *_String)
283 | # define strlwr _strlwr
284 | // int vsnwprintf(wchar_t *_Buffer, size_t _BufferCount, const wchar_t *_Format, va_list _ArgList)
285 | # define vsnwprintf _vsnwprintf
286 | // int wcsicmp(const wchar_t *_String1, const wchar_t *_String2)
287 | # define wcsicmp _wcsicmp
288 | #endif
289 |
290 | #if _MSC_VER
291 | #undef strdup
292 | static __inline char* w32u8_strdup(const char* src) {
293 | size_t length = strlen(src) + 1;
294 | return (char*)memcpy(malloc(length), src, length);
295 | }
296 | // char* strdup(const char *_Source)
297 | #define strdup w32u8_strdup
298 |
299 | #undef wcsdup
300 | static __inline wchar_t* w32u8_wcsdup(const wchar_t* src) {
301 | size_t length = (wcslen(src) + 1) * sizeof(wchar_t);
302 | return (wchar_t*)memcpy(malloc(length), src, length);
303 | }
304 | // wchar_t* wcsdup(const wchar_t *_Source)
305 | #define wcsdup w32u8_wcsdup
306 | #endif
307 |
308 | // Convenience macro to convert one fixed-length string to UTF-16.
309 | #define FixedLengthStringConvert(str_in, str_len) \
310 | size_t str_in##_len = (str_len != -1 ? str_len : strlen(str_in)); \
311 | size_t str_in##_w_len; \
312 | VLA(wchar_t, str_in##_w, str_in##_len + 1); \
313 | str_in##_w_len = StringToUTF16(str_in##_w, str_in, str_in##_len); \
314 | str_in##_w[str_in##_w_len] = L'\0'
315 |
316 | // Now, if Microsoft just had used integer identifiers for resources instead
317 | // of names plus the MAKEINTRESOURCE / MAKEINTATOM hacks, we could just
318 | // point all these calls to their wide versions and be done with it.
319 | // Instead, there is some maintenance to do...
320 | #define RESID_DEC(local) \
321 | LPWSTR local##_w = NULL
322 |
323 | #define RESID_IS_STR(src) \
324 | (HIWORD(src) != 0)
325 |
326 | #define RESID_CONV(local, src) MACRO_WRAP(\
327 | if(RESID_IS_STR(src)) { \
328 | size_t local##_len = strlen(src) + 1; \
329 | VLA(wchar_t, local##_w_vla, local##_len); \
330 | local##_w = StringToUTF16_VLA(local##_w_vla, src, local##_len); \
331 | } else { \
332 | local##_w = (LPWSTR)(src); \
333 | } \
334 | )
335 |
336 | #define RESID_FREE(local, src) MACRO_WRAP(\
337 | if(RESID_IS_STR(src)) { \
338 | WCHAR_T_FREE(local); \
339 | } \
340 | )
341 |
342 | /// printf format specifier parsing
343 | /// -------------------------------
344 | // Information about a single printf format specification.
345 | typedef struct {
346 | int argc_before_type;
347 | char type;
348 | int type_size_in_ints;
349 | } printf_format_t;
350 |
351 | // Fills [fmt] with information about the printf format specification
352 | // starting at [p]. [p] is assumed to start after the initial % character,
353 | // and is returned after the final character denoting the type.
354 | // Based on Wine's implementation for msvcrt.dll (dlls/msvcrt/printf.h).
355 | const char* printf_format_parse(printf_format_t *fmt, const char *p);
356 | /// -------------------------------
357 |
358 | // Returns a diagnostic (!) string describing the currently running Windows or
359 | // Wine version, regardless of any manifests.
360 | const char* windows_version(void);
361 |
--------------------------------------------------------------------------------
/src/msvcrt_dll.c:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * C runtime functions.
7 | */
8 |
9 | const w32u8_pair_t msvcrt_pairs[] = {
10 | {"fopen", fopen_u},
11 | { NULL }
12 | };
13 |
14 | // Yes, this should better be implemented as a wrapper around fopen_s() (and
15 | // thus, _wfopen_s()), but XP's msvcrt.dll doesn't have that function.
16 | FILE * __cdecl fopen_u(
17 | const char * _Filename,
18 | const char * _Mode
19 | )
20 | {
21 | FILE *ret = NULL;
22 | WCHAR_T_DEC(_Filename);
23 | WCHAR_T_DEC(_Mode);
24 | WCHAR_T_CONV(_Filename);
25 | WCHAR_T_CONV(_Mode);
26 | ret = _wfopen(_Filename_w, _Mode_w);
27 | WCHAR_T_FREE(_Filename);
28 | WCHAR_T_FREE(_Mode);
29 | return ret;
30 | }
31 |
--------------------------------------------------------------------------------
/src/msvcrt_dll.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * C runtime functions.
7 | */
8 |
9 | #pragma once
10 |
11 | FILE * __cdecl fopen_u(
12 | const char * _Filename,
13 | const char * _Mode
14 | );
15 | #undef fopen
16 | #define fopen fopen_u
17 |
--------------------------------------------------------------------------------
/src/psapi_dll.c:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * psapi.dll functions.
7 | */
8 |
9 | const w32u8_pair_t psapi_pairs[] = {
10 | {"GetModuleFileNameExA", GetModuleFileNameExU},
11 | { NULL }
12 | };
13 |
14 | DWORD WINAPI GetModuleFileNameExU(
15 | HANDLE hProcess,
16 | HMODULE hModule,
17 | LPSTR lpFilename,
18 | DWORD nSize
19 | )
20 | {
21 | VLA(wchar_t, lpFilename_w, nSize);
22 | DWORD ret = GetModuleFileNameExW(hProcess, hModule, lpFilename_w, nSize);
23 | StringToUTF8(lpFilename, lpFilename_w, nSize);
24 | VLA_FREE(lpFilename_w);
25 | return ret;
26 | }
27 |
--------------------------------------------------------------------------------
/src/psapi_dll.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * psapi.dll functions.
7 | */
8 |
9 | #pragma once
10 |
11 | WRAPPER_DEC(DWORD WINAPI, GetModuleFileNameEx,
12 | HANDLE hProcess,
13 | HMODULE hModule,
14 | LPSTR lpFilename,
15 | DWORD nSize
16 | );
17 | #undef GetModuleFileNameEx
18 | #define GetModuleFileNameEx GetModuleFileNameExU
19 |
--------------------------------------------------------------------------------
/src/shell32_dll.c:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * shell32.dll functions.
7 | */
8 |
9 | const w32u8_pair_t shell32_pairs[] = {
10 | {"DragQueryFileA", DragQueryFileU},
11 | {"ExtractIconA", ExtractIconU},
12 | {"ExtractIconExA", ExtractIconExU},
13 | {"SHBrowseForFolderA", SHBrowseForFolderU},
14 | {"SHGetFolderPathA", SHGetFolderPathU},
15 | {"SHGetPathFromIDListA", SHGetPathFromIDListU},
16 | {"ShellExecuteA", ShellExecuteU},
17 | { NULL }
18 | };
19 |
20 | LPSTR* WINAPI CommandLineToArgvU(
21 | LPCWSTR lpCmdLine,
22 | int* pNumArgs
23 | )
24 | {
25 | int cmd_line_pos; // Array "index" of the actual command line string
26 | WCSLEN_DEC(lpCmdLine);
27 | char **argv_u;
28 |
29 | wchar_t **argv_w = CommandLineToArgvW(lpCmdLine, pNumArgs);
30 | if(!argv_w) {
31 | return NULL;
32 | }
33 | cmd_line_pos = *pNumArgs + 1;
34 |
35 | // argv is indeed terminated with an additional sentinel NULL pointer.
36 | argv_u = LocalAlloc(
37 | LMEM_FIXED, cmd_line_pos * sizeof(char*) + lpCmdLine_len
38 | );
39 | if(argv_u) {
40 | int i;
41 | char *cur_arg_u = (char*)&argv_u[cmd_line_pos];
42 | for(i = 0; i < *pNumArgs; i++) {
43 | size_t cur_arg_u_len;
44 | argv_u[i] = cur_arg_u;
45 | cur_arg_u_len = StringToUTF8(
46 | cur_arg_u, argv_w[i], lpCmdLine_len
47 | ) + 1;
48 | cur_arg_u += cur_arg_u_len;
49 | lpCmdLine_len -= cur_arg_u_len;
50 | }
51 | argv_u[i] = NULL;
52 | }
53 |
54 | LocalFree(argv_w);
55 | return argv_u;
56 | }
57 |
58 | UINT WINAPI DragQueryFileU(
59 | HANDLE hDrop,
60 | UINT iFile,
61 | LPSTR lpszFile,
62 | UINT cch
63 | )
64 | {
65 | DWORD ret;
66 | VLA(wchar_t, lpszFile_w, cch);
67 |
68 | if(!lpszFile) {
69 | VLA_FREE(lpszFile_w);
70 | }
71 | ret = DragQueryFileW(hDrop, iFile, lpszFile_w, cch);
72 | if(ret) {
73 | if(lpszFile) {
74 | StringToUTF8(lpszFile, lpszFile_w, cch);
75 | } else if(iFile != 0xFFFFFFFF) {
76 | VLA(wchar_t, lpBufferReal_w, ret);
77 | ret = DragQueryFileW(hDrop, iFile, lpBufferReal_w, cch);
78 | ret = StringToUTF8(NULL, lpBufferReal_w, 0);
79 | VLA_FREE(lpBufferReal_w);
80 | }
81 | }
82 | VLA_FREE(lpszFile_w);
83 | return ret;
84 | }
85 |
86 | HICON WINAPI ExtractIconU(
87 | HINSTANCE hInst,
88 | LPCSTR lpszExeFileName,
89 | UINT nIconIndex
90 | )
91 | {
92 | HICON ret;
93 | WCHAR_T_DEC(lpszExeFileName);
94 | WCHAR_T_CONV(lpszExeFileName);
95 | ret = ExtractIconW(hInst, lpszExeFileName_w, nIconIndex);
96 | WCHAR_T_FREE(lpszExeFileName);
97 | return ret;
98 | }
99 |
100 | UINT WINAPI ExtractIconExU(
101 | LPCSTR lpszFile,
102 | int nIconIndex,
103 | HICON *phiconLarge,
104 | HICON *phiconSmall,
105 | UINT nIcons
106 | )
107 | {
108 | UINT ret;
109 | WCHAR_T_DEC(lpszFile);
110 | WCHAR_T_CONV(lpszFile);
111 | ret = ExtractIconExW(
112 | lpszFile_w, nIconIndex, phiconLarge, phiconSmall, nIcons
113 | );
114 | WCHAR_T_FREE(lpszFile);
115 | return ret;
116 | }
117 |
118 | // CoGetApartmentType() is not available prior to Windows 7, but luckily,
119 | // http://msdn.microsoft.com/en-us/library/windows/desktop/dd542641%28v=vs.85%29.aspx
120 | // describes a way how to get the same functionality on previous systems.
121 | static HRESULT CoGetApartmentTypeCompat(
122 | _Out_ APTTYPE *apttype
123 | )
124 | {
125 | int ret = S_FALSE;
126 | APTTYPEQUALIFIER apttype_qualifier;
127 |
128 | #ifdef __MINGW32__
129 | // Since adding -luuid causes MinGW to link in *all* GUIDs, we define
130 | // this manually, and therefore save ~17.5 KB in the compiled binary.
131 | const IID IID_IComThreadingInfo = {
132 | 0x000001ce,0x0000,0x0000, { 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46 }
133 | };
134 | #endif
135 |
136 | typedef HRESULT WINAPI CoGetApartmentType_t(
137 | _Out_ APTTYPE *pAptType,
138 | _Out_ APTTYPEQUALIFIER *pAptQualifier
139 | );
140 | // GetVersionEx() is deprecated with Windows 8.1, not everybody will
141 | // have VersionHelpers.h, and this is a lot more fault-tolerant anyway.
142 | HMODULE ole32 = LoadLibrary("ole32.dll");
143 | CoGetApartmentType_t *cgat = NULL;
144 |
145 | *apttype = APTTYPE_MTA;
146 | if(!ole32) {
147 | return S_FALSE;
148 | }
149 | cgat = (CoGetApartmentType_t*)GetProcAddress(ole32, "CoGetApartmentType");
150 | if(cgat) {
151 | ret = cgat(apttype, &apttype_qualifier);
152 | } else {
153 | IUnknown *ctx_token = NULL;
154 | ret = CoGetContextToken((ULONG_PTR*)&ctx_token);
155 | if(ret == S_OK) {
156 | IComThreadingInfo *cti = NULL;
157 | ret = IUnknown_QueryInterface(
158 | ctx_token, &IID_IComThreadingInfo, (void**)&cti
159 | );
160 | if(ret == S_OK) {
161 | ret = IComThreadingInfo_GetCurrentApartmentType(cti, apttype);
162 | IUnknown_Release(cti);
163 | }
164 | } else if(ret == CO_E_NOTINITIALIZED) {
165 | *apttype = APTTYPE_CURRENT;
166 | }
167 | }
168 | FreeLibrary(ole32);
169 | return ret;
170 | }
171 |
172 | PIDLIST_ABSOLUTE WINAPI SHBrowseForFolderU(
173 | LPBROWSEINFOA lpbi
174 | )
175 | {
176 | APTTYPE apttype;
177 | PIDLIST_ABSOLUTE ret;
178 | wchar_t pszDisplayName_w[MAX_PATH];
179 | const char *lpszTitle = lpbi->lpszTitle;
180 | BROWSEINFOW lpbi_w = *((BROWSEINFOW*)lpbi);
181 | WCHAR_T_DEC(lpszTitle);
182 | WCHAR_T_CONV(lpszTitle);
183 |
184 | // Use the new UI if we can
185 | CoGetApartmentTypeCompat(&apttype);
186 | if(apttype != APTTYPE_MTA) {
187 | lpbi_w.ulFlags |= BIF_USENEWUI;
188 | }
189 | // Really, folder browse dialogs without edit box should be outlawed.
190 | lpbi_w.ulFlags |= BIF_EDITBOX;
191 | lpbi_w.pszDisplayName = pszDisplayName_w;
192 | lpbi_w.lpszTitle = lpszTitle_w;
193 | ret = SHBrowseForFolderW(&lpbi_w);
194 | StringToUTF8(lpbi->pszDisplayName, pszDisplayName_w, MAX_PATH);
195 | WCHAR_T_FREE(lpszTitle);
196 | return ret;
197 | }
198 |
199 | HRESULT WINAPI SHGetFolderPathU(
200 | HWND hWnd,
201 | int csidl,
202 | HANDLE hToken,
203 | DWORD dwFlags,
204 | LPSTR pszPath
205 | )
206 | {
207 | wchar_t pszPath_w[MAX_PATH];
208 | HRESULT ret;
209 |
210 | if(pszPath) {
211 | pszPath[0] = '\0';
212 | }
213 |
214 | ret = SHGetFolderPathW(hWnd, csidl, hToken, dwFlags, pszPath_w);
215 | if(ret == S_OK && pszPath) {
216 | StringToUTF8(pszPath, pszPath_w, MAX_PATH);
217 | }
218 | return ret;
219 | }
220 |
221 | BOOL WINAPI SHGetPathFromIDListU(
222 | PCIDLIST_ABSOLUTE pidl,
223 | LPSTR pszPath
224 | )
225 | {
226 | wchar_t pszPath_w[MAX_PATH];
227 | BOOL ret = SHGetPathFromIDListW(pidl, pszPath_w);
228 | if(pszPath) {
229 | StringToUTF8(pszPath, pszPath_w, MAX_PATH);
230 | return ret;
231 | }
232 | return 0;
233 | }
234 |
235 | HRESULT WINAPI SHParseDisplayNameU(
236 | LPCSTR pszName,
237 | IBindCtx *pbc,
238 | LPITEMIDLIST *ppidl,
239 | SFGAOF sfgaoIn,
240 | SFGAOF *psfgaoOut
241 | )
242 | {
243 | HRESULT ret;
244 | WCHAR_T_DEC(pszName);
245 | WCHAR_T_CONV(pszName);
246 | if(pszName_w) {
247 | wchar_t *p = pszName_w;
248 | while(*p) {
249 | if(*p == L'/') {
250 | *p = L'\\';
251 | }
252 | p++;
253 | }
254 | }
255 | ret = SHParseDisplayName(pszName_w, pbc, ppidl, sfgaoIn, psfgaoOut);
256 | WCHAR_T_FREE(pszName);
257 | return ret;
258 | }
259 |
260 | HINSTANCE WINAPI ShellExecuteU(
261 | HWND hwnd,
262 | LPCSTR lpOperation,
263 | LPCSTR lpFile,
264 | LPCSTR lpParameters,
265 | LPCSTR lpDirectory,
266 | INT nShowCmd
267 | )
268 | {
269 | HINSTANCE ret;
270 | WCHAR_T_DEC(lpOperation);
271 | WCHAR_T_DEC(lpFile);
272 | WCHAR_T_DEC(lpParameters);
273 | WCHAR_T_DEC(lpDirectory);
274 |
275 | WCHAR_T_CONV(lpOperation);
276 | WCHAR_T_CONV(lpFile);
277 | WCHAR_T_CONV(lpParameters);
278 | WCHAR_T_CONV(lpDirectory);
279 | ret = ShellExecuteW(hwnd, lpOperation_w, lpFile_w, lpParameters_w, lpDirectory_w, nShowCmd);
280 | WCHAR_T_FREE(lpOperation);
281 | WCHAR_T_FREE(lpFile);
282 | WCHAR_T_FREE(lpParameters);
283 | WCHAR_T_FREE(lpDirectory);
284 | return ret;
285 | }
--------------------------------------------------------------------------------
/src/shell32_dll.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * shell32.dll functions.
7 | */
8 |
9 | #pragma once
10 |
11 | WRAPPER_DEC(LPSTR* WINAPI, CommandLineToArgv,
12 | LPCWSTR lpCmdLine,
13 | int* pNumArgs
14 | );
15 |
16 | WRAPPER_DEC(UINT WINAPI, DragQueryFile,
17 | HANDLE hDrop,
18 | UINT iFile,
19 | LPSTR lpszFile,
20 | UINT cch
21 | );
22 | #undef DragQueryFile
23 | #define DragQueryFile DragQueryFileU
24 |
25 | WRAPPER_DEC(HICON WINAPI, ExtractIcon,
26 | HINSTANCE hInst,
27 | LPCSTR lpszExeFileName,
28 | UINT nIconIndex
29 | );
30 | #undef ExtractIcon
31 | #define ExtractIcon ExtractIconU
32 |
33 | WRAPPER_DEC(UINT WINAPI, ExtractIconEx,
34 | LPCSTR lpszFile,
35 | int nIconIndex,
36 | HICON *phiconLarge,
37 | HICON *phiconSmall,
38 | UINT nIcons
39 | );
40 | #undef ExtractIconEx
41 | #define ExtractIconEx ExtractIconExU
42 |
43 | WRAPPER_DEC(PIDLIST_ABSOLUTE WINAPI, SHBrowseForFolder,
44 | LPBROWSEINFOA lpbi
45 | );
46 | #undef SHBrowseForFolder
47 | #define SHBrowseForFolder SHBrowseForFolderU
48 | #undef BROWSEINFO
49 | #undef PBROWSEINFO
50 | #undef LPBROWSEINFO
51 | #define BROWSEINFO BROWSEINFOA
52 | #define PBROWSEINFO PBROWSEINFOA
53 | #define LPBROWSEINFO LPBROWSEINFOA
54 |
55 | WRAPPER_DEC(HRESULT WINAPI, SHGetFolderPath,
56 | HWND hWnd,
57 | int csidl,
58 | HANDLE hToken,
59 | DWORD dwFlags,
60 | LPSTR pszPath
61 | );
62 | #undef SHGetFolderPath
63 | #define SHGetFolderPath SHGetFolderPathU
64 |
65 | WRAPPER_DEC(BOOL WINAPI, SHGetPathFromIDList,
66 | PCIDLIST_ABSOLUTE pidl,
67 | LPSTR pszPath
68 | );
69 | #undef SHGetPathFromIDList
70 | #define SHGetPathFromIDList SHGetPathFromIDListU
71 |
72 | WRAPPER_DEC(HRESULT WINAPI, SHParseDisplayName,
73 | LPCSTR pszName,
74 | IBindCtx *pbc,
75 | LPITEMIDLIST *ppidl,
76 | SFGAOF sfgaoIn,
77 | SFGAOF *psfgaoOut
78 | );
79 |
80 | WRAPPER_DEC(HINSTANCE WINAPI, ShellExecute,
81 | HWND hwnd,
82 | LPCSTR lpOperation,
83 | LPCSTR lpFile,
84 | LPCSTR lpParameters,
85 | LPCSTR lpDirectory,
86 | INT nShowCmd
87 | );
88 | #undef ShellExecute
89 | #define ShellExecute ShellExecuteU
--------------------------------------------------------------------------------
/src/shlwapi_dll.c:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * shlwapi.dll functions.
7 | */
8 |
9 | const w32u8_pair_t shlwapi_pairs[] = {
10 | {"PathFileExistsA", PathFileExistsU},
11 | {"PathFindFileNameA", PathFindFileNameU},
12 | {"PathMatchSpecA", PathMatchSpecU},
13 | {"PathMatchSpecExA", PathMatchSpecExU},
14 | {"PathRemoveFileSpecA", PathRemoveFileSpecU},
15 | {"PathAddBackslashA", PathAddBackslashU},
16 | {"PathIsDirectoryA", PathIsDirectoryU},
17 | {"PathIsRelativeA", PathIsRelativeU},
18 | {"PathCanonicalizeA", PathCanonicalizeU},
19 | {"PathCombineA", PathCombineU},
20 | {"PathAppendA", PathAppendU},
21 | { NULL }
22 | };
23 |
24 | BOOL STDAPICALLTYPE PathFileExistsU(
25 | LPCSTR pszPath
26 | )
27 | {
28 | BOOL ret;
29 | WCHAR_T_DEC(pszPath);
30 | WCHAR_T_CONV(pszPath);
31 | ret = PathFileExistsW(pszPath_w);
32 | WCHAR_T_FREE(pszPath);
33 | return ret;
34 | }
35 |
36 | LPSTR STDAPICALLTYPE PathFindFileNameU(
37 | LPCSTR pszPath
38 | )
39 | {
40 | LPCSTR ret = pszPath;
41 | while(pszPath && pszPath[0]) {
42 | char c0 = pszPath[0];
43 | char c1 = pszPath[1];
44 | if(
45 | (c0 == '\\' || c0 == '/' || c0 == ':')
46 | && (c1 && c1 != '\\' && c1 != '/')
47 | ) {
48 | ret = pszPath;
49 | }
50 | pszPath = CharNextU(pszPath);
51 | }
52 | return (LPSTR)ret;
53 | }
54 |
55 | BOOL STDAPICALLTYPE PathMatchSpecU(
56 | LPCSTR pszFile,
57 | LPCSTR pszSpec
58 | )
59 | {
60 | BOOL ret;
61 | WCHAR_T_DEC(pszFile);
62 | WCHAR_T_DEC(pszSpec);
63 | WCHAR_T_CONV(pszFile);
64 | WCHAR_T_CONV(pszSpec);
65 | ret = PathMatchSpecW(pszFile_w, pszSpec_w);
66 | WCHAR_T_FREE(pszFile);
67 | WCHAR_T_FREE(pszSpec);
68 | return ret;
69 | }
70 |
71 | HRESULT STDAPICALLTYPE PathMatchSpecExU(
72 | LPCSTR pszFile,
73 | LPCSTR pszSpec,
74 | DWORD dwFlags
75 | )
76 | {
77 | HRESULT ret;
78 | WCHAR_T_DEC(pszFile);
79 | WCHAR_T_DEC(pszSpec);
80 | WCHAR_T_CONV(pszFile);
81 | WCHAR_T_CONV(pszSpec);
82 | ret = PathMatchSpecExW(pszFile_w, pszSpec_w, dwFlags);
83 | WCHAR_T_FREE(pszFile);
84 | WCHAR_T_FREE(pszSpec);
85 | return ret;
86 | }
87 |
88 | BOOL STDAPICALLTYPE PathRemoveFileSpecU(
89 | LPSTR pszPath
90 | )
91 | {
92 | // Hey, let's re-write the function to also handle forward slashes
93 | // while we're at it!
94 | LPSTR newPath = PathFindFileNameU(pszPath);
95 | if((newPath) && (newPath != pszPath)) {
96 | newPath[0] = TEXT('\0');
97 | return 1;
98 | }
99 | return 0;
100 | }
101 |
102 | LPSTR STDAPICALLTYPE PathAddBackslashU(
103 | LPSTR pszPath
104 | )
105 | {
106 | if (pszPath == 0 || *pszPath == '\0') {
107 | return NULL;
108 | }
109 | size_t pszPathLen = strlen(pszPath);
110 | if (pszPath[pszPathLen - 1] != '\\' && pszPath[pszPathLen - 1] != '/') {
111 | pszPath[pszPathLen] = '\\';
112 | pszPath[pszPathLen + 1] = '\0';
113 | return (pszPath + pszPathLen + 1);
114 | } else {
115 | return (pszPath + pszPathLen);
116 | }
117 | }
118 |
119 | BOOL STDAPICALLTYPE PathIsDirectoryU(
120 | LPCSTR pszPath
121 | )
122 | {
123 | BOOL ret;
124 | WCHAR_T_DEC(pszPath);
125 | WCHAR_T_CONV(pszPath);
126 | ret = PathIsDirectoryW(pszPath_w);
127 | WCHAR_T_FREE(pszPath);
128 | return ret;
129 | }
130 |
131 | BOOL STDAPICALLTYPE PathIsRelativeU(
132 | LPCSTR pszPath
133 | )
134 | {
135 | return (*pszPath != '\\' && *pszPath != '/') && (*(pszPath + 1) != ':');
136 | }
137 |
138 | BOOL STDAPICALLTYPE PathCanonicalizeU(
139 | LPSTR pszBuf,
140 | LPCSTR pszPath
141 | )
142 | { // This function may get reimplemented https://doxygen.reactos.org/de/dff/dll_2win32_2shlwapi_2path_8c.html#aa31be5d2410fbd8564ec0da929354a0f
143 | wchar_t pszBuf_w[MAX_PATH];
144 | WCHAR_T_DEC(pszPath);
145 | WCHAR_T_CONV(pszPath);
146 | BOOL ret = PathCanonicalizeW(pszBuf_w, pszPath_w);
147 |
148 | WCHAR_T_FREE(pszPath);
149 |
150 | if (!ret) {
151 | return FALSE;
152 | }
153 |
154 | SetLastError(0);
155 | StringToUTF8(pszBuf, pszBuf_w, MAX_PATH);
156 | if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
157 | return FALSE;
158 | }
159 |
160 | return TRUE;
161 | }
162 |
163 | LPSTR STDAPICALLTYPE PathCombineU(
164 | LPSTR pszDest,
165 | LPCSTR pszDir,
166 | LPCSTR pszFile
167 | )
168 | {
169 | if (pszDest) {
170 | if (pszDir && pszDest != pszDir) {
171 | strcpy(pszDest, pszDir);
172 | }
173 | if (pszFile) {
174 | strcpy(PathIsRelativeU(pszFile) ? PathAddBackslashU(pszDest) : pszDest, pszFile);
175 | }
176 | char final_buffer[MAX_PATH];
177 | BOOL ret = PathCanonicalizeU(final_buffer, pszDest);
178 | if (!ret) {
179 | return NULL;
180 | }
181 | strcpy(pszDest, final_buffer);
182 | }
183 | return pszDest;
184 | }
185 |
186 | BOOL STDAPICALLTYPE PathAppendU(
187 | LPSTR pszPath,
188 | LPCSTR pszMore
189 | )
190 | {
191 | if (!PathCombineU(pszPath, pszPath, pszMore)) {
192 | return FALSE;
193 | }
194 | return TRUE;
195 | }
196 |
--------------------------------------------------------------------------------
/src/shlwapi_dll.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * shlwapi.dll functions.
7 | */
8 |
9 | #pragma once
10 |
11 | WRAPPER_DEC(BOOL STDAPICALLTYPE, PathFileExists,
12 | LPCSTR pszPath
13 | );
14 | #undef PathFileExists
15 | #define PathFileExists PathFileExistsU
16 |
17 | WRAPPER_DEC(LPSTR STDAPICALLTYPE, PathFindFileName,
18 | LPCSTR pszPath
19 | );
20 | #undef PathFindFileName
21 | #define PathFindFileName PathFindFileNameU
22 |
23 | WRAPPER_DEC(BOOL STDAPICALLTYPE, PathMatchSpec,
24 | LPCSTR pszFile,
25 | LPCSTR pszSpec
26 | );
27 | #undef PathMatchSpec
28 | #define PathMatchSpec PathMatchSpecU
29 |
30 | WRAPPER_DEC(HRESULT STDAPICALLTYPE, PathMatchSpecEx,
31 | LPCSTR pszFile,
32 | LPCSTR pszSpec,
33 | DWORD dwFlags
34 | );
35 | #undef PathMatchSpecEx
36 | #define PathMatchSpecEx PathMatchSpecExU
37 |
38 | WRAPPER_DEC(BOOL STDAPICALLTYPE, PathRemoveFileSpec,
39 | LPSTR pszPath
40 | );
41 | #undef PathRemoveFileSpec
42 | #define PathRemoveFileSpec PathRemoveFileSpecU
43 |
44 | WRAPPER_DEC(LPSTR STDAPICALLTYPE, PathAddBackslash,
45 | LPSTR pszPath
46 | );
47 | #undef PathAddBackslash
48 | #define PathAddBackslash PathAddBackslashU
49 |
50 | WRAPPER_DEC(BOOL STDAPICALLTYPE, PathIsDirectory,
51 | LPCSTR pszPath
52 | );
53 | #undef PathIsDirectory
54 | #define PathIsDirectory PathIsDirectoryU
55 |
56 | WRAPPER_DEC(BOOL STDAPICALLTYPE, PathIsRelative,
57 | LPCSTR pszPath
58 | );
59 | #undef PathIsRelative
60 | #define PathIsRelative PathIsRelativeU
61 |
62 | WRAPPER_DEC(BOOL STDAPICALLTYPE, PathCanonicalize,
63 | LPSTR pszBuf,
64 | LPCSTR pszPath
65 | );
66 | #undef PathCanonicalize
67 | #define PathCanonicalize PathCanonicalizeU
68 |
69 | WRAPPER_DEC(LPSTR STDAPICALLTYPE, PathCombine,
70 | LPSTR pszDest,
71 | LPCSTR pszDir,
72 | LPCSTR pszFile
73 | );
74 | #undef PathCombine
75 | #define PathCombine PathCombineU
76 |
77 | WRAPPER_DEC(BOOL STDAPICALLTYPE, PathAppend,
78 | LPSTR pszPath,
79 | LPCSTR pszMore
80 | );
81 | #undef PathAppend
82 | #define PathAppend PathAppendU
--------------------------------------------------------------------------------
/src/user32_dll.c:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * user32.dll functions.
7 | */
8 |
9 | // Some of the message-related functions here link straight to their W
10 | // counterparts. In these cases, both the A and W functions use the same
11 | // parameters, but since we only ever create Unicode windows, we also have
12 | // to ensure that Unicode is used on the entire message path.
13 | const w32u8_pair_t user32_pairs[] = {
14 | {"CallWindowProcA", CallWindowProcW},
15 | {"CharLowerA", CharLowerU},
16 | {"CharNextA", CharNextU},
17 | {"CreateDialogParamA", CreateDialogParamU},
18 | {"CreateWindowExA", CreateWindowExU},
19 | {"DefWindowProcA", DefWindowProcW},
20 | {"DialogBoxParamA", DialogBoxParamU},
21 | {"DrawTextA", DrawTextU},
22 | {"DrawTextExA", DrawTextExU},
23 | {"GetClassInfoA", GetClassInfoU},
24 | {"GetClassInfoExA", GetClassInfoExU},
25 | {"GetWindowLongA", GetWindowLongW},
26 | {"GetWindowLongPtrA", GetWindowLongPtrW},
27 | {"InsertMenuItemA", InsertMenuItemU},
28 | {"LoadStringA", LoadStringU},
29 | {"MessageBoxA", MessageBoxU},
30 | {"RegisterClassA", RegisterClassU},
31 | {"RegisterClassExA", RegisterClassExU},
32 | {"SetDlgItemTextA", SetDlgItemTextU},
33 | {"SetMenuItemInfoA", SetMenuItemInfoU},
34 | {"SetWindowLongA", SetWindowLongW},
35 | {"SetWindowLongPtrA", SetWindowLongPtrW},
36 | {"SetWindowTextA", SetWindowTextU},
37 | {"TabbedTextOutA", TabbedTextOutU},
38 | {"UnregisterClassA", UnregisterClassU},
39 | { NULL }
40 | };
41 |
42 | /// Structure conversions
43 | /// ---------------------
44 | // Needs to be a macro because these members unfortunately have different
45 | // offsets in WNDCLASS and WNDCLASSEX. Also, no reason to cast the individual
46 | // W and A types to the common WNDCLASS(EX) just to calm down the compiler.
47 | #define WndclassCopyBase(wc_dst, wc_src) \
48 | (wc_dst)->style = (wc_src)->style; \
49 | (wc_dst)->lpfnWndProc = (wc_src)->lpfnWndProc; \
50 | (wc_dst)->cbClsExtra = (wc_src)->cbClsExtra; \
51 | (wc_dst)->cbWndExtra = (wc_src)->cbWndExtra; \
52 | (wc_dst)->hInstance = (wc_src)->hInstance; \
53 | (wc_dst)->hIcon = (wc_src)->hIcon; \
54 | (wc_dst)->hCursor = (wc_src)->hCursor; \
55 | (wc_dst)->hbrBackground = (wc_src)->hbrBackground;
56 |
57 | #define WndclassExCopyBase(wc_dst, wc_src) \
58 | (wc_dst)->cbSize = (wc_src)->cbSize; \
59 | (wc_dst)->hIconSm = (wc_src)->hIconSm;
60 |
61 | #define WndclassAToW(w, a) \
62 | size_t lpszClassName_len = strlen((a)->lpszClassName) + 1; \
63 | VLA(wchar_t, lpszClassName_w, lpszClassName_len); \
64 | RESID_DEC(lpszMenuName); \
65 | (w)->lpszClassName = StringToUTF16_VLA(lpszClassName_w, (a)->lpszClassName, lpszClassName_len); \
66 | RESID_CONV(lpszMenuName, (a)->lpszMenuName); \
67 | (w)->lpszMenuName = lpszMenuName_w; \
68 | WndclassCopyBase((w), (a))
69 |
70 | #define WndclassWClean(a) \
71 | VLA_FREE(lpszClassName_w); \
72 | RESID_FREE(lpszMenuName, (a)->lpszMenuName);
73 | /// ---------------------
74 |
75 | LPSTR WINAPI CharLowerU(
76 | LPSTR lpsz
77 | )
78 | {
79 | if ((uintptr_t)lpsz & ~(uintptr_t)0xFFFF) {
80 | WCHAR_T_DEC(lpsz);
81 | WCHAR_T_CONV(lpsz);
82 | CharLowerW(lpsz_w);
83 | WideCharToMultiByte(CP_UTF8, 0, lpsz_w, -1, lpsz, strlen(lpsz), 0, FALSE);
84 | WCHAR_T_FREE(lpsz);
85 | return lpsz;
86 | }
87 | else {
88 | // TODO: implement
89 | return CharLowerA(lpsz);
90 | }
91 | }
92 |
93 | LPCSTR WINAPI CharNextU(
94 | LPCSTR lpsz
95 | )
96 | {
97 | LPCSTR ret;
98 | extern UINT fallback_codepage;
99 |
100 | if(lpsz == NULL || *lpsz == '\0') {
101 | ret = lpsz;
102 | }
103 | if ((lpsz[0] & 0xe0) == 0xc0 && (lpsz[1] & 0xc0) == 0x80) {
104 | ret = lpsz + 2;
105 | }
106 | else if ((lpsz[0] & 0xf0) == 0xe0 && (lpsz[1] & 0xc0) == 0x80 && (lpsz[2] & 0xc0) == 0x80) {
107 | ret = lpsz + 3;
108 | }
109 | else if ((lpsz[0] & 0xf8) == 0xf0 && (lpsz[1] & 0xc0) == 0x80 && (lpsz[2] & 0xc0) == 0x80 && (lpsz[3] & 0xc0) == 0x80) {
110 | ret = lpsz + 4;
111 | }
112 | else if(IsDBCSLeadByteEx(fallback_codepage, lpsz[0])) {
113 | size_t lpsz_len = strlen(lpsz);
114 | if(lpsz_len < 2) {
115 | ret = lpsz + 1;
116 | } else {
117 | ret = lpsz + 2;
118 | }
119 | }
120 | else {
121 | ret = lpsz + 1;
122 | }
123 | return ret;
124 | }
125 |
126 | HWND WINAPI CreateDialogParamU(
127 | HINSTANCE hInstance,
128 | RESID lpTemplateRes,
129 | HWND hWndParent,
130 | DLGPROC lpDialogFunc,
131 | LPARAM dwInitParam
132 | )
133 | {
134 | HWND ret;
135 | RESID_DEC(lpTemplateRes);
136 | RESID_CONV(lpTemplateRes, lpTemplateRes);
137 | ret = CreateDialogParamW(
138 | hInstance, lpTemplateRes_w, hWndParent, lpDialogFunc, dwInitParam
139 | );
140 | RESID_FREE(lpTemplateRes, lpTemplateRes);
141 | return ret;
142 | }
143 |
144 | HWND WINAPI CreateWindowExU(
145 | DWORD dwExStyle,
146 | LPCSTR lpClassName,
147 | LPCSTR lpWindowName,
148 | DWORD dwStyle,
149 | int X,
150 | int Y,
151 | int nWidth,
152 | int nHeight,
153 | HWND hWndParent,
154 | HMENU hMenu,
155 | HINSTANCE hInstance,
156 | LPVOID lpParam
157 | )
158 | {
159 | HWND ret;
160 | RESID_DEC(lpClassName);
161 | WCHAR_T_DEC(lpWindowName);
162 | RESID_CONV(lpClassName, lpClassName);
163 | WCHAR_T_CONV(lpWindowName);
164 |
165 | ret = CreateWindowExW(
166 | dwExStyle, lpClassName_w, lpWindowName_w, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu,
167 | hInstance, lpParam
168 | );
169 | RESID_FREE(lpClassName, lpClassName);
170 | WCHAR_T_FREE(lpWindowName);
171 | return ret;
172 | }
173 |
174 | INT_PTR WINAPI DialogBoxParamU(
175 | HINSTANCE hInstance,
176 | RESID lpTemplateRes,
177 | HWND hWndParent,
178 | DLGPROC lpDialogFunc,
179 | LPARAM dwInitParam
180 | )
181 | {
182 | INT_PTR ret;
183 | RESID_DEC(lpTemplateRes);
184 | RESID_CONV(lpTemplateRes, lpTemplateRes);
185 | ret = DialogBoxParamW(
186 | hInstance, lpTemplateRes_w, hWndParent, lpDialogFunc, dwInitParam
187 | );
188 | RESID_FREE(lpTemplateRes, lpTemplateRes);
189 | return ret;
190 | }
191 |
192 | int WINAPI DrawTextU(
193 | HDC hdc,
194 | LPCSTR lpchText,
195 | int cchText,
196 | LPRECT lprc,
197 | UINT format
198 | )
199 | {
200 | int ret;
201 | FixedLengthStringConvert(lpchText, cchText);
202 | ret = DrawTextW(hdc, lpchText_w, lpchText_w_len, lprc, format);
203 | WCHAR_T_FREE(lpchText);
204 | return ret;
205 | }
206 |
207 | int WINAPI DrawTextExU(
208 | HDC hdc,
209 | LPSTR lpchText,
210 | int cchText,
211 | LPRECT lprc,
212 | UINT format,
213 | LPDRAWTEXTPARAMS lpdtp
214 | ) {
215 | int ret;
216 | FixedLengthStringConvert(lpchText, cchText);
217 | ret = DrawTextExW(hdc, lpchText_w, lpchText_w_len, lprc, format, lpdtp);
218 | WCHAR_T_FREE(lpchText);
219 | return ret;
220 | }
221 |
222 | BOOL WINAPI GetClassInfoU(
223 | HINSTANCE hInstance,
224 | LPCSTR lpClassName,
225 | LPWNDCLASSA wc
226 | )
227 | {
228 | WNDCLASSEXA wcex;
229 | BOOL ret = GetClassInfoExU(hInstance, lpClassName, &wcex);
230 | if(ret) {
231 | WndclassCopyBase(wc, &wcex);
232 | wc->lpszClassName = lpClassName;
233 | }
234 | return ret;
235 | }
236 |
237 | BOOL WINAPI GetClassInfoExU(
238 | HINSTANCE hInstance,
239 | LPCSTR lpClassName,
240 | LPWNDCLASSEXA wcex_a
241 | )
242 | {
243 | BOOL ret;
244 | WNDCLASSEXW wcex_w = {0};
245 | wcex_w.cbSize = sizeof(WNDCLASSEXW);
246 | RESID_DEC(lpClassName);
247 | RESID_CONV(lpClassName, lpClassName);
248 | ret = GetClassInfoExW(hInstance, lpClassName_w, &wcex_w);
249 | if(ret) {
250 | WndclassCopyBase(wcex_a, &wcex_w);
251 | WndclassExCopyBase(wcex_a, &wcex_w);
252 | wcex_a->lpszClassName = lpClassName;
253 | }
254 | RESID_FREE(lpClassName, lpClassName);
255 | return ret;
256 | }
257 |
258 | BOOL WINAPI InsertMenuItemU(
259 | HMENU hmenu,
260 | UINT item,
261 | BOOL fByPosition,
262 | LPCMENUITEMINFOA lpmi
263 | )
264 | {
265 | BOOL ret;
266 | MENUITEMINFOW lpmi_w;
267 | wchar_t *str_w = NULL;
268 | if(lpmi) {
269 | memcpy(&lpmi_w, lpmi, sizeof(MENUITEMINFOW));
270 | if(lpmi->fMask & MIIM_TYPE || lpmi->fMask & MIIM_STRING) {
271 | // yes, [cch] is ignored
272 | const char *str_local = lpmi->dwTypeData;
273 | WCHAR_T_DECA(str_local);
274 | WCHAR_T_CONV(str_local);
275 | str_w = lpmi_w.dwTypeData = str_local_w;
276 | }
277 | } else {
278 | ZeroMemory(&lpmi_w, sizeof(MENUITEMINFOW));
279 | }
280 | ret = InsertMenuItemW(hmenu, item, fByPosition, &lpmi_w);
281 | w32u8_freea(str_w);
282 | return ret;
283 | }
284 |
285 | BOOL WINAPI SetMenuItemInfoU(
286 | HMENU hmenu,
287 | UINT item,
288 | BOOL fByPosition,
289 | LPCMENUITEMINFOA lpmi
290 | )
291 | {
292 | BOOL ret;
293 | MENUITEMINFOW lpmi_w;
294 | wchar_t *str_w = NULL;
295 | if(lpmi) {
296 | memcpy(&lpmi_w, lpmi, sizeof(MENUITEMINFOW));
297 | if(lpmi->fMask & MIIM_TYPE || lpmi->fMask & MIIM_STRING) {
298 | // yes, [cch] is ignored
299 | const char *str_local = lpmi->dwTypeData;
300 | WCHAR_T_DECA(str_local);
301 | WCHAR_T_CONV(str_local);
302 | str_w = lpmi_w.dwTypeData = str_local_w;
303 | }
304 | } else {
305 | ZeroMemory(&lpmi_w, sizeof(MENUITEMINFOW));
306 | }
307 | ret = SetMenuItemInfoW(hmenu, item, fByPosition, &lpmi_w);
308 | w32u8_freea(str_w);
309 | return ret;
310 | }
311 |
312 | int WINAPI LoadStringU(
313 | HINSTANCE hInstance,
314 | UINT uID,
315 | LPSTR lpBuffer,
316 | int cchBufferMax
317 | )
318 | {
319 | int ret = -1;
320 | // Since LoadStringW() could also return double-null-terminated strings
321 | // (see http://blogs.msdn.com/b/oldnewthing/archive/2009/10/09/9904648.aspx),
322 | // this indeed is not as easy as simply calling LoadStringW() and
323 | // converting the result.
324 | HRSRC hrsrc = FindResourceW(hInstance,
325 | MAKEINTRESOURCEW((LOWORD(uID) >> 4) + 1), (LPWSTR)RT_STRING
326 | );
327 | IMAGE_RESOURCE_DIR_STRING_U *str_res = LoadResource(hInstance, hrsrc);
328 | if(hrsrc && str_res && cchBufferMax) {
329 | unsigned int id = uID & 0x000f;
330 | while(id--) {
331 | str_res = (IMAGE_RESOURCE_DIR_STRING_U*)(
332 | ((LPCWSTR)str_res) + str_res->Length + 1
333 | );
334 | }
335 | ret = StringToMBFixed(
336 | lpBuffer, str_res->NameString, cchBufferMax, str_res->Length
337 | );
338 | lpBuffer[ret] = 0;
339 | }
340 | return ret;
341 | }
342 |
343 | int WINAPI MessageBoxU_Generic(
344 | w32u8_MessageBoxFunc_t *func,
345 | HWND hWnd,
346 | LPCSTR lpText,
347 | LPCSTR lpCaption,
348 | UINT uType
349 | )
350 | {
351 | int ret;
352 | WCHAR_T_DEC(lpText);
353 | WCHAR_T_DEC(lpCaption);
354 | WCHAR_T_CONV(lpText);
355 | WCHAR_T_CONV(lpCaption);
356 | ret = func(hWnd, lpText_w, lpCaption_w, uType);
357 | WCHAR_T_FREE(lpText);
358 | WCHAR_T_FREE(lpCaption);
359 | return ret;
360 | }
361 |
362 | int WINAPI MessageBoxU(
363 | HWND hWnd,
364 | LPCSTR lpText,
365 | LPCSTR lpCaption,
366 | UINT uType
367 | )
368 | {
369 | return MessageBoxU_Generic(MessageBoxW, hWnd, lpText, lpCaption, uType);
370 | }
371 |
372 |
373 | ATOM WINAPI RegisterClassU(
374 | CONST WNDCLASSA *lpWndClass
375 | )
376 | {
377 | ATOM ret;
378 | WNDCLASSW WndClassW;
379 | WndclassAToW(&WndClassW, lpWndClass);
380 | ret = RegisterClassW(&WndClassW);
381 | WndclassWClean(lpWndClass);
382 | return ret;
383 | }
384 |
385 | ATOM WINAPI RegisterClassExU(
386 | CONST WNDCLASSEXA *lpWndClass
387 | )
388 | {
389 | ATOM ret;
390 | WNDCLASSEXW WndClassW;
391 | WndclassAToW(&WndClassW, lpWndClass);
392 | WndclassExCopyBase(&WndClassW, lpWndClass);
393 | ret = RegisterClassExW(&WndClassW);
394 | WndclassWClean(lpWndClass);
395 | return ret;
396 | }
397 |
398 | BOOL WINAPI SetDlgItemTextU(
399 | HWND hDlg,
400 | int nIDDlgItem,
401 | LPCSTR lpString
402 | )
403 | {
404 | BOOL ret;
405 | WCHAR_T_DEC(lpString);
406 | WCHAR_T_CONV(lpString);
407 | ret = SetDlgItemTextW(hDlg, nIDDlgItem, lpString_w);
408 | WCHAR_T_FREE(lpString);
409 | return ret;
410 | }
411 |
412 | BOOL WINAPI SetWindowTextU(
413 | HWND hWnd,
414 | LPCSTR lpString
415 | )
416 | {
417 | BOOL ret;
418 | WCHAR_T_DEC(lpString);
419 | WCHAR_T_CONV(lpString);
420 | ret = SetWindowTextW(hWnd, lpString_w);
421 | WCHAR_T_FREE(lpString);
422 | return ret;
423 | }
424 |
425 | LONG WINAPI TabbedTextOutU(
426 | HDC hdc,
427 | int x,
428 | int y,
429 | LPCSTR lpString,
430 | int chCount,
431 | int nTabPositions,
432 | CONST INT *lpnTabStopPositions,
433 | int nTabOrigin
434 | )
435 | {
436 | BOOL ret;
437 | FixedLengthStringConvert(lpString, chCount);
438 | ret = TabbedTextOutW(
439 | hdc, x, y, lpString_w, lpString_w_len,
440 | nTabPositions, lpnTabStopPositions, nTabOrigin
441 | );
442 | WCHAR_T_FREE(lpString);
443 | return ret;
444 | }
445 |
446 | BOOL WINAPI UnregisterClassU(
447 | LPCSTR lpClassName,
448 | HINSTANCE hInstance
449 | )
450 | {
451 | BOOL ret;
452 | RESID_DEC(lpClassName);
453 | RESID_CONV(lpClassName, lpClassName);
454 | ret = UnregisterClassW(lpClassName_w, hInstance);
455 | RESID_FREE(lpClassName, lpClassName);
456 | return ret;
457 | }
458 |
--------------------------------------------------------------------------------
/src/user32_dll.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * user32.dll functions.
7 | */
8 |
9 | #pragma once
10 |
11 | #undef CallWindowProc
12 | #define CallWindowProc CallWindowProcW
13 |
14 | WRAPPER_DEC(LPCSTR WINAPI, CharNext,
15 | LPCSTR lpsz
16 | );
17 | #undef CharNext
18 | #define CharNext CharNextU
19 |
20 | WRAPPER_DEC(LPSTR WINAPI, CharLower,
21 | LPSTR lpsz
22 | );
23 | #undef CharLower
24 | #define CharLower CharLowerU
25 |
26 | WRAPPER_DEC(HWND WINAPI, CreateDialogParam,
27 | HINSTANCE hInstance,
28 | RESID lpTemplateRes,
29 | HWND hWndParent,
30 | DLGPROC lpDialogFunc,
31 | LPARAM dwInitParam
32 | );
33 | #undef CreateDialogParam
34 | #define CreateDialogParam CreateDialogParamU
35 |
36 | WRAPPER_DEC(HWND WINAPI, CreateWindowEx,
37 | DWORD dwExStyle,
38 | LPCSTR lpClassName,
39 | LPCSTR lpWindowName,
40 | DWORD dwStyle,
41 | int X,
42 | int Y,
43 | int nWidth,
44 | int nHeight,
45 | HWND hWndParent,
46 | HMENU hMenu,
47 | HINSTANCE hInstance,
48 | LPVOID lpParam
49 | );
50 | #undef CreateWindowEx
51 | #define CreateWindowEx CreateWindowExU
52 |
53 | // Yep, both original functions use the same parameters
54 | #undef DefWindowProc
55 | #define DefWindowProc DefWindowProcW
56 |
57 | WRAPPER_DEC(INT_PTR WINAPI, DialogBoxParam,
58 | HINSTANCE hInstance,
59 | RESID lpTemplateRes,
60 | HWND hWndParent,
61 | DLGPROC lpDialogFunc,
62 | LPARAM dwInitParam
63 | );
64 | #undef DialogBoxParam
65 | #define DialogBoxParam DialogBoxParamU
66 |
67 | WRAPPER_DEC(int WINAPI, DrawText,
68 | HDC hdc,
69 | LPCSTR lpchText,
70 | int cchText,
71 | LPRECT lprc,
72 | UINT format
73 | );
74 | #undef DrawText
75 | #define DrawText DrawTextU
76 |
77 | WRAPPER_DEC(int WINAPI, DrawTextEx,
78 | HDC hdc,
79 | LPSTR lpchText,
80 | int cchText,
81 | LPRECT lprc,
82 | UINT format,
83 | LPDRAWTEXTPARAMS lpdtp
84 | );
85 | #undef DrawTextEx
86 | #define DrawTextEx DrawTextExU
87 |
88 | WRAPPER_DEC(BOOL WINAPI, GetClassInfo,
89 | HINSTANCE hInstance,
90 | LPCSTR lpClassName,
91 | LPWNDCLASSA lpWndClass
92 | );
93 | #undef GetClassInfo
94 | #define GetClassInfo GetClassInfoU
95 |
96 | WRAPPER_DEC(BOOL WINAPI, GetClassInfoEx,
97 | HINSTANCE hInstance,
98 | LPCSTR lpszClass,
99 | LPWNDCLASSEXA lpwcx
100 | );
101 | #undef GetClassInfoEx
102 | #define GetClassInfoEx GetClassInfoExU
103 |
104 | // These (and SetWindowLong(Ptr) below) are necessary because Windows otherwise
105 | // silently converts certain text parameters for window procedures to ANSI.
106 | // (see http://blogs.msdn.com/b/oldnewthing/archive/2003/12/01/55900.aspx)
107 | #undef GetWindowLong
108 | #undef GetWindowLongPtr
109 | #define GetWindowLong GetWindowLongW
110 | #define GetWindowLongPtr GetWindowLongPtrW
111 |
112 | WRAPPER_DEC(BOOL WINAPI, InsertMenuItem,
113 | HMENU hmenu,
114 | UINT item,
115 | BOOL fByPosition,
116 | LPCMENUITEMINFOA lpmi
117 | );
118 | #undef InsertMenuItem
119 | #define InsertMenuItem InsertMenuItemU
120 |
121 | WRAPPER_DEC(int WINAPI, LoadString,
122 | HINSTANCE hInstance,
123 | UINT uID,
124 | LPSTR lpBuffer,
125 | int cchBufferMax
126 | );
127 | #undef LoadString
128 | #define LoadString LoadStringU
129 |
130 | typedef int WINAPI w32u8_MessageBoxFunc_t(
131 | HWND hWnd,
132 | LPCWSTR lpText,
133 | LPCWSTR lpCaption,
134 | UINT uType
135 | );
136 |
137 | int WINAPI MessageBoxU_Generic(
138 | w32u8_MessageBoxFunc_t *func,
139 | HWND hWnd,
140 | LPCSTR lpText,
141 | LPCSTR lpCaption,
142 | UINT uType
143 | );
144 |
145 | #if ISOLATION_AWARE_ENABLED
146 | # define IsolationAwareMessageBoxU(hWnd, lpText, lpCaption, uType) \
147 | MessageBoxU_Generic(IsolationAwareMessageBoxW, (hWnd), (lpText), (lpCaption), (uType))
148 | # undef IsolationAwareMessageBox
149 | # define IsolationAwareMessageBox IsolationAwareMessageBoxU
150 | #endif
151 |
152 | WRAPPER_DEC(int WINAPI, MessageBox,
153 | HWND hWnd,
154 | LPCSTR lpText,
155 | LPCSTR lpCaption,
156 | UINT uType
157 | );
158 | #undef MessageBox
159 | #define MessageBox MessageBoxU
160 |
161 | WRAPPER_DEC(ATOM WINAPI, RegisterClass,
162 | CONST WNDCLASSA *lpWndClass
163 | );
164 | #undef RegisterClass
165 | #define RegisterClass RegisterClassU
166 |
167 | WRAPPER_DEC(ATOM WINAPI, RegisterClassEx,
168 | CONST WNDCLASSEXA *lpWndClass
169 | );
170 | #undef RegisterClassEx
171 | #define RegisterClassEx RegisterClassExU
172 |
173 | WRAPPER_DEC(BOOL WINAPI, SetDlgItemText,
174 | HWND hDlg,
175 | int nIDDlgItem,
176 | LPCSTR lpString
177 | );
178 | #undef SetDlgItemText
179 | #define SetDlgItemText SetDlgItemTextU
180 |
181 | WRAPPER_DEC(BOOL WINAPI, SetMenuItemInfo,
182 | HMENU hmenu,
183 | UINT item,
184 | BOOL fByPositon,
185 | LPCMENUITEMINFOA lpmi
186 | );
187 | #undef SetMenuItemInfo
188 | #define SetMenuItemInfo SetMenuItemInfoU
189 |
190 | #undef SetWindowLong
191 | #undef SetWindowLongPtr
192 | #define SetWindowLong SetWindowLongW
193 | #define SetWindowLongPtr SetWindowLongPtrW
194 |
195 | WRAPPER_DEC(BOOL WINAPI, SetWindowText,
196 | HWND hWnd,
197 | LPCSTR lpString
198 | );
199 | #undef SetWindowText
200 | #define SetWindowText SetWindowTextU
201 |
202 | WRAPPER_DEC(LONG WINAPI, TabbedTextOut,
203 | HDC hdc,
204 | int x,
205 | int y,
206 | LPCSTR lpString,
207 | int chCount,
208 | int nTabPositions,
209 | CONST INT *lpnTabStopPositions,
210 | int nTabOrigin
211 | );
212 | #undef TabbedTextOut
213 | #define TabbedTextOut TabbedTextOutU
214 |
215 | WRAPPER_DEC(BOOL WINAPI, UnregisterClass,
216 | LPCSTR lpClassName,
217 | HINSTANCE hInstance
218 | );
219 | #undef UnregisterClass
220 | #define UnregisterClass UnregisterClassU
221 |
--------------------------------------------------------------------------------
/src/utf.c:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * Unicode conversion functions.
7 | */
8 |
9 | size_t StringToUTF16(wchar_t *str_w, const char *str_mb, size_t str_len)
10 | {
11 | if(!str_mb || !str_len) {
12 | return 0;
13 | }
14 | if(str_len == -1) {
15 | str_len = strlen(str_mb) + 1;
16 | }
17 | size_t str_len_w = str_w ? str_len : 0;
18 | return MultiByteToWideCharU(0, 0, str_mb, str_len, str_w, str_len_w);
19 | }
20 |
21 | wchar_t* StringToUTF16_VLA(wchar_t *str_w, const char *str_mb, size_t str_len)
22 | {
23 | if(str_mb) {
24 | StringToUTF16(str_w, str_mb, str_len);
25 | return str_w;
26 | }
27 | return NULL;
28 | }
29 |
30 | size_t StringToUTF8(char *str_utf8, const wchar_t *str_w, size_t str_utf8_len)
31 | {
32 | int ret = WideCharToMultiByte(
33 | CP_UTF8, 0, str_w, -1, str_utf8, str_utf8_len, NULL, NULL
34 | );
35 | return str_w ? ret - 1 : ret;
36 | }
37 |
38 | size_t StringToMBFixed(char *str_mb, const wchar_t *str_w, size_t str_mb_len, size_t str_w_len)
39 | {
40 | BOOL invalid = FALSE;
41 | extern UINT fallback_codepage;
42 | int ret = WideCharToMultiByte(
43 | fallback_codepage, WC_NO_BEST_FIT_CHARS,
44 | str_w, str_w_len, str_mb, str_mb_len, NULL, &invalid
45 | );
46 | if(!ret || invalid) {
47 | ret = WideCharToMultiByte(
48 | CP_UTF8, 0, str_w, str_w_len, str_mb, str_mb_len, NULL, NULL
49 | );
50 | }
51 | return ret;
52 | }
53 |
54 | UINT CharToUTF16(UINT c_mb)
55 | {
56 | // Following the scheme used in existing DBCS code and Wine, I *guess*
57 | // this is how you would be supposed to pass UTF-8 multibyte characters
58 | // to GetGlyphOutlineA? Not that there's any code out there that actually
59 | // *does* this (without being forcefully hacked into it, that is, and
60 | // we'd need some test cases for that).
61 | // Anyway, this feels cleaner than using IsDBCSLeadByteEx().
62 | char c_a[sizeof(UINT) + 1] = { 0 };
63 | wchar_t c_w[sizeof(c_a)];
64 | UINT codepoint = 0;
65 |
66 | char *p = c_a;
67 | size_t i;
68 | for (i = 0; i < sizeof(UINT); i++) {
69 | size_t shift = (sizeof(UINT) - 1 - i) * 8;
70 | char byte = (c_mb & (0xFF << shift)) >> shift;
71 | if (byte) {
72 | *p++ = byte;
73 | }
74 | }
75 | StringToUTF16(c_w, c_a, sizeof(c_a));
76 |
77 | /**
78 | * The nondescript "character" parameter of GetGlyphOutlineW() could
79 | * either be interpreted as an UTF-32 code point or as UTF-16,
80 | * using... well, *some* kind of byte order for surrogate pairs?
81 | * As of now, it doesn't actually matter – the function does not
82 | * support code points above U+FFFF, both interpretations are
83 | * identical for code points below that, and nobody on the Internet
84 | * seems to have any idea on how to use the W function for anything
85 | * outside the Basic Multilingual Plane.
86 | * Given the fact that this function has existed in this form (and
87 | * probably hasn't been significantly altered) since Windows 3.1,
88 | * where UINT was still a 16-bit type, this is hardly surprising.
89 | *
90 | * Nevertheless, I'd better do *something* here since the code would
91 | * otherwise be obviously wrong, and the code point interpretation
92 | * appears to be the more sensible one.
93 | * (Which, given Microsoft's track record with Unicode, means
94 | * that it's exactly the *wrong* thing to do...)
95 | */
96 | if (c_w[0] < 0xD800 || c_w[0] >= 0xE000) {
97 | codepoint = c_w[0];
98 | }
99 | else {
100 | codepoint = ((c_w[0] - 0xD800) << 10) + (c_w[1] - 0xDC00) + 0x10000;
101 | }
102 |
103 | return codepoint;
104 | }
105 |
106 | char* EnsureUTF8(const char *str, size_t str_len)
107 | {
108 | char *str_utf8 = NULL;
109 | if(str) {
110 | size_t str_w_len = str_len + 1;
111 | size_t str_utf8_len = str_w_len * UTF8_MUL;
112 | VLA(wchar_t, str_w, str_w_len);
113 | str_utf8 = (char*)malloc(str_utf8_len);
114 | ZeroMemory(str_w, str_w_len * sizeof(wchar_t));
115 | ZeroMemory(str_utf8, str_utf8_len * sizeof(char));
116 | WCHAR_T_CONV(str);
117 | StringToUTF8(str_utf8, str_w, str_utf8_len);
118 | VLA_FREE(str_w);
119 | }
120 | return str_utf8;
121 | }
122 |
--------------------------------------------------------------------------------
/src/utf.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * Unicode conversion functions.
7 | */
8 |
9 | #pragma once
10 |
11 | // Maximum length of the largest wchar_t in UTF-8
12 | #define UTF8_MUL 4
13 |
14 | /**
15 | * Converts [str_len] characters of the "narrow" string [str_mb] to UTF-16.
16 | * Input can either be in UTF-8 or the fallback codepage specified by a call
17 | * to w32u8_set_fallback_codepage().
18 | *
19 | * [str_len] can be -1 - this assumes that [str_mb] is null-terminated and
20 | * calculates the length automatically.
21 | *
22 | * If [str_w] is NULL, the function returns the required
23 | * length, in wide characters, for a buffer that fits [str_mb].
24 | */
25 | size_t StringToUTF16(wchar_t *str_w, const char *str_mb, size_t str_len);
26 |
27 | // StringToUTF16 for assigning VLA strings to optional structure members.
28 | // Returns NULL if [str_mb] is NULL. Note that [str_w] still has to be freed
29 | // by the caller in this case - which, however, it needs to do anyway if
30 | // [str_mb] (and thus, [str_w]) point to a valid string.
31 | wchar_t* StringToUTF16_VLA(wchar_t *str_w, const char *str_mb, size_t str_len);
32 |
33 | // Converts a null-terminated UTF-16 string to UTF-8 and returns the
34 | // converted size excluding the terminating null character.
35 | // [str_utf8_len] takes the size of [str_utf8] in bytes.
36 | size_t StringToUTF8(char *str_utf8, const wchar_t *str_w, size_t str_utf8_len);
37 |
38 | // Converts the fixed-length string [str_w] to the fallback codepage or, if
39 | // that conversion failed, to UTF-8. Useful in cases where applications
40 | // concatenate strings returned from Windows functions with hardcoded strings
41 | // in the application's native encoding.
42 | size_t StringToMBFixed(char *str_mb, const wchar_t *str_w, size_t str_mb_len, size_t str_w_len);
43 |
44 | // Converts the "narrow" character [c_mb] to UTF-16.
45 | // The input and output characters are in the format expected by GetGlyphOutline.
46 | // Input can either be in UTF-8 or the fallback codepage specified by a call
47 | // to w32u8_set_fallback_codepage().
48 | UINT CharToUTF16(UINT c_mb);
49 |
50 | // Returns [str] in UTF-8.
51 | // Return value has to be free()d by the caller!
52 | char* EnsureUTF8(const char *str, size_t str_len);
53 |
--------------------------------------------------------------------------------
/src/version_dll.c:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * version.dll functions.
7 | */
8 |
9 | /// Functions not available before Vista
10 | /// ------------------------------------
11 | typedef DWORD WINAPI DLL_FUNC_TYPE(version, GetFileVersionInfoSizeExW)(
12 | DWORD dwFlags,
13 | LPCWSTR lpstrFilename,
14 | LPDWORD lpdwHandle
15 | );
16 |
17 | typedef BOOL WINAPI DLL_FUNC_TYPE(version, GetFileVersionInfoExW)(
18 | DWORD dwFlags,
19 | LPCWSTR lpstrFilename,
20 | DWORD dwHandle,
21 | DWORD dwLen,
22 | LPVOID lpData
23 | );
24 |
25 | DLL_FUNC_DEF(version, GetFileVersionInfoSizeExW);
26 | DLL_FUNC_DEF(version, GetFileVersionInfoExW);
27 | /// ------------------------------------
28 |
29 | const w32u8_pair_t version_pairs[] = {
30 | {"GetFileVersionInfoA", GetFileVersionInfoU},
31 | {"GetFileVersionInfoExA", GetFileVersionInfoExU},
32 | {"GetFileVersionInfoSizeA", GetFileVersionInfoSizeU},
33 | {"GetFileVersionInfoSizeExA", GetFileVersionInfoSizeExU},
34 | { NULL }
35 | };
36 |
37 | BOOL WINAPI GetFileVersionInfoU(
38 | LPCSTR lpstrFilename,
39 | DWORD dwHandle,
40 | DWORD dwLen,
41 | LPVOID lpData
42 | )
43 | {
44 | BOOL ret;
45 | WCHAR_T_DEC(lpstrFilename);
46 | WCHAR_T_CONV(lpstrFilename);
47 | ret = GetFileVersionInfoW(lpstrFilename_w, dwHandle, dwLen, lpData);
48 | WCHAR_T_FREE(lpstrFilename);
49 | return ret;
50 | }
51 |
52 | BOOL WINAPI GetFileVersionInfoExU(
53 | DWORD dwFlags,
54 | LPCSTR lpstrFilename,
55 | DWORD dwHandle,
56 | DWORD dwLen,
57 | LPVOID lpData
58 | )
59 | {
60 | BOOL ret;
61 | WCHAR_T_DEC(lpstrFilename);
62 | WCHAR_T_CONV(lpstrFilename);
63 | DLL_FUNC_CALL(version, GetFileVersionInfoExW,
64 | dwFlags, lpstrFilename_w, dwHandle, dwLen, lpData
65 | );
66 | WCHAR_T_FREE(lpstrFilename);
67 | return ret;
68 | }
69 |
70 | DWORD WINAPI GetFileVersionInfoSizeU(
71 | LPCSTR lpstrFilename,
72 | LPDWORD lpdwHandle
73 | )
74 | {
75 | BOOL ret;
76 | WCHAR_T_DEC(lpstrFilename);
77 | WCHAR_T_CONV(lpstrFilename);
78 | ret = GetFileVersionInfoSizeW(lpstrFilename_w, lpdwHandle);
79 | WCHAR_T_FREE(lpstrFilename);
80 | return ret;
81 | }
82 |
83 | DWORD WINAPI GetFileVersionInfoSizeExU(
84 | DWORD dwFlags,
85 | LPCSTR lpstrFilename,
86 | LPDWORD lpdwHandle
87 | )
88 | {
89 | DWORD ret;
90 | WCHAR_T_DEC(lpstrFilename);
91 | WCHAR_T_CONV(lpstrFilename);
92 | DLL_FUNC_CALL(version, GetFileVersionInfoSizeExW,
93 | dwFlags, lpstrFilename_w, lpdwHandle
94 | );
95 | WCHAR_T_FREE(lpstrFilename);
96 | return ret;
97 | }
98 |
99 | void version_init(void)
100 | {
101 | HMODULE version = GetModuleHandleA("version.dll");
102 | if(version) {
103 | DLL_FUNC_GET(version, GetFileVersionInfoSizeExW);
104 | DLL_FUNC_GET(version, GetFileVersionInfoExW);
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/version_dll.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * version.dll functions.
7 | */
8 |
9 | #pragma once
10 |
11 | WRAPPER_DEC(BOOL WINAPI, GetFileVersionInfo,
12 | LPCSTR lpstrFilename,
13 | DWORD dwHandle,
14 | DWORD dwLen,
15 | LPVOID lpData
16 | );
17 | #undef GetFileVersionInfo
18 | #define GetFileVersionInfo GetFileVersionInfoU
19 |
20 | WRAPPER_DEC(BOOL WINAPI, GetFileVersionInfoEx,
21 | DWORD dwFlags,
22 | LPCSTR lpstrFilename,
23 | DWORD dwHandle,
24 | DWORD dwLen,
25 | LPVOID lpData
26 | );
27 | #undef GetFileVersionInfoEx
28 | #define GetFileVersionInfoEx GetFileVersionInfoExU
29 |
30 | WRAPPER_DEC(DWORD WINAPI, GetFileVersionInfoSize,
31 | LPCSTR lpstrFilename,
32 | LPDWORD lpdwHandle
33 | );
34 | #undef GetFileVersionInfoSize
35 | #define GetFileVersionInfoSize GetFileVersionInfoSizeU
36 |
37 | WRAPPER_DEC(DWORD WINAPI, GetFileVersionInfoSizeEx,
38 | DWORD dwFlags,
39 | LPCSTR lpstrFilename,
40 | LPDWORD lpdwHandle
41 | );
42 | #undef GetFileVersionInfoSizeEx
43 | #define GetFileVersionInfoSizeEx GetFileVersionInfoSizeExU
44 |
45 | void version_init(void);
46 |
--------------------------------------------------------------------------------
/src/win32_utf8.c:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * Initialization stubs.
7 | */
8 |
9 | #ifndef WIN32_UTF8_NO_API
10 | const w32u8_dll_t* w32u8_get_wrapped_functions()
11 | {
12 | // Yes, this is the prettiest way I came up with.
13 | extern const w32u8_pair_t comdlg32_pairs[];
14 | extern const w32u8_pair_t dsound_pairs[];
15 | extern const w32u8_pair_t gdi32_pairs[];
16 | extern const w32u8_pair_t kernel32_pairs[];
17 | extern const w32u8_pair_t msvcrt_pairs[];
18 | extern const w32u8_pair_t psapi_pairs[];
19 | extern const w32u8_pair_t shell32_pairs[];
20 | extern const w32u8_pair_t shlwapi_pairs[];
21 | extern const w32u8_pair_t user32_pairs[];
22 | extern const w32u8_pair_t version_pairs[];
23 | extern const w32u8_pair_t wininet_pairs[];
24 |
25 | static const w32u8_dll_t dlls[] = {
26 | {"comdlg32.dll", comdlg32_pairs},
27 | {"dsound.dll", dsound_pairs},
28 | {"gdi32.dll", gdi32_pairs},
29 | {"kernel32.dll", kernel32_pairs},
30 | {"msvcrt.dll", msvcrt_pairs},
31 | {"psapi.dll", psapi_pairs},
32 | {"shell32.dll", shell32_pairs},
33 | {"shlwapi.dll", shlwapi_pairs},
34 | {"user32.dll", user32_pairs},
35 | {"version.dll", version_pairs},
36 | {"wininet.dll", wininet_pairs},
37 | { NULL }
38 | };
39 | return dlls;
40 | }
41 | #endif
42 |
43 | UINT fallback_codepage = CP_ACP;
44 |
45 | void w32u8_set_fallback_codepage(UINT codepage)
46 | {
47 | fallback_codepage = codepage;
48 | }
49 |
50 | void win32_utf8_init(void)
51 | {
52 | version_init();
53 | }
54 |
55 | void win32_utf8_exit(void)
56 | {
57 | kernel32_exit();
58 | }
59 |
60 | // Yes, this _has_ to be included in every project.
61 | // Visual C++ won't use it when imported from a library
62 | // and just defaults to msvcrt's one in this case.
63 | BOOL APIENTRY DllMain(HMODULE hDll, DWORD ulReasonForCall, LPVOID lpReserved)
64 | {
65 | (void)hDll;
66 | (void)lpReserved;
67 |
68 | switch(ulReasonForCall) {
69 | case DLL_PROCESS_ATTACH:
70 | win32_utf8_init();
71 | break;
72 | case DLL_PROCESS_DETACH:
73 | win32_utf8_exit();
74 | break;
75 | }
76 | return TRUE;
77 | }
78 |
--------------------------------------------------------------------------------
/src/wininet_dll.c:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * wininet.dll functions.
7 | */
8 |
9 | const w32u8_pair_t wininet_pairs[] = {
10 | {"InternetCombineUrlA", InternetCombineUrlU},
11 | {"InternetCrackUrlA", InternetCrackUrlU},
12 | {"InternetOpenA", InternetOpenU},
13 | {"InternetOpenUrlA", InternetOpenUrlU},
14 | { NULL }
15 | };
16 |
17 | BOOL WINAPI InternetCombineUrlU(
18 | LPCSTR lpszBaseUrl,
19 | LPCSTR lpszRelativeUrl,
20 | LPSTR lpszBuffer,
21 | LPDWORD lpdwBufferLength,
22 | DWORD dwFlags
23 | )
24 | {
25 | BOOL ret = FALSE;
26 | if(lpdwBufferLength) {
27 | DWORD last_error;
28 | WCHAR_T_DEC(lpszBaseUrl);
29 | WCHAR_T_DEC(lpszRelativeUrl);
30 | DWORD len_w = *lpdwBufferLength;
31 | VLA(wchar_t, lpszBuffer_w, *lpdwBufferLength);
32 |
33 | if(!lpszBuffer) {
34 | lpszBuffer_w = NULL;
35 | }
36 | WCHAR_T_CONV(lpszBaseUrl);
37 | WCHAR_T_CONV(lpszRelativeUrl);
38 | ret = InternetCombineUrlW(
39 | lpszBaseUrl_w, lpszRelativeUrl_w, lpszBuffer_w, &len_w, dwFlags
40 | );
41 | /**
42 | * "If the function succeeds, this parameter receives the size of the
43 | * combined URL, in characters, not including the null-terminating character.
44 | * If the function fails, this parameter receives the size of the required buffer,
45 | * in characters (including the null-terminating character).
46 | * (http://msdn.microsoft.com/en-us/library/windows/desktop/aa384355%28v=vs.85%29.aspx)
47 | */
48 | if(ret) {
49 | len_w++;
50 | }
51 | last_error = GetLastError();
52 | if(lpszBuffer) {
53 | *lpdwBufferLength = StringToUTF8(lpszBuffer, lpszBuffer_w, *lpdwBufferLength);
54 | } else {
55 | // Hey, let's be nice and return the _actual_ length.
56 | VLA(wchar_t, lpszBufferReal_w, len_w);
57 | InternetCombineUrlW(
58 | lpszBaseUrl_w, lpszRelativeUrl_w, lpszBufferReal_w, &len_w, dwFlags
59 | );
60 | // + 1 for the reason explained above.
61 | ret = StringToUTF8(NULL, lpszBufferReal_w, 0) + 1;
62 | VLA_FREE(lpszBufferReal_w);
63 | }
64 | WCHAR_T_FREE(lpszBaseUrl);
65 | WCHAR_T_FREE(lpszRelativeUrl);
66 | VLA_FREE(lpszBuffer_w);
67 | SetLastError(last_error);
68 | }
69 | return ret;
70 | }
71 |
72 | #define UC_SET_W(elm) \
73 | if(lpUC->lpsz##elm) { \
74 | lpUC_w.lpsz##elm = w32u8_alloca(wchar_t, lpUC->dw##elm##Length + 1); \
75 | }
76 |
77 | #define UC_CONVERT_AND_FREE(elm) \
78 | if(lpUC->lpsz##elm) { \
79 | StringToUTF8(lpUC->lpsz##elm, lpUC_w.lpsz##elm, lpUC->dw##elm##Length ); \
80 | w32u8_freea(lpUC_w.lpsz##elm); \
81 | }
82 |
83 | #define UC_MACRO_EXPAND(macro) \
84 | macro(Scheme); \
85 | macro(HostName); \
86 | macro(UserName); \
87 | macro(Password); \
88 | macro(UrlPath); \
89 | macro(ExtraInfo)
90 |
91 | BOOL WINAPI InternetCrackUrlU(
92 | LPCSTR lpszUrl,
93 | DWORD dwUrlLength,
94 | DWORD dwFlags,
95 | LPURL_COMPONENTSA lpUC
96 | )
97 | {
98 | BOOL ret = FALSE;
99 | if(lpUC && lpszUrl) {
100 | DWORD last_error;
101 | URL_COMPONENTSW lpUC_w;
102 | WCHAR_T_DEC(lpszUrl);
103 |
104 | if(dwUrlLength == 0) {
105 | dwUrlLength = lpszUrl_len;
106 | }
107 |
108 | memcpy(&lpUC_w, lpUC, lpUC->dwStructSize);
109 | UC_MACRO_EXPAND(UC_SET_W);
110 |
111 | WCHAR_T_CONV(lpszUrl);
112 | ret = InternetCrackUrlW(lpszUrl_w, dwUrlLength, dwFlags, &lpUC_w);
113 |
114 | last_error = GetLastError();
115 | UC_MACRO_EXPAND(UC_CONVERT_AND_FREE);
116 |
117 | WCHAR_T_FREE(lpszUrl);
118 | SetLastError(last_error);
119 | }
120 | return ret;
121 | }
122 |
123 | HINTERNET WINAPI InternetOpenU(
124 | LPCSTR lpszAgent,
125 | DWORD dwAccessType,
126 | LPCSTR lpszProxy,
127 | LPCSTR lpszProxyBypass,
128 | DWORD dwFlags
129 | )
130 | {
131 | size_t agent_len = strlen(lpszAgent) + 1;
132 | size_t proxy_len = lpszProxy ? strlen(lpszProxy) + 1 : 0;
133 | size_t proxy_bypass_len = lpszProxyBypass ? strlen(lpszProxyBypass) + 1 : 0;
134 | size_t total_len = agent_len + proxy_len + proxy_bypass_len;
135 | VLA(wchar_t, param_buffers, total_len);
136 | wchar_t* param_buffer_write = param_buffers;
137 |
138 | size_t written = StringToUTF16(param_buffer_write, lpszAgent, agent_len);
139 | lpszAgent = (LPCSTR)param_buffer_write;
140 | param_buffer_write += written;
141 | if (lpszProxy) {
142 | written = StringToUTF16(param_buffer_write, lpszProxy, proxy_len);
143 | lpszProxy = (LPCSTR)param_buffer_write;
144 | param_buffer_write += written;
145 | }
146 | if (lpszProxyBypass) {
147 | StringToUTF16(param_buffer_write, lpszProxyBypass, proxy_bypass_len);
148 | lpszProxyBypass = (LPCSTR)param_buffer_write;
149 | }
150 |
151 | HINTERNET ret = InternetOpenW(
152 | (LPCWSTR)lpszAgent, dwAccessType, (LPCWSTR)lpszProxy, (LPCWSTR)lpszProxyBypass, dwFlags
153 | );
154 |
155 | VLA_FREE(param_buffers);
156 | return ret;
157 | }
158 |
159 | HINTERNET WINAPI InternetOpenUrlU(
160 | HINTERNET hInternet,
161 | LPCSTR lpszUrl,
162 | LPCSTR lpszHeaders,
163 | DWORD dwHeadersLength,
164 | DWORD dwFlags,
165 | DWORD_PTR dwContext
166 | )
167 | {
168 | HINTERNET ret = NULL;
169 | if(dwHeadersLength == -1 && lpszHeaders) {
170 | dwHeadersLength = strlen(lpszHeaders) + 1;
171 | }
172 | if(lpszUrl) {
173 | WCHAR_T_DEC(lpszUrl);
174 | WCHAR_T_CONV(lpszUrl);
175 |
176 | wchar_t* lpszHeaders_w = NULL;
177 | if (lpszHeaders) {
178 | lpszHeaders_w = (wchar_t*)w32u8_alloca(wchar_t, dwHeadersLength);
179 | dwHeadersLength = StringToUTF16(lpszHeaders_w, lpszHeaders, dwHeadersLength);
180 | }
181 | ret = InternetOpenUrlW(
182 | hInternet, lpszUrl_w, lpszHeaders_w, dwHeadersLength, dwFlags, dwContext
183 | );
184 | WCHAR_T_FREE(lpszUrl);
185 | if (lpszHeaders_w) {
186 | w32u8_freea(lpszHeaders_w);
187 | }
188 | }
189 | return ret;
190 | }
191 |
--------------------------------------------------------------------------------
/src/wininet_dll.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * wininet.dll functions.
7 | */
8 |
9 | #pragma once
10 |
11 | WRAPPER_DEC(BOOL WINAPI, InternetCombineUrl,
12 | LPCSTR lpszBaseUrl,
13 | LPCSTR lpszRelativeUrl,
14 | LPSTR lpszBuffer,
15 | LPDWORD lpdwBufferLength,
16 | DWORD dwFlags
17 | );
18 | #undef InternetCombineUrl
19 | #define InternetCombineUrl InternetCombineUrlU
20 |
21 | WRAPPER_DEC(BOOL WINAPI, InternetCrackUrl,
22 | LPCSTR lpszUrl,
23 | DWORD dwUrlLength,
24 | DWORD dwFlags,
25 | LPURL_COMPONENTSA lpUrlComponents
26 | );
27 | #undef InternetCrackUrl
28 | #define InternetCrackUrl InternetCrackUrlU
29 |
30 | // You might think this is not necessary if your [lpszAgent] only contains
31 | // ASCII characters anyway. However, in contrast to every other Windows API,
32 | // wininet actually uses narrow strings internally, with the encoding being
33 | // determined by this function – InternetOpenA() sets it to the ANSI codepage,
34 | // InternetOpenW() sets it to UTF-8.
35 | // Despite that, InternetOpenUrlW() *always* converts the URL to UTF-8 for
36 | // InternetOpenUrlA(), which then assumes the ANSI codepage for further
37 | // processing. Have fun with the bug reports if that codepage happens to
38 | // be a DBCS one.
39 | //
40 | // (Wine, however, does use a traditional implementation where the A function
41 | // wraps the W function, in case you're now thinking that InternetOpenUrlU()
42 | // is useless if you are sure that your URLs are UTF-8...)
43 | WRAPPER_DEC(HINTERNET WINAPI, InternetOpen,
44 | LPCSTR lpszAgent,
45 | DWORD dwAccessType,
46 | LPCSTR lpszProxy,
47 | LPCSTR lpszProxyBypass,
48 | DWORD dwFlags
49 | );
50 | #undef InternetOpen
51 | #define InternetOpen InternetOpenU
52 |
53 | WRAPPER_DEC(HINTERNET WINAPI, InternetOpenUrl,
54 | HINTERNET hInternet,
55 | LPCSTR lpszUrl,
56 | LPCSTR lpszHeaders,
57 | DWORD dwHeadersLength,
58 | DWORD dwFlags,
59 | DWORD_PTR dwContext
60 | );
61 | #undef InternetOpenUrl
62 | #define InternetOpenUrl InternetOpenUrlU
63 |
--------------------------------------------------------------------------------
/src/wrappers.c:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * Generic call wrappers to cut down redundancy.
7 | */
8 |
9 | DWORD WINAPI WrapGetString(
10 | WrapGetStringFunc_t *func,
11 | DWORD nBufferLength,
12 | LPSTR lpBuffer
13 | )
14 | {
15 | DWORD ret;
16 | VLA(wchar_t, lpBuffer_w, nBufferLength);
17 |
18 | if(!lpBuffer) {
19 | VLA_FREE(lpBuffer_w);
20 | }
21 | ret = func(nBufferLength, lpBuffer_w);
22 | if(lpBuffer) {
23 | StringToUTF8(lpBuffer, lpBuffer_w, nBufferLength);
24 | } else {
25 | // Hey, let's be nice and return the _actual_ length.
26 | VLA(wchar_t, lpBufferReal_w, ret);
27 | func(ret, lpBufferReal_w);
28 | ret = StringToUTF8(NULL, lpBufferReal_w, 0) + 1;
29 | VLA_FREE(lpBufferReal_w);
30 | }
31 | VLA_FREE(lpBuffer_w);
32 | return ret;
33 | }
34 |
--------------------------------------------------------------------------------
/src/wrappers.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * Generic call wrappers to cut down redundancy.
7 | */
8 |
9 | #pragma once
10 |
11 |
12 | // Wrapper for functions that write a string into a buffer, and return its
13 | // necessary size when passing 0 for [nBufferLength].
14 | typedef DWORD WINAPI WrapGetStringFunc_t(
15 | DWORD nBufferLength,
16 | LPWSTR lpBuffer
17 | );
18 | DWORD WINAPI WrapGetString(
19 | WrapGetStringFunc_t *func,
20 | DWORD nBufferLength,
21 | LPSTR lpBuffer
22 | );
23 |
--------------------------------------------------------------------------------
/win32_utf8.def:
--------------------------------------------------------------------------------
1 | EXPORTS
2 | w32u8_set_fallback_codepage @1
3 | w32u8_get_wrapped_functions
4 | win32_utf8_entry
5 |
6 | ; Macros
7 | ; ------
8 | printf_format_parse
9 | windows_version
10 |
11 | ; Unicode
12 | ; -------
13 | StringToUTF16
14 | StringToUTF16_VLA
15 | StringToUTF8
16 | StringToMBFixed
17 | CharToUTF16
18 | EnsureUTF8
19 |
20 | ; comdlg32.dll
21 | ; ------------
22 | GetOpenFileNameU
23 | GetSaveFileNameU
24 |
25 | ; dsound.dll
26 | ; ----------
27 | DirectSoundCaptureEnumerateU
28 | DirectSoundEnumerateU
29 |
30 | ; gdi32.dll
31 | ; ---------
32 | lower_CreateFontA
33 | lower_CreateFontIndirectA
34 | lower_EnumFontFamiliesA
35 |
36 | lower_CreateFontW
37 | lower_CreateFontIndirectW
38 | lower_EnumFontFamiliesW
39 |
40 | AddFontResourceExU
41 | CreateFontU
42 | CreateFontIndirectU
43 | CreateFontIndirectExU
44 | EnumFontFamiliesU
45 | EnumFontFamiliesExU
46 | ExtTextOutU
47 | GetGlyphOutlineU
48 | GetTextExtentPoint32U
49 | RemoveFontResourceExU
50 | TextOutU
51 | PolyTextOutU
52 |
53 | ; kernel32.dll
54 | ; ------------
55 | CopyFileU
56 | CopyFileExU
57 | CreateDirectoryU
58 | CreateFileU
59 | CreateFileMappingU
60 | CreateProcessU
61 | DeleteFileU
62 | FindFirstFileU
63 | FindNextFileU
64 | FormatMessageU
65 | GetCommandLineU
66 | GetCurrentDirectoryU
67 | GetEnvironmentVariableU
68 | GetFileAttributesU
69 | GetFileAttributesExU
70 | GetFullPathNameU
71 | GetModuleFileNameU
72 | GetModuleHandleExU
73 | GetPrivateProfileIntU
74 | GetPrivateProfileStringU
75 | GetStartupInfoU
76 | GetTempFileNameU
77 | GetTempPathU
78 | IsDBCSLeadByteFB
79 | LoadLibraryU
80 | LoadLibraryExU
81 | MoveFileU
82 | MoveFileExU
83 | MoveFileWithProgressU
84 | MultiByteToWideCharU
85 | OpenFileMappingU
86 | ReadFileU
87 | RemoveDirectoryU
88 | SetCurrentDirectoryU
89 | SetEnvironmentVariableU
90 | WideCharToMultiByteU
91 | WriteFileU
92 | WritePrivateProfileStringU
93 |
94 | ; msvcrt.dll
95 | ; ----------
96 | fopen_u
97 |
98 | ; psapi.dll
99 | ; ---------
100 | GetModuleFileNameExU
101 |
102 | ; shell32.dll
103 | ; -----------
104 | CommandLineToArgvU
105 | DragQueryFileU
106 | ExtractIconU
107 | ExtractIconExU
108 | SHBrowseForFolderU
109 | SHGetFolderPathU
110 | SHGetPathFromIDListU
111 | SHParseDisplayNameU
112 | ShellExecuteU
113 |
114 | ; shlwapi.dll
115 | ; -----------
116 | PathFileExistsU
117 | PathFindFileNameU
118 | PathMatchSpecU
119 | PathMatchSpecExU
120 | PathRemoveFileSpecU
121 | PathAddBackslashU
122 | PathIsDirectoryU
123 | PathIsRelativeU
124 | PathCanonicalizeU
125 | PathCombineU
126 | PathAppendU
127 |
128 | ; user32.dll
129 | ; ----------
130 | CharLowerU
131 | CharNextU
132 | CreateDialogParamU
133 | CreateWindowExU
134 | DialogBoxParamU
135 | DrawTextU
136 | DrawTextExU
137 | GetClassInfoU
138 | GetClassInfoExU
139 | InsertMenuItemU
140 | LoadStringU
141 | MessageBoxU_Generic
142 | MessageBoxU
143 | RegisterClassU
144 | RegisterClassExU
145 | SetDlgItemTextU
146 | SetMenuItemInfoU
147 | SetWindowTextU
148 | TabbedTextOutU
149 | UnregisterClassU
150 |
151 | ; version.dll
152 | ; -----------
153 | GetFileVersionInfoU
154 | GetFileVersionInfoExU
155 | GetFileVersionInfoSizeU
156 | GetFileVersionInfoSizeExU
157 |
158 | ; wininet.dll
159 | ; -----------
160 | InternetCombineUrlU
161 | InternetCrackUrlU
162 | InternetOpenU
163 | InternetOpenUrlU
164 |
--------------------------------------------------------------------------------
/win32_utf8.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * Main public header.
7 | */
8 |
9 | #pragma once
10 |
11 | #ifdef __cplusplus
12 | extern "C" {
13 | #endif
14 |
15 | // These must be lowercase to work with MinGW on case-sensitive systems.
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 |
28 | #include "src/entry.h"
29 | #include "src/macros.h"
30 | #include "src/message_enum.h"
31 | #include "src/utf.h"
32 |
33 | #include "src/comdlg32_dll.h"
34 | #include "src/dsound_dll.h"
35 | #include "src/gdi32_dll.h"
36 | #include "src/kernel32_dll.h"
37 | #include "src/msvcrt_dll.h"
38 | #include "src/psapi_dll.h"
39 | #include "src/shell32_dll.h"
40 | #include "src/shlwapi_dll.h"
41 | #include "src/user32_dll.h"
42 | #include "src/version_dll.h"
43 | #include "src/wininet_dll.h"
44 |
45 | typedef struct {
46 | // Name of the original ANSI function (e.g. "CreateFileA")
47 | const char *ansi_name;
48 | // Pointer to our UTF-8 version (e.g. CreateFileU)
49 | const void *utf8_ptr;
50 | } w32u8_pair_t;
51 |
52 | typedef struct {
53 | // DLL name (e.g. "kernel32.dll")
54 | const char *name;
55 | // List of functions we wrap in this DLL
56 | const w32u8_pair_t *funcs;
57 | } w32u8_dll_t;
58 |
59 | // Returns a complete list of function wrappers provided by win32_utf8,
60 | // categorized by the DLL of the original function.
61 | // Both the function pair lists per DLL and the DLL list itself are terminated
62 | // by a zero-filled entry.
63 | #ifndef WIN32_UTF8_NO_API
64 | const w32u8_dll_t* w32u8_get_wrapped_functions();
65 | #else
66 | #define w32u8_get_wrapped_functions ERROR_win32_utf8_was_configured_without_API!
67 | #endif
68 |
69 | // Sets a custom codepage for wide char conversion, which is used if the input
70 | // to a *U function is not valid UTF-8.
71 | // Useful for console applications (which use CP_OEMCP by default) or patching
72 | // applications where the application's native codepage isn't ASCII.
73 | void w32u8_set_fallback_codepage(UINT codepage);
74 |
75 | #ifdef __cplusplus
76 | }
77 | #endif
78 |
--------------------------------------------------------------------------------
/win32_utf8.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Win32 UTF-8 wrapper
5 | https://github.com/thpatch/win32_utf8
6 |
7 |
8 |
9 | Debug Dynamic
10 | Win32
11 |
12 |
13 | Debug Dynamic
14 | x64
15 |
16 |
17 | Debug Static
18 | Win32
19 |
20 |
21 | Debug Static
22 | x64
23 |
24 |
25 | Release Dynamic
26 | Win32
27 |
28 |
29 | Release Dynamic
30 | x64
31 |
32 |
33 | Release Static
34 | Win32
35 |
36 |
37 | Release Static
38 | x64
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | {A185D5A7-3E03-4C87-9774-65C014631F5B}
59 | Win32Proj
60 | win32_utf8
61 | win32_utf8
62 |
63 |
64 |
65 | Unicode
66 | v120_xp
67 | $(SolutionDir)bin\
68 | $(SolutionDir)obj\$(ProjectName) $(Configuration) $(PlatformShortName)\
69 | $(ProjectName)_$(Linking)_$(PlatformShortName)$(TargetSuffix)
70 |
71 |
72 |
73 | Level3
74 | /Zc:threadSafeInit- %(AdditionalOptions)
75 |
76 |
77 | Windows
78 | true
79 | delayimp.lib;%(AdditionalDependencies)
80 | /DELAYLOAD:comdlg32.dll /DELAYLOAD:dsound.dll /DELAYLOAD:gdi32.dll /DELAYLOAD:ole32.dll /DELAYLOAD:psapi.dll /DELAYLOAD:shell32.dll /DELAYLOAD:shlwapi.dll /DELAYLOAD:user32.dll /DELAYLOAD:version.dll /DELAYLOAD:wininet.dll %(AdditionalOptions)
81 | win32_utf8.def
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 | true
109 |
110 |
111 | true
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
--------------------------------------------------------------------------------
/win32_utf8_build_dynamic.c:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * Main compilation unit for dynamic builds.
7 | */
8 |
9 | #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"
16 |
--------------------------------------------------------------------------------
/win32_utf8_build_static.c:
--------------------------------------------------------------------------------
1 | /**
2 | * Win32 UTF-8 wrapper
3 | *
4 | * ----
5 | *
6 | * Main compilation unit for static builds.
7 | */
8 |
9 | #include "src/build.c"
10 | #include "src/entry.c"
11 |
--------------------------------------------------------------------------------