build: adding patch for glib!2020.

This is a patch for issue #913, the infamous "non-existent floppy drive"
or other unreachable network or unmounted drives. Well at least it
*should* fix the issue, but GLib developers are hoping we could test. So
let's add this in there for our next Windows package.

(cherry picked from commit 7e2a0238a2)
This commit is contained in:
Jehan
2021-04-07 13:28:11 +02:00
parent dbc3139759
commit 2c182ed5eb

View File

@ -0,0 +1,270 @@
From d1c542254fe44bdd6ad43574adb571222465b126 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=A0=D1=83=D1=81=D0=BB=D0=B0=D0=BD=20=D0=98=D0=B6=D0=B1?=
=?UTF-8?q?=D1=83=D0=BB=D0=B0=D1=82=D0=BE=D0=B2?= <lrn1986@gmail.com>
Date: Tue, 30 Mar 2021 19:39:20 +0000
Subject: [PATCH 1/2] GWin32Mount: Don't use SHGetFileInfoW() for icons
This function can cause significant delays when the mounted volume
is disconnected or just weird. Use IExtractIconW::GetIconLocation()
instead.
Theoretically, this should require COM to be initialized, but in my tests
this code worked just fine without calling CoInitializeEx().
---
gio/gwin32mount.c | 141 ++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 136 insertions(+), 5 deletions(-)
diff --git a/gio/gwin32mount.c b/gio/gwin32mount.c
index 83d8695a1..6388beb11 100644
--- a/gio/gwin32mount.c
+++ b/gio/gwin32mount.c
@@ -25,7 +25,47 @@
#include <string.h>
#define WIN32_MEAN_AND_LEAN
+#define COBJMACROS
#include <windows.h>
+#include <shlobj.h>
+#include <shlwapi.h>
+
+/* At the moment of writing IExtractIconW interface in Mingw-w64
+ * is missing IUnknown members in its vtable. Use our own
+ * fixed declaration for now.
+ */
+#undef INTERFACE
+#define INTERFACE IMyExtractIconW
+DECLARE_INTERFACE_(IMyExtractIconW,IUnknown)
+{
+ /*** IUnknown methods ***/
+ STDMETHOD_(HRESULT,QueryInterface)(THIS_ REFIID riid, void** ppvObject) PURE;
+ STDMETHOD_(ULONG,AddRef)(THIS) PURE;
+ STDMETHOD_(ULONG,Release)(THIS) PURE;
+ /*** IMyExtractIconW methods ***/
+ STDMETHOD(GetIconLocation)(THIS_ UINT uFlags, LPWSTR pszIconFile, UINT cchMax, int *piIndex, PUINT pwFlags) PURE;
+ STDMETHOD(Extract)(THIS_ LPCWSTR pszFile, UINT nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIconSize) PURE;
+};
+#undef INTERFACE
+
+#if !defined(__cplusplus) || defined(CINTERFACE)
+/*** IUnknown methods ***/
+#define IMyExtractIconW_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b)
+#define IMyExtractIconW_AddRef(p) (p)->lpVtbl->AddRef(p)
+#define IMyExtractIconW_Release(p) (p)->lpVtbl->Release(p)
+/*** IMyExtractIconW methods ***/
+#define IMyExtractIconW_GetIconLocation(p,a,b,c,d,e) (p)->lpVtbl->GetIconLocation(p,a,b,c,d,e)
+#define IMyExtractIconW_Extract(p,a,b,c,d,e) (p)->lpVtbl->Extract(p,a,b,c,d,e)
+#else
+/*** IUnknown methods ***/
+#define IMyExtractIconW_QueryInterface(p,a,b) (p)->QueryInterface(a,b)
+#define IMyExtractIconW_AddRef(p) (p)->AddRef()
+#define IMyExtractIconW_Release(p) (p)->Release()
+/*** IMyExtractIconW methods ***/
+#define IMyExtractIconW_GetIconLocation(p,a,b,c,d,e) (p)->GetIconLocation(p,a,b,c,d,e)
+#define IMyExtractIconW_Extract(p,a,b,c,d,e) (p)->Extract(p,a,b,c,d,e)
+#endif
+
#include <glib.h>
#include "gwin32volumemonitor.h"
@@ -210,6 +250,92 @@ _win32_drive_type_to_icon (int type, gboolean use_symbolic)
}
}
+/* mount_path doesn't need to end with a path separator.
+ mount_path must use backslashes as path separators, not slashes.
+ IShellFolder::ParseDisplayName() takes non-const string as input,
+ so mount_path can't be a const string.
+ result_name and result_index must not be NULL.
+ Returns TRUE when result_name is set (free with g_free),
+ FALSE otherwise.
+ */
+static gboolean
+get_icon_name_index (wchar_t *mount_path,
+ wchar_t **result_name,
+ int *result_index)
+{
+ IShellFolder *desktop;
+ PIDLIST_RELATIVE volume;
+ IShellFolder *volume_parent;
+ PCUITEMID_CHILD volume_relative;
+ IMyExtractIconW *eicon;
+ int icon_index;
+ UINT icon_flags;
+ wchar_t *name_buffer;
+ gsize name_buffer_size;
+ gsize arbitrary_reasonable_limit = 5000;
+ gboolean result = FALSE;
+
+ *result_name = NULL;
+
+ /* Get the desktop folder object reference */
+ if (!SUCCEEDED (SHGetDesktopFolder (&desktop)))
+ return FALSE;
+
+ /* Construct the volume IDList relative to desktop */
+ if (SUCCEEDED (IShellFolder_ParseDisplayName (desktop, NULL, NULL, mount_path, NULL, &volume, NULL)))
+ {
+ /* Get the parent of the volume (transfer-full) and the IDList relative to parent (transfer-none) */
+ if (SUCCEEDED (SHBindToParent (volume, &IID_IShellFolder, (void **) &volume_parent, &volume_relative)))
+ {
+ /* Get a reference to IExtractIcon object for the volume */
+ if (SUCCEEDED (IShellFolder_GetUIObjectOf (volume_parent, NULL, 1, (LPCITEMIDLIST *) &volume_relative, &IID_IExtractIconW, NULL, (void **) &eicon)))
+ {
+ gboolean keep_going = TRUE;
+ name_buffer = NULL;
+ name_buffer_size = MAX_PATH / 2;
+ while (keep_going)
+ {
+ name_buffer_size *= 2;
+ name_buffer = g_renew (wchar_t, name_buffer, name_buffer_size);
+ name_buffer[name_buffer_size - 1] = 0x1; /* sentinel */
+ keep_going = FALSE;
+
+ /* Try to get the icon location */
+ if (SUCCEEDED (IMyExtractIconW_GetIconLocation (eicon, GIL_FORSHELL, name_buffer, name_buffer_size, &icon_index, &icon_flags)))
+ {
+ if (name_buffer[name_buffer_size - 1] != 0x1)
+ {
+ if (name_buffer_size < arbitrary_reasonable_limit)
+ {
+ /* Buffer was too small, keep going */
+ keep_going = TRUE;
+ continue;
+ }
+ /* Else stop trying */
+ }
+ /* name_buffer might not contain a name */
+ else if ((icon_flags & GIL_NOTFILENAME) != GIL_NOTFILENAME)
+ {
+ *result_name = g_steal_pointer (&name_buffer);
+ *result_index = icon_index;
+ result = TRUE;
+ }
+ }
+ }
+
+ g_free (name_buffer);
+ IMyExtractIconW_Release (eicon);
+ }
+ IShellFolder_Release (volume_parent);
+ }
+ CoTaskMemFree (volume);
+ }
+
+ IShellFolder_Release (desktop);
+
+ return result;
+}
+
static GIcon *
g_win32_mount_get_icon (GMount *mount)
{
@@ -220,15 +346,20 @@ g_win32_mount_get_icon (GMount *mount)
/* lazy creation */
if (!win32_mount->icon)
{
- SHFILEINFOW shfi;
+ wchar_t *icon_path;
+ int icon_index;
+ wchar_t *p;
wchar_t *wfn = g_utf8_to_utf16 (win32_mount->mount_path, -1, NULL, NULL, NULL);
- if (SHGetFileInfoW (wfn, 0, &shfi, sizeof (shfi), SHGFI_ICONLOCATION))
+ for (p = wfn; p != NULL && *p != 0; p++)
+ if (*p == L'/')
+ *p = L'\\';
+
+ if (get_icon_name_index (wfn, &icon_path, &icon_index))
{
- gchar *name = g_utf16_to_utf8 (shfi.szDisplayName, -1, NULL, NULL, NULL);
- gchar *id = g_strdup_printf ("%s,%i", name, shfi.iIcon);
+ gchar *id = g_strdup_printf ("%S,%i", icon_path, icon_index);
+ g_free (icon_path);
win32_mount->icon = g_themed_icon_new (id);
- g_free (name);
g_free (id);
}
else
--
GitLab
From cb994350c427b8c5e514ecb26fd036d889cfd839 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=A0=D1=83=D1=81=D0=BB=D0=B0=D0=BD=20=D0=98=D0=B6=D0=B1?=
=?UTF-8?q?=D1=83=D0=BB=D0=B0=D1=82=D0=BE=D0=B2?= <lrn1986@gmail.com>
Date: Tue, 30 Mar 2021 20:27:25 +0000
Subject: [PATCH 2/2] GWin32Mount: Don't use SHGetFileInfoW() for mount
displaynames
This function can create long delays when used on disconnected
or just weird volumes. Use IShellFolder::GetDisplayNameOf() instead.
Fixes #2096
---
gio/gwin32mount.c | 46 ++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 42 insertions(+), 4 deletions(-)
diff --git a/gio/gwin32mount.c b/gio/gwin32mount.c
index 6388beb11..f5be8adfa 100644
--- a/gio/gwin32mount.c
+++ b/gio/gwin32mount.c
@@ -143,14 +143,52 @@ g_win32_mount_init (GWin32Mount *win32_mount)
{
}
+/* wdrive doesn't need to end with a path separator.
+ wdrive must use backslashes as path separators, not slashes.
+ IShellFolder::ParseDisplayName() takes non-const string as input,
+ so wdrive can't be a const string.
+ Returns the name on success (free with g_free),
+ NULL otherwise.
+ */
+static gchar *
+get_mount_display_name (gunichar2 *wdrive)
+{
+ IShellFolder *desktop;
+ PIDLIST_RELATIVE volume;
+ STRRET volume_name;
+ gchar *result = NULL;
+
+ /* Get the desktop folder object reference */
+ if (!SUCCEEDED (SHGetDesktopFolder (&desktop)))
+ return result;
+
+ if (SUCCEEDED (IShellFolder_ParseDisplayName (desktop, NULL, NULL, wdrive, NULL, &volume, NULL)))
+ {
+ volume_name.uType = STRRET_WSTR;
+
+ if (SUCCEEDED (IShellFolder_GetDisplayNameOf (desktop, volume, SHGDN_FORADDRESSBAR, &volume_name)))
+ {
+ wchar_t *volume_name_wchar;
+
+ if (SUCCEEDED (StrRetToStrW (&volume_name, volume, &volume_name_wchar)))
+ {
+ result = g_utf16_to_utf8 (volume_name_wchar, -1, NULL, NULL, NULL);
+ CoTaskMemFree (volume_name_wchar);
+ }
+ }
+ CoTaskMemFree (volume);
+ }
+
+ IShellFolder_Release (desktop);
+
+ return result;
+}
+
static gchar *
_win32_get_displayname (const char *drive)
{
gunichar2 *wdrive = g_utf8_to_utf16 (drive, -1, NULL, NULL, NULL);
- gchar *name = NULL;
- SHFILEINFOW sfi;
- if (SHGetFileInfoW(wdrive, 0, &sfi, sizeof(sfi), SHGFI_DISPLAYNAME))
- name = g_utf16_to_utf8 (sfi.szDisplayName, -1, NULL, NULL, NULL);
+ gchar *name = get_mount_display_name (wdrive);
g_free (wdrive);
return name ? name : g_strdup (drive);
--
GitLab