+/******************************************************************************\r
+* AlertSuppress.c\r
+*\r
+* Copyright (c) 2022, longpanda <admin@ventoy.net>\r
+* Copyright (c) 2011-2022 Pete Batard <pete@akeo.ie>\r
+*\r
+* This program is free software; you can redistribute it and/or\r
+* modify it under the terms of the GNU General Public License as\r
+* published by the Free Software Foundation; either version 3 of the\r
+* License, or (at your option) any later version.\r
+*\r
+* This program is distributed in the hope that it will be useful, but\r
+* WITHOUT ANY WARRANTY; without even the implied warranty of\r
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+* General Public License for more details.\r
+*\r
+* You should have received a copy of the GNU General Public License\r
+* along with this program; if not, see <http://www.gnu.org/licenses/>.\r
+*\r
+*/\r
+\r
+#include <Windows.h>\r
+#include <winternl.h>\r
+#include <commctrl.h>\r
+#include <initguid.h>\r
+#include <vds.h>\r
+#include "Ventoy2Disk.h"\r
+\r
+\r
+#define sfree(p) do {if (p != NULL) {free((void*)(p)); p = NULL;}} while(0)\r
+#define wconvert(p) wchar_t* w ## p = utf8_to_wchar(p)\r
+#define walloc(p, size) wchar_t* w ## p = (p == NULL)?NULL:(wchar_t*)calloc(size, sizeof(wchar_t))\r
+#define wfree(p) sfree(w ## p)\r
+\r
+#define static_strcpy(dst, src) strcpy_s(dst, sizeof(dst), src)\r
+#define static_strcat(dst, src) strcat_s(dst, sizeof(dst), src)\r
+\r
+\r
+#define wchar_to_utf8_no_alloc(wsrc, dest, dest_size) \\r
+ WideCharToMultiByte(CP_UTF8, 0, wsrc, -1, dest, dest_size, NULL, NULL)\r
+#define utf8_to_wchar_no_alloc(src, wdest, wdest_size) \\r
+ MultiByteToWideChar(CP_UTF8, 0, src, -1, wdest, wdest_size)\r
+\r
+\r
+/*\r
+ * Converts an UTF8 string to UTF-16 (allocate returned string)\r
+ * Returns NULL on error\r
+ */\r
+static __inline wchar_t* utf8_to_wchar(const char* str)\r
+{\r
+ int size = 0;\r
+ wchar_t* wstr = NULL;\r
+\r
+ if (str == NULL)\r
+ return NULL;\r
+\r
+ // Convert the empty string too\r
+ if (str[0] == 0)\r
+ return (wchar_t*)calloc(1, sizeof(wchar_t));\r
+\r
+ // Find out the size we need to allocate for our converted string\r
+ size = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);\r
+ if (size <= 1) // An empty string would be size 1\r
+ return NULL;\r
+\r
+ if ((wstr = (wchar_t*)calloc(size, sizeof(wchar_t))) == NULL)\r
+ return NULL;\r
+\r
+ if (utf8_to_wchar_no_alloc(str, wstr, size) != size) {\r
+ sfree(wstr);\r
+ return NULL;\r
+ }\r
+ return wstr;\r
+}\r
+\r
+static char title_str[2][128], button_str[128];\r
+static char system_dir[MAX_PATH], sysnative_dir[MAX_PATH];\r
+\r
+static HWINEVENTHOOK ap_weh = NULL;\r
+\r
+\r
+static __inline UINT GetSystemDirectoryU(char* lpBuffer, UINT uSize)\r
+{\r
+ UINT ret = 0, err = ERROR_INVALID_DATA;\r
+ // coverity[returned_null]\r
+ walloc(lpBuffer, uSize);\r
+ ret = GetSystemDirectoryW(wlpBuffer, uSize);\r
+ err = GetLastError();\r
+ if ((ret != 0) && ((ret = wchar_to_utf8_no_alloc(wlpBuffer, lpBuffer, uSize)) == 0)) {\r
+ err = GetLastError();\r
+ }\r
+ wfree(lpBuffer);\r
+ SetLastError(err);\r
+ return ret;\r
+}\r
+\r
+\r
+static char* ToLocaleName(DWORD lang_id)\r
+{\r
+ static char mui_str[LOCALE_NAME_MAX_LENGTH];\r
+ wchar_t wmui_str[LOCALE_NAME_MAX_LENGTH];\r
+\r
+ if (LCIDToLocaleName(lang_id, wmui_str, LOCALE_NAME_MAX_LENGTH, 0) > 0) {\r
+ wchar_to_utf8_no_alloc(wmui_str, mui_str, LOCALE_NAME_MAX_LENGTH);\r
+ }\r
+ else {\r
+ static_strcpy(mui_str, "en-US");\r
+ }\r
+ return mui_str;\r
+}\r
+\r
+static __inline int LoadStringU(HINSTANCE hInstance, UINT uID, LPSTR lpBuffer, int nBufferMax)\r
+{\r
+ int ret;\r
+ DWORD err = ERROR_INVALID_DATA;\r
+ if (nBufferMax == 0) {\r
+ // read-only pointer to resource mode is not supported\r
+ SetLastError(ERROR_INVALID_PARAMETER);\r
+ return 0;\r
+ }\r
+ // coverity[returned_null]\r
+ walloc(lpBuffer, nBufferMax);\r
+ ret = LoadStringW(hInstance, uID, wlpBuffer, nBufferMax);\r
+ err = GetLastError();\r
+ if ((ret > 0) && ((ret = wchar_to_utf8_no_alloc(wlpBuffer, lpBuffer, nBufferMax)) == 0)) {\r
+ err = GetLastError();\r
+ }\r
+ wfree(lpBuffer);\r
+ SetLastError(err);\r
+ return ret;\r
+}\r
+\r
+/*\r
+ * typedefs for the function prototypes. Use the something like:\r
+ * PF_DECL(FormatEx);\r
+ * which translates to:\r
+ * FormatEx_t pfFormatEx = NULL;\r
+ * in your code, to declare the entrypoint and then use:\r
+ * PF_INIT(FormatEx, Fmifs);\r
+ * which translates to:\r
+ * pfFormatEx = (FormatEx_t) GetProcAddress(GetDLLHandle("fmifs"), "FormatEx");\r
+ * to make it accessible.\r
+ */\r
+#define MAX_LIBRARY_HANDLES 64\r
+extern HMODULE OpenedLibrariesHandle[MAX_LIBRARY_HANDLES];\r
+extern UINT16 OpenedLibrariesHandleSize;\r
+#define OPENED_LIBRARIES_VARS HMODULE OpenedLibrariesHandle[MAX_LIBRARY_HANDLES]; uint16_t OpenedLibrariesHandleSize = 0\r
+#define CLOSE_OPENED_LIBRARIES while(OpenedLibrariesHandleSize > 0) FreeLibrary(OpenedLibrariesHandle[--OpenedLibrariesHandleSize])\r
+\r
+static __inline HMODULE GetLibraryHandle(char* szLibraryName) {\r
+ HMODULE h = NULL;\r
+ wchar_t* wszLibraryName = NULL;\r
+ int size;\r
+ if (szLibraryName == NULL || szLibraryName[0] == 0)\r
+ goto out;\r
+ size = MultiByteToWideChar(CP_UTF8, 0, szLibraryName, -1, NULL, 0);\r
+ if ((size <= 1) || ((wszLibraryName = (wchar_t*)calloc(size, sizeof(wchar_t))) == NULL) ||\r
+ (MultiByteToWideChar(CP_UTF8, 0, szLibraryName, -1, wszLibraryName, size) != size))\r
+ goto out;\r
+ // If the library is already opened, just return a handle (that doesn't need to be freed)\r
+ if ((h = GetModuleHandleW(wszLibraryName)) != NULL)\r
+ goto out;\r
+ // Sanity check\r
+ if (OpenedLibrariesHandleSize >= MAX_LIBRARY_HANDLES) {\r
+ Log("Error: MAX_LIBRARY_HANDLES is too small");\r
+ goto out;\r
+ }\r
+ h = LoadLibraryExW(wszLibraryName, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);\r
+ // Some Windows 7 platforms (most likely the ones missing KB2533623 per the\r
+ // official LoadLibraryEx doc) can return ERROR_INVALID_PARAMETER when using\r
+ // the Ex() version. If that's the case, fallback to using LoadLibraryW().\r
+ if ((h == NULL) && (SCODE_CODE(GetLastError()) == ERROR_INVALID_PARAMETER))\r
+ h = LoadLibraryW(wszLibraryName);\r
+ if (h != NULL)\r
+ OpenedLibrariesHandle[OpenedLibrariesHandleSize++] = h;\r
+ else\r
+ Log("Unable to load '%S.dll': %u", wszLibraryName, LASTERR);\r
+out:\r
+ free(wszLibraryName);\r
+ return h;\r
+}\r
+\r
+#define PF_TYPE(api, ret, proc, args) typedef ret (api *proc##_t)args\r
+#define PF_DECL(proc) static proc##_t pf##proc = NULL\r
+#define PF_TYPE_DECL(api, ret, proc, args) PF_TYPE(api, ret, proc, args); PF_DECL(proc)\r
+#define PF_INIT(proc, name) if (pf##proc == NULL) pf##proc = \\r
+ (proc##_t) GetProcAddress(GetLibraryHandle(#name), #proc)\r
+#define PF_INIT_OR_OUT(proc, name) do {PF_INIT(proc, name); \\r
+ if (pf##proc == NULL) {Log("Unable to locate %s() in '%s.dll': %u", \\r
+ #proc, #name, LASTERR); goto out;} } while(0)\r
+#define PF_INIT_OR_SET_STATUS(proc, name) do {PF_INIT(proc, name); \\r
+ if ((pf##proc == NULL) && (NT_SUCCESS(status))) status = STATUS_NOT_IMPLEMENTED; } while(0)\r
+\r
+static BOOL is_x64(void)\r
+{\r
+ BOOL ret = FALSE;\r
+ PF_TYPE_DECL(WINAPI, BOOL, IsWow64Process, (HANDLE, PBOOL));\r
+ // Detect if we're running a 32 or 64 bit system\r
+ if (sizeof(uintptr_t) < 8) {\r
+ PF_INIT(IsWow64Process, Kernel32);\r
+ if (pfIsWow64Process != NULL) {\r
+ (*pfIsWow64Process)(GetCurrentProcess(), &ret);\r
+ }\r
+ }\r
+ else {\r
+ ret = TRUE;\r
+ }\r
+ return ret;\r
+}\r
+\r
+\r
+static __inline UINT GetSystemWindowsDirectoryU(char* lpBuffer, UINT uSize)\r
+{\r
+ UINT ret = 0, err = ERROR_INVALID_DATA;\r
+ // coverity[returned_null]\r
+ walloc(lpBuffer, uSize);\r
+ ret = GetSystemWindowsDirectoryW(wlpBuffer, uSize);\r
+ err = GetLastError();\r
+ if ((ret != 0) && ((ret = wchar_to_utf8_no_alloc(wlpBuffer, lpBuffer, uSize)) == 0)) {\r
+ err = GetLastError();\r
+ }\r
+ wfree(lpBuffer);\r
+ SetLastError(err);\r
+ return ret;\r
+}\r
+\r
+static __inline HMODULE LoadLibraryU(LPCSTR lpFileName)\r
+{\r
+ HMODULE ret;\r
+ DWORD err = ERROR_INVALID_DATA;\r
+ wconvert(lpFileName);\r
+ ret = LoadLibraryW(wlpFileName);\r
+ err = GetLastError();\r
+ wfree(lpFileName);\r
+ SetLastError(err);\r
+ return ret;\r
+}\r
+\r
+\r
+BOOL SetAlertPromptMessages(void)\r
+{\r
+ HMODULE hMui;\r
+ char mui_path[MAX_PATH];\r
+\r
+ if (GetSystemDirectoryU(system_dir, sizeof(system_dir)) == 0) {\r
+ Log("Could not get system directory: %u", LASTERR);\r
+ static_strcpy(system_dir, "C:\\Windows\\System32");\r
+ }\r
+\r
+ // Construct Sysnative ourselves as there is no GetSysnativeDirectory() call\r
+ // By default (64bit app running on 64 bit OS or 32 bit app running on 32 bit OS)\r
+ // Sysnative and System32 are the same\r
+ static_strcpy(sysnative_dir, system_dir);\r
+ // But if the app is 32 bit and the OS is 64 bit, Sysnative must differ from System32\r
+#if (defined(VTARCH_X86) || defined(VTARCH_ARM))\r
+ if (is_x64()) {\r
+ if (GetSystemWindowsDirectoryU(sysnative_dir, sizeof(sysnative_dir)) == 0) {\r
+ Log("Could not get Windows directory: %u", LASTERR);\r
+ static_strcpy(sysnative_dir, "C:\\Windows");\r
+ }\r
+ static_strcat(sysnative_dir, "\\Sysnative");\r
+ }\r
+#endif\r
+\r
+ Log("system_dir=<%s>", system_dir);\r
+ Log("sysnative_dir=<%s>", sysnative_dir);\r
+\r
+ sprintf_s(mui_path, MAX_PATH, "%s\\%s\\shell32.dll.mui", sysnative_dir, ToLocaleName(GetUserDefaultUILanguage()));\r
+\r
+ hMui = LoadLibraryU(mui_path);\r
+ if (hMui)\r
+ {\r
+ // 4097 = "You need to format the disk in drive %c: before you can use it." (dialog text)\r
+ // 4125 = "Microsoft Windows" (dialog title)\r
+ // 4126 = "Format disk" (button)\r
+ if (LoadStringU(hMui, 4125, title_str[0], sizeof(title_str[0])) <= 0) {\r
+ static_strcpy(title_str[0], "Microsoft Windows");\r
+ Log("Warning: Could not locate localized format prompt title string in '%s': %u", mui_path, LASTERR);\r
+ }\r
+ if (LoadStringU(hMui, 4126, button_str, sizeof(button_str)) <= 0) {\r
+ static_strcpy(button_str, "Format disk");\r
+ Log("Warning: Could not locate localized format prompt button string in '%s': %u", mui_path, LASTERR);\r
+ }\r
+ FreeLibrary(hMui);\r
+\r
+ Log("LoadLibrary shell32.dll.mui SUCCESS");\r
+ }\r
+ else\r
+ {\r
+ Log("LoadLibrary shell32.dll.mui FAILED");\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+static __inline int GetWindowTextU(HWND hWnd, char* lpString, int nMaxCount)\r
+{\r
+ int ret = 0;\r
+ DWORD err = ERROR_INVALID_DATA;\r
+ if (nMaxCount < 0)\r
+ return 0;\r
+ // Handle the empty string as GetWindowTextW() returns 0 then\r
+ if ((lpString != NULL) && (nMaxCount > 0))\r
+ lpString[0] = 0;\r
+ // coverity[returned_null]\r
+ walloc(lpString, nMaxCount);\r
+ ret = GetWindowTextW(hWnd, wlpString, nMaxCount);\r
+ err = GetLastError();\r
+ // coverity[var_deref_model]\r
+ if ((ret != 0) && ((ret = wchar_to_utf8_no_alloc(wlpString, lpString, nMaxCount)) == 0)) {\r
+ err = GetLastError();\r
+ }\r
+ wfree(lpString);\r
+ lpString[nMaxCount - 1] = 0;\r
+ SetLastError(err);\r
+ return ret;\r
+}\r
+\r
+\r
+/*\r
+ * The following function calls are used to automatically detect and close the native\r
+ * Windows format prompt "You must format the disk in drive X:". To do that, we use\r
+ * an event hook that gets triggered whenever a window is placed in the foreground.\r
+ * In that hook, we look for a dialog that has style WS_POPUPWINDOW and has the relevant\r
+ * title. However, because the title in itself is too generic (the expectation is that\r
+ * it will be "Microsoft Windows") we also enumerate all the child controls from that\r
+ * prompt, using another callback, until we find one that contains the text we expect\r
+ * for the "Format disk" button.\r
+ * Oh, and since all of these strings are localized, we must first pick them up from\r
+ * the relevant mui's.\r
+ */\r
+static BOOL CALLBACK AlertPromptCallback(HWND hWnd, LPARAM lParam)\r
+{\r
+ char str[128];\r
+ BOOL* found = (BOOL*)lParam;\r
+\r
+ if (GetWindowTextU(hWnd, str, sizeof(str)) == 0)\r
+ return TRUE;\r
+ if (strcmp(str, button_str) == 0)\r
+ *found = TRUE;\r
+ return TRUE;\r
+}\r
+\r
+static void CALLBACK AlertPromptHook(HWINEVENTHOOK hWinEventHook, DWORD Event, HWND hWnd, LONG idObject, LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime)\r
+{\r
+ char str[128];\r
+ BOOL found;\r
+\r
+ if (Event == EVENT_SYSTEM_FOREGROUND) {\r
+ if (GetWindowLongPtr(hWnd, GWL_STYLE) & WS_POPUPWINDOW) {\r
+ str[0] = 0;\r
+ GetWindowTextU(hWnd, str, sizeof(str));\r
+ if (strcmp(str, title_str[0]) == 0) {\r
+ found = FALSE;\r
+ EnumChildWindows(hWnd, AlertPromptCallback, (LPARAM)&found);\r
+ if (found) {\r
+ SendMessage(hWnd, WM_COMMAND, (WPARAM)IDCANCEL, (LPARAM)0);\r
+ Log("###### Closed Windows format prompt #######");\r
+ }\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+\r
+BOOL SetAlertPromptHook(void)\r
+{\r
+ if (ap_weh != NULL)\r
+ return TRUE; // No need to set again if active\r
+ ap_weh = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, NULL,\r
+ AlertPromptHook, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);\r
+ return (ap_weh != NULL);\r
+}\r
+\r
+BOOL AlertSuppressInit(void)\r
+{\r
+ BOOL bRet;\r
+\r
+ SetAlertPromptMessages();\r
+\r
+ bRet = SetAlertPromptHook();\r
+ Log("SetAlertPromptHook %s", bRet ? "SUCCESS" : "FAILED");\r
+\r
+ return TRUE;\r
+}
\ No newline at end of file