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
This commit is contained in:
Руслан Ижбулатов
2014-07-29 08:18:32 +00:00
parent 31d08bd85e
commit 0d02cc8203
7 changed files with 138 additions and 1 deletions

View File

@ -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

View File

@ -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 \

View File

@ -28,3 +28,4 @@ VS_VERSION_INFO VERSIONINFO
VALUE "Translation", 0x409, 1200
END
END
ISOLATIONAWARE_MANIFEST_RESOURCE_ID RT_MANIFEST libgtk3.manifest

View File

@ -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;

View File

@ -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__ */

View File

@ -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)
{

21
gtk/libgtk3.manifest.in Normal file
View File

@ -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>