| // Copyright 2024 The Bazel Authors. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #define WIN32_LEAN_AND_MEAN |
| #include <windows.h> |
| |
| #include <string> |
| |
| // This program patches the app manifest of the java.exe launcher to force its |
| // active code page to UTF-8 on Windows 1903 and later. |
| // https://learn.microsoft.com/en-us/windows/apps/design/globalizing/use-utf8-code-page#set-a-process-code-page-to-utf-8 |
| // |
| // This is necessary because the launcher sets sun.jnu.encoding to the system |
| // code page, which by default is a legacy code page such as Cp1252 on Windows. |
| // This causes the JVM to be unable to interact with files whose paths contain |
| // Unicode characters not representable in the system code page, as well as |
| // command-line arguments and environment variables containing such characters. |
| // |
| // Usage in the libjava.dll code: |
| // https://github.com/openjdk/jdk/blob/e7f0bf11ff0e89b6b156d5e88ca3771c706aa46a/src/java.base/windows/native/libjava/java_props_md.c#L63-L65 |
| int wmain(int argc, wchar_t *argv[]) { |
| if (argc != 2) { |
| fwprintf(stderr, L"Usage: %ls <filename>\n", argv[0]); |
| return 1; |
| } |
| |
| // Read the app manifest (aka side-by-side or fusion manifest) from the |
| // executable, which requires loading it as a "module". |
| HMODULE exe = LoadLibraryExW(argv[1], nullptr, LOAD_LIBRARY_AS_DATAFILE); |
| if (!exe) { |
| fwprintf(stderr, L"Error loading file %ls: %d\n", argv[1], GetLastError()); |
| return 1; |
| } |
| HRSRC manifest_resource = FindResourceA(exe, MAKEINTRESOURCE(1), RT_MANIFEST); |
| if (!manifest_resource) { |
| fwprintf(stderr, L"Resource not found: %d\n", GetLastError()); |
| return 1; |
| } |
| HGLOBAL manifest_handle = LoadResource(exe, manifest_resource); |
| if (!manifest_handle) { |
| fwprintf(stderr, L"Error loading resource: %d\n", GetLastError()); |
| return 1; |
| } |
| LPVOID manifest_data = LockResource(manifest_handle); |
| if (!manifest_data) { |
| fwprintf(stderr, L"Error locking resource: %d\n", GetLastError()); |
| return 1; |
| } |
| DWORD manifest_len = SizeofResource(exe, manifest_resource); |
| std::string manifest((char *)manifest_data, manifest_len); |
| UnlockResource(manifest_handle); |
| FreeResource(manifest_handle); |
| FreeLibrary(exe); |
| |
| // Insert the activeCodePage element into the manifest at the end of the |
| // windowsSettings element. |
| // https://github.com/openjdk/jdk/blob/29882bfe7b7e76446a96862cd0a5e81c7e054415/src/java.base/windows/native/launcher/java.manifest#L43 |
| std::size_t insert_pos = manifest.find("</asmv3:windowsSettings>"); |
| if (insert_pos == std::wstring::npos) { |
| fwprintf(stderr, L"End tag not found in manifest:\n%hs", manifest.c_str()); |
| return 1; |
| } |
| std::string new_manifest = manifest.substr(0, insert_pos) + |
| "<activeCodePage " |
| "xmlns=\"http://schemas.microsoft.com/SMI/2019/" |
| "WindowsSettings\">UTF-8</activeCodePage>" + |
| manifest.substr(insert_pos); |
| |
| // Write back the modified app manifest. |
| HANDLE update_handle = BeginUpdateResourceW(argv[1], false); |
| if (!update_handle) { |
| fwprintf(stderr, L"Error opening file %ls for update: %d\n", argv[1], |
| GetLastError()); |
| return 1; |
| } |
| if (!UpdateResourceA(update_handle, RT_MANIFEST, MAKEINTRESOURCE(1), |
| MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), |
| const_cast<char *>(new_manifest.c_str()), |
| new_manifest.size())) { |
| fwprintf(stderr, L"Error updating resource: %d\n", GetLastError()); |
| return 1; |
| } |
| if (!EndUpdateResourceW(update_handle, false)) { |
| fwprintf(stderr, L"Error finalizing update: %d\n", GetLastError()); |
| return 1; |
| } |
| |
| return 0; |
| } |