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 g_FormatDiskTitle
[256];
77 static char g_FormatDiskButton
[256];
79 static char g_LocNotAvaliableTitle
[256]; //Location is not available
80 static char g_InsertDiskTitle
[256]; // Insert disk
82 static char system_dir
[MAX_PATH
], sysnative_dir
[MAX_PATH
];
84 static HWINEVENTHOOK ap_weh
= NULL
;
87 static __inline UINT
GetSystemDirectoryU(char* lpBuffer
, UINT uSize
)
89 UINT ret
= 0, err
= ERROR_INVALID_DATA
;
90 // coverity[returned_null]
91 walloc(lpBuffer
, uSize
);
92 ret
= GetSystemDirectoryW(wlpBuffer
, uSize
);
94 if ((ret
!= 0) && ((ret
= wchar_to_utf8_no_alloc(wlpBuffer
, lpBuffer
, uSize
)) == 0)) {
103 static char* ToLocaleName(DWORD lang_id
)
105 static char mui_str
[LOCALE_NAME_MAX_LENGTH
];
106 wchar_t wmui_str
[LOCALE_NAME_MAX_LENGTH
];
108 if (LCIDToLocaleName(lang_id
, wmui_str
, LOCALE_NAME_MAX_LENGTH
, 0) > 0) {
109 wchar_to_utf8_no_alloc(wmui_str
, mui_str
, LOCALE_NAME_MAX_LENGTH
);
112 static_strcpy(mui_str
, "en-US");
117 static __inline
int LoadStringU(HINSTANCE hInstance
, UINT uID
, LPSTR lpBuffer
, int nBufferMax
)
120 DWORD err
= ERROR_INVALID_DATA
;
121 if (nBufferMax
== 0) {
122 // read-only pointer to resource mode is not supported
123 SetLastError(ERROR_INVALID_PARAMETER
);
126 // coverity[returned_null]
127 walloc(lpBuffer
, nBufferMax
);
128 ret
= LoadStringW(hInstance
, uID
, wlpBuffer
, nBufferMax
);
129 err
= GetLastError();
130 if ((ret
> 0) && ((ret
= wchar_to_utf8_no_alloc(wlpBuffer
, lpBuffer
, nBufferMax
)) == 0)) {
131 err
= GetLastError();
139 * typedefs for the function prototypes. Use the something like:
141 * which translates to:
142 * FormatEx_t pfFormatEx = NULL;
143 * in your code, to declare the entrypoint and then use:
144 * PF_INIT(FormatEx, Fmifs);
145 * which translates to:
146 * pfFormatEx = (FormatEx_t) GetProcAddress(GetDLLHandle("fmifs"), "FormatEx");
147 * to make it accessible.
149 #define MAX_LIBRARY_HANDLES 64
150 extern HMODULE OpenedLibrariesHandle
[MAX_LIBRARY_HANDLES
];
151 extern UINT16 OpenedLibrariesHandleSize
;
152 #define OPENED_LIBRARIES_VARS HMODULE OpenedLibrariesHandle[MAX_LIBRARY_HANDLES]; uint16_t OpenedLibrariesHandleSize = 0
153 #define CLOSE_OPENED_LIBRARIES while(OpenedLibrariesHandleSize > 0) FreeLibrary(OpenedLibrariesHandle[--OpenedLibrariesHandleSize])
155 static __inline HMODULE
GetLibraryHandle(char* szLibraryName
) {
157 wchar_t* wszLibraryName
= NULL
;
159 if (szLibraryName
== NULL
|| szLibraryName
[0] == 0)
161 size
= MultiByteToWideChar(CP_UTF8
, 0, szLibraryName
, -1, NULL
, 0);
162 if ((size
<= 1) || ((wszLibraryName
= (wchar_t*)calloc(size
, sizeof(wchar_t))) == NULL
) ||
163 (MultiByteToWideChar(CP_UTF8
, 0, szLibraryName
, -1, wszLibraryName
, size
) != size
))
165 // If the library is already opened, just return a handle (that doesn't need to be freed)
166 if ((h
= GetModuleHandleW(wszLibraryName
)) != NULL
)
169 if (OpenedLibrariesHandleSize
>= MAX_LIBRARY_HANDLES
) {
170 Log("Error: MAX_LIBRARY_HANDLES is too small");
173 h
= LoadLibraryExW(wszLibraryName
, NULL
, LOAD_LIBRARY_SEARCH_SYSTEM32
);
174 // Some Windows 7 platforms (most likely the ones missing KB2533623 per the
175 // official LoadLibraryEx doc) can return ERROR_INVALID_PARAMETER when using
176 // the Ex() version. If that's the case, fallback to using LoadLibraryW().
177 if ((h
== NULL
) && (SCODE_CODE(GetLastError()) == ERROR_INVALID_PARAMETER
))
178 h
= LoadLibraryW(wszLibraryName
);
180 OpenedLibrariesHandle
[OpenedLibrariesHandleSize
++] = h
;
182 Log("Unable to load '%S.dll': %u", wszLibraryName
, LASTERR
);
184 free(wszLibraryName
);
188 #define PF_TYPE(api, ret, proc, args) typedef ret (api *proc##_t)args
189 #define PF_DECL(proc) static proc##_t pf##proc = NULL
190 #define PF_TYPE_DECL(api, ret, proc, args) PF_TYPE(api, ret, proc, args); PF_DECL(proc)
191 #define PF_INIT(proc, name) if (pf##proc == NULL) pf##proc = \
192 (proc##_t) GetProcAddress(GetLibraryHandle(#name), #proc)
193 #define PF_INIT_OR_OUT(proc, name) do {PF_INIT(proc, name); \
194 if (pf##proc == NULL) {Log("Unable to locate %s() in '%s.dll': %u", \
195 #proc, #name, LASTERR); goto out;} } while(0)
196 #define PF_INIT_OR_SET_STATUS(proc, name) do {PF_INIT(proc, name); \
197 if ((pf##proc == NULL) && (NT_SUCCESS(status))) status = STATUS_NOT_IMPLEMENTED; } while(0)
199 static BOOL
is_x64(void)
202 PF_TYPE_DECL(WINAPI
, BOOL
, IsWow64Process
, (HANDLE
, PBOOL
));
203 // Detect if we're running a 32 or 64 bit system
204 if (sizeof(uintptr_t) < 8) {
205 PF_INIT(IsWow64Process
, Kernel32
);
206 if (pfIsWow64Process
!= NULL
) {
207 (*pfIsWow64Process
)(GetCurrentProcess(), &ret
);
217 static __inline UINT
GetSystemWindowsDirectoryU(char* lpBuffer
, UINT uSize
)
219 UINT ret
= 0, err
= ERROR_INVALID_DATA
;
220 // coverity[returned_null]
221 walloc(lpBuffer
, uSize
);
222 ret
= GetSystemWindowsDirectoryW(wlpBuffer
, uSize
);
223 err
= GetLastError();
224 if ((ret
!= 0) && ((ret
= wchar_to_utf8_no_alloc(wlpBuffer
, lpBuffer
, uSize
)) == 0)) {
225 err
= GetLastError();
232 static __inline HMODULE
LoadLibraryU(LPCSTR lpFileName
)
235 DWORD err
= ERROR_INVALID_DATA
;
236 wconvert(lpFileName
);
237 ret
= LoadLibraryW(wlpFileName
);
238 err
= GetLastError();
257 //sz_Or_Ord windowClass;
258 //WCHAR title[titleLen];
263 //WCHAR typeface[stringLen];
267 static BOOL
LoadDialogCaption(HMODULE hMui
, DWORD ID
, CHAR
* title
, DWORD len
)
272 DLGTEMPLATEEX
* pDlgTempEx
= NULL
;
273 HGLOBAL hTemplate
= NULL
;
274 WORD
* pWordData
= NULL
;
276 hDlg
= FindResource(hMui
, MAKEINTRESOURCE(1024), RT_DIALOG
);
279 hTemplate
= LoadResource(hMui
, hDlg
);
282 pDlgTempEx
= (DLGTEMPLATEEX
*)LockResource(hTemplate
);
285 if (pDlgTempEx
->signature
!= 0xFFFF)
290 pWordData
= (WORD
*)(pDlgTempEx
+ 1);
293 if (*pWordData
== 0x0000)
297 else if (*pWordData
== 0xFFFF)
310 if (*pWordData
== 0x0000)
314 else if (*pWordData
== 0xFFFF)
326 wchar_to_utf8_no_alloc(pWordData
, title
, len
);
335 BOOL
SetAlertPromptMessages(void)
338 char mui_path
[MAX_PATH
];
340 if (GetSystemDirectoryU(system_dir
, sizeof(system_dir
)) == 0) {
341 Log("Could not get system directory: %u", LASTERR
);
342 static_strcpy(system_dir
, "C:\\Windows\\System32");
345 // Construct Sysnative ourselves as there is no GetSysnativeDirectory() call
346 // By default (64bit app running on 64 bit OS or 32 bit app running on 32 bit OS)
347 // Sysnative and System32 are the same
348 static_strcpy(sysnative_dir
, system_dir
);
349 // But if the app is 32 bit and the OS is 64 bit, Sysnative must differ from System32
350 #if (defined(VTARCH_X86) || defined(VTARCH_ARM))
352 if (GetSystemWindowsDirectoryU(sysnative_dir
, sizeof(sysnative_dir
)) == 0) {
353 Log("Could not get Windows directory: %u", LASTERR
);
354 static_strcpy(sysnative_dir
, "C:\\Windows");
356 static_strcat(sysnative_dir
, "\\Sysnative");
360 Log("system_dir=<%s>", system_dir
);
361 Log("sysnative_dir=<%s>", sysnative_dir
);
363 sprintf_s(mui_path
, MAX_PATH
, "%s\\%s\\shell32.dll.mui", sysnative_dir
, ToLocaleName(GetUserDefaultUILanguage()));
365 hMui
= LoadLibraryU(mui_path
);
368 Log("LoadLibrary shell32.dll.mui SUCCESS");
372 Log("LoadLibrary shell32.dll.mui FAILED");
378 // 4097 = "You need to format the disk in drive %c: before you can use it." (dialog text)
379 // 4125 = "Microsoft Windows" (dialog title)
380 // 4126 = "Format disk" (button)
381 if (LoadStringU(hMui
, 4125, g_FormatDiskTitle
, sizeof(g_FormatDiskTitle
)) <= 0) {
382 static_strcpy(g_FormatDiskTitle
, "Microsoft Windows");
383 Log("Warning: Could not locate localized format prompt title string in '%s': %u", mui_path
, LASTERR
);
386 if (LoadStringU(hMui
, 4126, g_FormatDiskButton
, sizeof(g_FormatDiskButton
)) <= 0) {
387 static_strcpy(g_FormatDiskButton
, "Format disk");
388 Log("Warning: Could not locate localized format prompt button string in '%s': %u", mui_path
, LASTERR
);
391 // 32964 = "Location is not available"
392 if (LoadStringU(hMui
, 32964, g_LocNotAvaliableTitle
, sizeof(g_LocNotAvaliableTitle
)) <= 0) {
393 static_strcpy(g_LocNotAvaliableTitle
, "Location is not available");
394 Log("Warning: Could not locate localized format prompt title string in '%s': %u", mui_path
, LASTERR
);
397 if (!LoadDialogCaption(hMui
, 1024, g_InsertDiskTitle
, sizeof(g_InsertDiskTitle
)))
399 static_strcpy(g_InsertDiskTitle
, "Insert disk");
400 Log("Warning: Could not locate insert disk title string in '%s': %u", mui_path
, LASTERR
);
407 static __inline
int GetWindowTextU(HWND hWnd
, char* lpString
, int nMaxCount
)
410 DWORD err
= ERROR_INVALID_DATA
;
413 // Handle the empty string as GetWindowTextW() returns 0 then
414 if ((lpString
!= NULL
) && (nMaxCount
> 0))
416 // coverity[returned_null]
417 walloc(lpString
, nMaxCount
);
418 ret
= GetWindowTextW(hWnd
, wlpString
, nMaxCount
);
419 err
= GetLastError();
420 // coverity[var_deref_model]
421 if ((ret
!= 0) && ((ret
= wchar_to_utf8_no_alloc(wlpString
, lpString
, nMaxCount
)) == 0)) {
422 err
= GetLastError();
425 lpString
[nMaxCount
- 1] = 0;
432 * The following function calls are used to automatically detect and close the native
433 * Windows format prompt "You must format the disk in drive X:". To do that, we use
434 * an event hook that gets triggered whenever a window is placed in the foreground.
435 * In that hook, we look for a dialog that has style WS_POPUPWINDOW and has the relevant
436 * title. However, because the title in itself is too generic (the expectation is that
437 * it will be "Microsoft Windows") we also enumerate all the child controls from that
438 * prompt, using another callback, until we find one that contains the text we expect
439 * for the "Format disk" button.
440 * Oh, and since all of these strings are localized, we must first pick them up from
441 * the relevant mui's.
443 static BOOL CALLBACK
AlertPromptCallback(HWND hWnd
, LPARAM lParam
)
446 BOOL
* found
= (BOOL
*)lParam
;
448 if (GetWindowTextU(hWnd
, str
, sizeof(str
)) == 0)
450 if (strcmp(str
, g_FormatDiskButton
) == 0)
455 static volatile BOOL g_AlertPromptHookEnable
= FALSE
;
457 void SetAlertPromptHookEnable(BOOL enable
)
459 g_AlertPromptHookEnable
= enable
;
462 static void CALLBACK
AlertPromptHook(HWINEVENTHOOK hWinEventHook
, DWORD Event
, HWND hWnd
, LONG idObject
, LONG idChild
, DWORD dwEventThread
, DWORD dwmsEventTime
)
467 if (Event
!= EVENT_SYSTEM_FOREGROUND
)
472 if (!g_AlertPromptHookEnable
)
477 //GetWindowTextU(hWnd, str, sizeof(str));
478 //Log("###### EVENT_SYSTEM_FOREGROUND Windows prompt <%s> #######", str);
480 if (GetWindowLongPtr(hWnd
, GWL_STYLE
) & WS_POPUPWINDOW
) {
482 GetWindowTextU(hWnd
, str
, sizeof(str
));
483 if (strcmp(str
, g_FormatDiskTitle
) == 0)
486 EnumChildWindows(hWnd
, AlertPromptCallback
, (LPARAM
)&found
);
489 SendMessage(hWnd
, WM_COMMAND
, (WPARAM
)IDCANCEL
, (LPARAM
)0);
490 Log("###### Detect 'Windows format' prompt, now close it. #######");
493 else if (strcmp(str
, g_LocNotAvaliableTitle
) == 0)
495 SendMessage(hWnd
, WM_COMMAND
, (WPARAM
)IDCANCEL
, (LPARAM
)0);
496 Log("###### Detect 'Location is not available' prompt, now close it. #######");
498 else if (strcmp(str
, g_InsertDiskTitle
) == 0)
500 SendMessage(hWnd
, WM_COMMAND
, (WPARAM
)IDCANCEL
, (LPARAM
)0);
501 Log("###### Detect 'Insert disk' prompt, now close it. #######");
507 BOOL
SetAlertPromptHook(void)
510 return TRUE
; // No need to set again if active
511 ap_weh
= SetWinEventHook(EVENT_SYSTEM_FOREGROUND
, EVENT_SYSTEM_FOREGROUND
, NULL
,
512 AlertPromptHook
, 0, 0, WINEVENT_OUTOFCONTEXT
| WINEVENT_SKIPOWNPROCESS
);
513 return (ap_weh
!= NULL
);
516 BOOL
AlertSuppressInit(void)
520 SetAlertPromptMessages();
522 bRet
= SetAlertPromptHook();
523 Log("SetAlertPromptHook %s", bRet
? "SUCCESS" : "FAILED");