├── .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 | ![OS compatibility message box](os_compat.png) 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 | --------------------------------------------------------------------------------