[gtk+] Make sure native W32 print dialog uses visual styles



commit 0d02cc8203e1a43506c0e81f392492aedadb6fa3
Author: Руслан Ижбулатов <lrn1986 gmail com>
Date:   Tue Jul 29 08:18:32 2014 +0000

    Make sure native W32 print dialog uses visual styles
    
    For that to happen the libgtk3 is embedded with a manifest that requests
    common controls library 6.x, and GTK lazily calls InitCommonControlsEx()
    to initialize those. Then this manifest is used to temporarily override
    the process activation contest when loading comdlg32 (which contains the
    code for the print dialog), ensuring that it too depends on common
    controls 6.x, even if the application that uses GTK does not.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=733773

 configure.ac                  |    1 +
 gtk/Makefile.am               |    3 +-
 gtk/gtk-win32.rc.in           |    1 +
 gtk/gtkprintoperation-win32.c |   19 +++++++++
 gtk/gtkprivate.h              |    4 ++
 gtk/gtkwin32.c                |   90 +++++++++++++++++++++++++++++++++++++++++
 gtk/libgtk3.manifest.in       |   21 +++++++++
 7 files changed, 138 insertions(+), 1 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 891094d..314dacf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1894,6 +1894,7 @@ gtk/Makefile
 gtk/makefile.msc
 gtk/gtkversion.h
 gtk/gtk-win32.rc
+gtk/libgtk3.manifest
 gtk/inspector/Makefile
 gtk/native/Makefile
 util/Makefile
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 2f74d2e..5c31084 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -90,7 +90,7 @@ gtk_win32_symbols = -export-symbols $(srcdir)/gtk.def
 gtk_win32_res = gtk-win32-res.o
 gtk_win32_res_ldflag = -Wl,gtk-win32-res.o
 
-gtk-win32-res.o : gtk-win32.rc
+gtk-win32-res.o : gtk-win32.rc libgtk3.manifest
        $(WINDRES) gtk-win32.rc $@
 
 gtk.def: libgtk-3.la
@@ -1726,6 +1726,7 @@ EXTRA_DIST +=                   \
        $(adwaita_sources)      \
        $(win32_theme_sources)  \
        $(gsettings_SCHEMAS)    \
+       libgtk3.manifest.in     \
        gtk-win32.rc.in         \
        gtkwin32embed.h         \
        gtkwin32embedwidget.h   \
diff --git a/gtk/gtk-win32.rc.in b/gtk/gtk-win32.rc.in
index 4a42d5a..dc9464a 100644
--- a/gtk/gtk-win32.rc.in
+++ b/gtk/gtk-win32.rc.in
@@ -28,3 +28,4 @@ VS_VERSION_INFO VERSIONINFO
       VALUE "Translation", 0x409, 1200
     END
   END
+ISOLATIONAWARE_MANIFEST_RESOURCE_ID RT_MANIFEST libgtk3.manifest
diff --git a/gtk/gtkprintoperation-win32.c b/gtk/gtkprintoperation-win32.c
index e59eb9f..3ac6bc4 100644
--- a/gtk/gtkprintoperation-win32.c
+++ b/gtk/gtkprintoperation-win32.c
@@ -1657,6 +1657,25 @@ gtk_print_operation_run_with_dialog (GtkPrintOperation *op,
   GtkPrintOperationPrivate *priv;
   IPrintDialogCallback *callback;
   HPROPSHEETPAGE prop_page;
+  static volatile gsize common_controls_initialized = 0;
+
+  if (g_once_init_enter (&common_controls_initialized))
+    {
+      BOOL initialized;
+      INITCOMMONCONTROLSEX icc;
+
+      memset (&icc, 0, sizeof (icc));
+      icc.dwSize = sizeof (icc);
+      icc.dwICC = ICC_WIN95_CLASSES;
+
+      initialized = InitCommonControlsEx (&icc);
+      if (!initialized)
+        g_warning ("Failed to InitCommonControlsEx: %lu\n", GetLastError ());
+
+      _gtk_load_dll_with_libgtk3_manifest ("comdlg32.dll");
+
+      g_once_init_leave (&common_controls_initialized, initialized ? 1 : 0);
+    }
   
   *do_print = FALSE;
 
diff --git a/gtk/gtkprivate.h b/gtk/gtkprivate.h
index a07f729..5fe7d88 100644
--- a/gtk/gtkprivate.h
+++ b/gtk/gtkprivate.h
@@ -91,6 +91,10 @@ gboolean        _gtk_propagate_captured_event  (GtkWidget       *widget,
                                                 GdkEvent        *event,
                                                 GtkWidget       *topmost);
 
+#ifdef G_OS_WIN32
+void _gtk_load_dll_with_libgtk3_manifest (const char *dllname);
+#endif
+
 G_END_DECLS
 
 #endif /* __GTK_PRIVATE_H__ */
diff --git a/gtk/gtkwin32.c b/gtk/gtkwin32.c
index e8bd7ef..c33659e 100644
--- a/gtk/gtkwin32.c
+++ b/gtk/gtkwin32.c
@@ -30,8 +30,15 @@
 
 #define STRICT
 #include <windows.h>
+#include <commctrl.h>
 #undef STRICT
 
+/* In practice, resulting DLL will have manifest resource under index 2.
+ * Fall back to that value if we can't find resource index programmatically.
+ */
+#define EMPIRIC_MANIFEST_RESOURCE_INDEX 2
+
+
 static HMODULE gtk_dll;
 
 BOOL WINAPI
@@ -49,6 +56,89 @@ DllMain (HINSTANCE hinstDLL,
   return TRUE;
 }
 
+static BOOL CALLBACK
+find_first_manifest (HMODULE  module_handle,
+                     LPCSTR   resource_type,
+                     LPSTR    resource_name,
+                     LONG_PTR user_data)
+{
+  LPSTR *result_name = (LPSTR *) user_data;
+
+  if (resource_type == RT_MANIFEST)
+    {
+      if (IS_INTRESOURCE (resource_name))
+        *result_name = resource_name;
+      else
+        *result_name = g_strdup (resource_name);
+      return FALSE;
+    }
+  return TRUE;
+}
+
+/**
+ * Grabs the first manifest it finds in libgtk3 (which is expected to be the
+ * common-controls-6.0.0.0 manifest we embedded to enable visual styles),
+ * uses it to create a process-default activation context, activates that
+ * context, loads up the library passed in @dllname, then deactivates and
+ * releases the context.
+ *
+ * In practice this is used to force system DLLs (like comdlg32) to be
+ * loaded as if the application had the same manifest as libgtk3
+ * (otherwise libgtk3 manifest only affests libgtk3 itself).
+ * This way application does not need to have a manifest or to link
+ * against comctl32.
+ *
+ * Note that loaded library handle leaks, so only use this function in
+ * g_once_init_enter (leaking once is OK, Windows will clean up after us).
+ */
+void
+_gtk_load_dll_with_libgtk3_manifest (const gchar *dll_name)
+{
+  HANDLE activation_ctx_handle;
+  ACTCTXA activation_ctx_descriptor;
+  ULONG_PTR activation_cookie;
+  LPSTR resource_name;
+  BOOL activated;
+
+  resource_name = NULL;
+  EnumResourceNames (gtk_dll, RT_MANIFEST, find_first_manifest,
+                     (LONG_PTR) &resource_name);
+
+  if (resource_name == NULL)
+    resource_name = MAKEINTRESOURCEA (EMPIRIC_MANIFEST_RESOURCE_INDEX);
+
+  memset (&activation_ctx_descriptor, 0, sizeof (activation_ctx_descriptor));
+  activation_ctx_descriptor.cbSize = sizeof (activation_ctx_descriptor);
+  activation_ctx_descriptor.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID |
+                                      ACTCTX_FLAG_HMODULE_VALID |
+                                      ACTCTX_FLAG_SET_PROCESS_DEFAULT;
+  activation_ctx_descriptor.hModule = gtk_dll;
+  activation_ctx_descriptor.lpResourceName = resource_name;
+  activation_ctx_handle = CreateActCtx (&activation_ctx_descriptor);
+
+  if (activation_ctx_handle == INVALID_HANDLE_VALUE)
+    g_warning ("Failed to CreateActCtx for module %p, resource %p: %lu\n",
+               gtk_dll, resource_name, GetLastError ());
+  else
+    {
+      activation_cookie = 0;
+      activated = ActivateActCtx (activation_ctx_handle, &activation_cookie);
+
+      if (!activated)
+        g_warning ("Failed to ActivateActCtx: %lu\n", GetLastError ());
+
+      LoadLibraryA (dll_name);
+
+      if (activated && !DeactivateActCtx (0, activation_cookie))
+        g_warning ("Failed to DeactivateActCtx: %lu\n", GetLastError ());
+
+      ReleaseActCtx (activation_ctx_handle);
+    }
+
+  if (!IS_INTRESOURCE (resource_name))
+    g_free (resource_name);
+}
+
 const gchar *
 _gtk_get_libdir (void)
 {
diff --git a/gtk/libgtk3.manifest.in b/gtk/libgtk3.manifest.in
new file mode 100644
index 0000000..6e1677e
--- /dev/null
+++ b/gtk/libgtk3.manifest.in
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+  <assemblyIdentity
+      version="1.0.0.0"
+      processorArchitecture="@EXE_MANIFEST_ARCHITECTURE@"
+      name="libgtk3"
+      type="win32"
+  />
+  <dependency>
+    <dependentAssembly>
+      <assemblyIdentity
+          type="win32"
+          name="Microsoft.Windows.Common-Controls"
+          version="6.0.0.0"
+          processorArchitecture="@EXE_MANIFEST_ARCHITECTURE@"
+          publicKeyToken="6595b64144ccf1df"
+          language="*"
+      />
+    </dependentAssembly>
+  </dependency>
+</assembly>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]