1 /******************************************************************************
4 * Copyright (c) 2022, longpanda <admin@ventoy.net>
5 * Copyright (c) 2011-2022 Pete Batard <pete@akeo.ie>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 3 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, see <http://www.gnu.org/licenses/>.
27 #include "Ventoy2Disk.h"
30 #define sfree(p) do {if (p != NULL) {free((void*)(p)); p = NULL;}} while(0)
31 #define wconvert(p) wchar_t* w ## p = utf8_to_wchar(p)
32 #define walloc(p, size) wchar_t* w ## p = (p == NULL)?NULL:(wchar_t*)calloc(size, sizeof(wchar_t))
33 #define wfree(p) sfree(w ## p)
35 #define static_strcpy(dst, src) strcpy_s(dst, sizeof(dst), src)
36 #define static_strcat(dst, src) strcat_s(dst, sizeof(dst), src)
39 #define wchar_to_utf8_no_alloc(wsrc, dest, dest_size) \
40 WideCharToMultiByte(CP_UTF8, 0, wsrc, -1, dest, dest_size, NULL, NULL)
41 #define utf8_to_wchar_no_alloc(src, wdest, wdest_size) \
42 MultiByteToWideChar(CP_UTF8, 0, src, -1, wdest, wdest_size)
46 * Converts an UTF8 string to UTF-16 (allocate returned string)
47 * Returns NULL on error
49 static __inline
wchar_t* utf8_to_wchar(const char* str
)
57 // Convert the empty string too
59 return (wchar_t*)calloc(1, sizeof(wchar_t));
61 // Find out the size we need to allocate for our converted string
62 size
= MultiByteToWideChar(CP_UTF8
, 0, str
, -1, NULL
, 0);
63 if (size
<= 1) // An empty string would be size 1
66 if ((wstr
= (wchar_t*)calloc(size
, sizeof(wchar_t))) == NULL
)
69 if (utf8_to_wchar_no_alloc(str
, wstr
, size
) != size
) {
76 static char title_str
[2][128], button_str
[128];
77 static char system_dir
[MAX_PATH
], sysnative_dir
[MAX_PATH
];
79 static HWINEVENTHOOK ap_weh
= NULL
;
82 static __inline UINT
GetSystemDirectoryU(char* lpBuffer
, UINT uSize
)
84 UINT ret
= 0, err
= ERROR_INVALID_DATA
;
85 // coverity[returned_null]
86 walloc(lpBuffer
, uSize
);
87 ret
= GetSystemDirectoryW(wlpBuffer
, uSize
);
89 if ((ret
!= 0) && ((ret
= wchar_to_utf8_no_alloc(wlpBuffer
, lpBuffer
, uSize
)) == 0)) {
98 static char* ToLocaleName(DWORD lang_id
)
100 static char mui_str
[LOCALE_NAME_MAX_LENGTH
];
101 wchar_t wmui_str
[LOCALE_NAME_MAX_LENGTH
];
103 if (LCIDToLocaleName(lang_id
, wmui_str
, LOCALE_NAME_MAX_LENGTH
, 0) > 0) {
104 wchar_to_utf8_no_alloc(wmui_str
, mui_str
, LOCALE_NAME_MAX_LENGTH
);
107 static_strcpy(mui_str
, "en-US");
112 static __inline
int LoadStringU(HINSTANCE hInstance
, UINT uID
, LPSTR lpBuffer
, int nBufferMax
)
115 DWORD err
= ERROR_INVALID_DATA
;
116 if (nBufferMax
== 0) {
117 // read-only pointer to resource mode is not supported
118 SetLastError(ERROR_INVALID_PARAMETER
);
121 // coverity[returned_null]
122 walloc(lpBuffer
, nBufferMax
);
123 ret
= LoadStringW(hInstance
, uID
, wlpBuffer
, nBufferMax
);
124 err
= GetLastError();
125 if ((ret
> 0) && ((ret
= wchar_to_utf8_no_alloc(wlpBuffer
, lpBuffer
, nBufferMax
)) == 0)) {
126 err
= GetLastError();
134 * typedefs for the function prototypes. Use the something like:
136 * which translates to:
137 * FormatEx_t pfFormatEx = NULL;
138 * in your code, to declare the entrypoint and then use:
139 * PF_INIT(FormatEx, Fmifs);
140 * which translates to:
141 * pfFormatEx = (FormatEx_t) GetProcAddress(GetDLLHandle("fmifs"), "FormatEx");
142 * to make it accessible.
144 #define MAX_LIBRARY_HANDLES 64
145 extern HMODULE OpenedLibrariesHandle
[MAX_LIBRARY_HANDLES
];
146 extern UINT16 OpenedLibrariesHandleSize
;
147 #define OPENED_LIBRARIES_VARS HMODULE OpenedLibrariesHandle[MAX_LIBRARY_HANDLES]; uint16_t OpenedLibrariesHandleSize = 0
148 #define CLOSE_OPENED_LIBRARIES while(OpenedLibrariesHandleSize > 0) FreeLibrary(OpenedLibrariesHandle[--OpenedLibrariesHandleSize])
150 static __inline HMODULE
GetLibraryHandle(char* szLibraryName
) {
152 wchar_t* wszLibraryName
= NULL
;
154 if (szLibraryName
== NULL
|| szLibraryName
[0] == 0)
156 size
= MultiByteToWideChar(CP_UTF8
, 0, szLibraryName
, -1, NULL
, 0);
157 if ((size
<= 1) || ((wszLibraryName
= (wchar_t*)calloc(size
, sizeof(wchar_t))) == NULL
) ||
158 (MultiByteToWideChar(CP_UTF8
, 0, szLibraryName
, -1, wszLibraryName
, size
) != size
))
160 // If the library is already opened, just return a handle (that doesn't need to be freed)
161 if ((h
= GetModuleHandleW(wszLibraryName
)) != NULL
)
164 if (OpenedLibrariesHandleSize
>= MAX_LIBRARY_HANDLES
) {
165 Log("Error: MAX_LIBRARY_HANDLES is too small");
168 h
= LoadLibraryExW(wszLibraryName
, NULL
, LOAD_LIBRARY_SEARCH_SYSTEM32
);
169 // Some Windows 7 platforms (most likely the ones missing KB2533623 per the
170 // official LoadLibraryEx doc) can return ERROR_INVALID_PARAMETER when using
171 // the Ex() version. If that's the case, fallback to using LoadLibraryW().
172 if ((h
== NULL
) && (SCODE_CODE(GetLastError()) == ERROR_INVALID_PARAMETER
))
173 h
= LoadLibraryW(wszLibraryName
);
175 OpenedLibrariesHandle
[OpenedLibrariesHandleSize
++] = h
;
177 Log("Unable to load '%S.dll': %u", wszLibraryName
, LASTERR
);
179 free(wszLibraryName
);
183 #define PF_TYPE(api, ret, proc, args) typedef ret (api *proc##_t)args
184 #define PF_DECL(proc) static proc##_t pf##proc = NULL
185 #define PF_TYPE_DECL(api, ret, proc, args) PF_TYPE(api, ret, proc, args); PF_DECL(proc)
186 #define PF_INIT(proc, name) if (pf##proc == NULL) pf##proc = \
187 (proc##_t) GetProcAddress(GetLibraryHandle(#name), #proc)
188 #define PF_INIT_OR_OUT(proc, name) do {PF_INIT(proc, name); \
189 if (pf##proc == NULL) {Log("Unable to locate %s() in '%s.dll': %u", \
190 #proc, #name, LASTERR); goto out;} } while(0)
191 #define PF_INIT_OR_SET_STATUS(proc, name) do {PF_INIT(proc, name); \
192 if ((pf##proc == NULL) && (NT_SUCCESS(status))) status = STATUS_NOT_IMPLEMENTED; } while(0)
194 static BOOL
is_x64(void)
197 PF_TYPE_DECL(WINAPI
, BOOL
, IsWow64Process
, (HANDLE
, PBOOL
));
198 // Detect if we're running a 32 or 64 bit system
199 if (sizeof(uintptr_t) < 8) {
200 PF_INIT(IsWow64Process
, Kernel32
);
201 if (pfIsWow64Process
!= NULL
) {
202 (*pfIsWow64Process
)(GetCurrentProcess(), &ret
);
212 static __inline UINT
GetSystemWindowsDirectoryU(char* lpBuffer
, UINT uSize
)
214 UINT ret
= 0, err
= ERROR_INVALID_DATA
;
215 // coverity[returned_null]
216 walloc(lpBuffer
, uSize
);
217 ret
= GetSystemWindowsDirectoryW(wlpBuffer
, uSize
);
218 err
= GetLastError();
219 if ((ret
!= 0) && ((ret
= wchar_to_utf8_no_alloc(wlpBuffer
, lpBuffer
, uSize
)) == 0)) {
220 err
= GetLastError();
227 static __inline HMODULE
LoadLibraryU(LPCSTR lpFileName
)
230 DWORD err
= ERROR_INVALID_DATA
;
231 wconvert(lpFileName
);
232 ret
= LoadLibraryW(wlpFileName
);
233 err
= GetLastError();
240 BOOL
SetAlertPromptMessages(void)
243 char mui_path
[MAX_PATH
];
245 if (GetSystemDirectoryU(system_dir
, sizeof(system_dir
)) == 0) {
246 Log("Could not get system directory: %u", LASTERR
);
247 static_strcpy(system_dir
, "C:\\Windows\\System32");
250 // Construct Sysnative ourselves as there is no GetSysnativeDirectory() call
251 // By default (64bit app running on 64 bit OS or 32 bit app running on 32 bit OS)
252 // Sysnative and System32 are the same
253 static_strcpy(sysnative_dir
, system_dir
);
254 // But if the app is 32 bit and the OS is 64 bit, Sysnative must differ from System32
255 #if (defined(VTARCH_X86) || defined(VTARCH_ARM))
257 if (GetSystemWindowsDirectoryU(sysnative_dir
, sizeof(sysnative_dir
)) == 0) {
258 Log("Could not get Windows directory: %u", LASTERR
);
259 static_strcpy(sysnative_dir
, "C:\\Windows");
261 static_strcat(sysnative_dir
, "\\Sysnative");
265 Log("system_dir=<%s>", system_dir
);
266 Log("sysnative_dir=<%s>", sysnative_dir
);
268 sprintf_s(mui_path
, MAX_PATH
, "%s\\%s\\shell32.dll.mui", sysnative_dir
, ToLocaleName(GetUserDefaultUILanguage()));
270 hMui
= LoadLibraryU(mui_path
);
273 // 4097 = "You need to format the disk in drive %c: before you can use it." (dialog text)
274 // 4125 = "Microsoft Windows" (dialog title)
275 // 4126 = "Format disk" (button)
276 if (LoadStringU(hMui
, 4125, title_str
[0], sizeof(title_str
[0])) <= 0) {
277 static_strcpy(title_str
[0], "Microsoft Windows");
278 Log("Warning: Could not locate localized format prompt title string in '%s': %u", mui_path
, LASTERR
);
280 if (LoadStringU(hMui
, 4126, button_str
, sizeof(button_str
)) <= 0) {
281 static_strcpy(button_str
, "Format disk");
282 Log("Warning: Could not locate localized format prompt button string in '%s': %u", mui_path
, LASTERR
);
286 Log("LoadLibrary shell32.dll.mui SUCCESS");
290 Log("LoadLibrary shell32.dll.mui FAILED");
296 static __inline
int GetWindowTextU(HWND hWnd
, char* lpString
, int nMaxCount
)
299 DWORD err
= ERROR_INVALID_DATA
;
302 // Handle the empty string as GetWindowTextW() returns 0 then
303 if ((lpString
!= NULL
) && (nMaxCount
> 0))
305 // coverity[returned_null]
306 walloc(lpString
, nMaxCount
);
307 ret
= GetWindowTextW(hWnd
, wlpString
, nMaxCount
);
308 err
= GetLastError();
309 // coverity[var_deref_model]
310 if ((ret
!= 0) && ((ret
= wchar_to_utf8_no_alloc(wlpString
, lpString
, nMaxCount
)) == 0)) {
311 err
= GetLastError();
314 lpString
[nMaxCount
- 1] = 0;
321 * The following function calls are used to automatically detect and close the native
322 * Windows format prompt "You must format the disk in drive X:". To do that, we use
323 * an event hook that gets triggered whenever a window is placed in the foreground.
324 * In that hook, we look for a dialog that has style WS_POPUPWINDOW and has the relevant
325 * title. However, because the title in itself is too generic (the expectation is that
326 * it will be "Microsoft Windows") we also enumerate all the child controls from that
327 * prompt, using another callback, until we find one that contains the text we expect
328 * for the "Format disk" button.
329 * Oh, and since all of these strings are localized, we must first pick them up from
330 * the relevant mui's.
332 static BOOL CALLBACK
AlertPromptCallback(HWND hWnd
, LPARAM lParam
)
335 BOOL
* found
= (BOOL
*)lParam
;
337 if (GetWindowTextU(hWnd
, str
, sizeof(str
)) == 0)
339 if (strcmp(str
, button_str
) == 0)
344 static void CALLBACK
AlertPromptHook(HWINEVENTHOOK hWinEventHook
, DWORD Event
, HWND hWnd
, LONG idObject
, LONG idChild
, DWORD dwEventThread
, DWORD dwmsEventTime
)
349 if (Event
== EVENT_SYSTEM_FOREGROUND
) {
350 if (GetWindowLongPtr(hWnd
, GWL_STYLE
) & WS_POPUPWINDOW
) {
352 GetWindowTextU(hWnd
, str
, sizeof(str
));
353 if (strcmp(str
, title_str
[0]) == 0) {
355 EnumChildWindows(hWnd
, AlertPromptCallback
, (LPARAM
)&found
);
357 SendMessage(hWnd
, WM_COMMAND
, (WPARAM
)IDCANCEL
, (LPARAM
)0);
358 Log("###### Closed Windows format prompt #######");
366 BOOL
SetAlertPromptHook(void)
369 return TRUE
; // No need to set again if active
370 ap_weh
= SetWinEventHook(EVENT_SYSTEM_FOREGROUND
, EVENT_SYSTEM_FOREGROUND
, NULL
,
371 AlertPromptHook
, 0, 0, WINEVENT_OUTOFCONTEXT
| WINEVENT_SKIPOWNPROCESS
);
372 return (ap_weh
!= NULL
);
375 BOOL
AlertSuppressInit(void)
379 SetAlertPromptMessages();
381 bRet
= SetAlertPromptHook();
382 Log("SetAlertPromptHook %s", bRet
? "SUCCESS" : "FAILED");