Skip to content

Commit 70883c5

Browse files
committed
Add new setting ApplicationSettings.app_user_model_id (cztomczak#395)
1 parent 8a3a77a commit 70883c5

File tree

5 files changed

+135
-30
lines changed

5 files changed

+135
-30
lines changed

api/ApplicationSettings.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Table of contents:
88
* [Introduction](#introduction)
99
* [Settings](#settings)
1010
* [accept_language_list](#accept_language_list)
11+
* [app_user_model_id](#app_user_model_id)
1112
* [auto_zooming](#auto_zooming)
1213
* [background_color](#background_color)
1314
* [browser_subprocess_path](#browser_subprocess_path)
@@ -67,6 +68,19 @@ If both values are empty then "en-US,en" will be used. Can be overridden
6768
for individual CefRequestContext instances via the
6869
CefRequestContextSettings.accept_language_list value.
6970

71+
72+
### app_user_model_id
73+
74+
This is setting is applied only on Windows.
75+
It sets [AppUserModelID](https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459(v=vs.85).aspx)
76+
(also known as AppID) for all processes (Browser, Renderer, GPU, etc.).
77+
Setting this is required on Windows 10 to workaround issues related to
78+
pinning app to Taskbar. More details can be found in [Issue #395](https://github.com/cztomczak/cefpython/issues/395).
79+
80+
Internally this setting will append "--app-user-model-id" switch to
81+
all processes.
82+
83+
7084
### auto_zooming
7185

7286
(string)

src/cefpython.pyx

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,18 @@ def Initialize(applicationSettings=None, commandLineSwitches=None, **kwargs):
599599
for kwarg in kwargs:
600600
raise Exception("Invalid argument: "+kwarg)
601601

602+
if command_line_switches:
603+
# Make a copy as commandLineSwitches is a reference only
604+
# that might get destroyed later.
605+
global g_commandLineSwitches
606+
for key in command_line_switches:
607+
g_commandLineSwitches[key] = copy.deepcopy(
608+
command_line_switches[key])
609+
# Use g_commandLineSwitches if you need to modify or access
610+
# command line switches inside this function.
611+
del command_line_switches
612+
del commandLineSwitches
613+
602614
IF UNAME_SYSNAME == "Linux":
603615
# Fix Issue #231 - Discovery of the "icudtl.dat" file fails on Linux.
604616
cdef str py_module_dir = GetModuleDirectory()
@@ -636,8 +648,9 @@ def Initialize(applicationSettings=None, commandLineSwitches=None, **kwargs):
636648
IF UNAME_SYSNAME == "Darwin":
637649
MacInitialize()
638650

639-
# -------------------------------------------------------------------------
640-
# CEF Python only options - default values
651+
# ------------------------------------------------------------------------
652+
# CEF Python only options
653+
# ------------------------------------------------------------------------
641654

642655
if "debug" not in application_settings:
643656
application_settings["debug"] = False
@@ -657,8 +670,13 @@ def Initialize(applicationSettings=None, commandLineSwitches=None, **kwargs):
657670
IF UNAME_SYSNAME == "Windows":
658671
if DpiAware.IsProcessDpiAware():
659672
application_settings["auto_zooming"] = "system_dpi"
673+
if "app_user_model_id" in application_settings:
674+
g_commandLineSwitches["app-user-model-id"] =\
675+
application_settings["app_user_model_id"]
660676

677+
# ------------------------------------------------------------------------
661678
# Paths
679+
# ------------------------------------------------------------------------
662680
cdef str module_dir = GetModuleDirectory()
663681
if platform.system() == "Darwin":
664682
if "framework_dir_path" not in application_settings:
@@ -679,7 +697,9 @@ def Initialize(applicationSettings=None, commandLineSwitches=None, **kwargs):
679697
application_settings["browser_subprocess_path"] = os.path.join(
680698
module_dir, "subprocess")
681699

700+
# ------------------------------------------------------------------------
682701
# Mouse context menu
702+
# ------------------------------------------------------------------------
683703
if "context_menu" not in application_settings:
684704
application_settings["context_menu"] = {}
685705
menuItems = ["enabled", "navigation", "print", "view_source",
@@ -688,8 +708,11 @@ def Initialize(applicationSettings=None, commandLineSwitches=None, **kwargs):
688708
if item not in application_settings["context_menu"]:
689709
application_settings["context_menu"][item] = True
690710

691-
# Remote debugging port. If value is 0 we will generate a random
692-
# port. To disable remote debugging set value to -1.
711+
# ------------------------------------------------------------------------
712+
# Remote debugging port.
713+
# ------------------------------------------------------------------------
714+
# If value is 0 we will generate a random port. To disable
715+
# remote debugging set value to -1.
693716
if application_settings["remote_debugging_port"] == 0:
694717
# Generate a random port.
695718
application_settings["remote_debugging_port"] =\
@@ -698,13 +721,14 @@ def Initialize(applicationSettings=None, commandLineSwitches=None, **kwargs):
698721
# Disable remote debugging
699722
application_settings["remote_debugging_port"] = 0
700723

701-
# -------------------------------------------------------------------------
702-
703-
# CEF options - default values.
724+
# ------------------------------------------------------------------------
725+
# CEF options - default values
726+
# ------------------------------------------------------------------------
704727
if not "multi_threaded_message_loop" in application_settings:
705728
application_settings["multi_threaded_message_loop"] = False
706729
if not "single_process" in application_settings:
707730
application_settings["single_process"] = False
731+
# ------------------------------------------------------------------------
708732

709733
cdef CefRefPtr[CefApp] cefApp = <CefRefPtr[CefApp]?>new CefPythonApp()
710734

@@ -733,14 +757,6 @@ def Initialize(applicationSettings=None, commandLineSwitches=None, **kwargs):
733757
cefApplicationSettings.no_sandbox = 1
734758
SetApplicationSettings(application_settings, &cefApplicationSettings)
735759

736-
if command_line_switches:
737-
# Make a copy as commandLineSwitches is a reference only
738-
# that might get destroyed later.
739-
global g_commandLineSwitches
740-
for key in command_line_switches:
741-
g_commandLineSwitches[key] = copy.deepcopy(
742-
command_line_switches[key])
743-
744760
# External message pump
745761
if GetAppSetting("external_message_pump")\
746762
and not g_external_message_pump.get():

src/settings.pyx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ cdef void SetApplicationSettings(
3636
or key == "unique_request_context_per_browser"\
3737
or key == "downloads_enabled"\
3838
or key == "context_menu" \
39-
or key == "auto_zooming":
39+
or key == "auto_zooming"\
40+
or key == "app_user_model_id":
4041
# CEF Python only options. These are not to be found in CEF.
4142
continue
4243
elif key == "accept_language_list":

src/subprocess/cefpython_app.cpp

Lines changed: 70 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,19 @@
99
#include "common/cefpython_public_api.h"
1010
#endif
1111

12+
#if defined(OS_WIN)
13+
#include <Shobjidl.h>
14+
#pragma comment(lib, "Shell32.lib")
15+
#endif // OS_WIN
16+
1217
#ifdef BROWSER_PROCESS
1318
#ifdef OS_LINUX
1419
#include <gtk/gtk.h>
1520
#include <gdk/gdk.h>
1621
#include <gdk/gdkx.h>
1722
#include "print_handler_gtk.h"
18-
#endif
19-
#endif
23+
#endif // OS_LINUX
24+
#endif // BROWSER_PROCESS
2025

2126
#include "cefpython_app.h"
2227
#include "util.h"
@@ -49,39 +54,91 @@ CefPythonApp::CefPythonApp() {
4954
void CefPythonApp::OnBeforeCommandLineProcessing(
5055
const CefString& process_type,
5156
CefRefPtr<CefCommandLine> command_line) {
57+
// IMPORTANT NOTES
58+
// ---------------
59+
// NOTE 1: Currently CEF logging is limited and you cannot log
60+
// info messages during execution of this function. The
61+
// default log severity in Chromium is LOG_ERROR, thus
62+
// you can will only see log messages when using LOG(ERROR)
63+
// here. You won't see LOG(WARNING) nor LOG(INFO) messages
64+
// here.
65+
// NOTE 2: The "g_debug" variable is never set at this moment
66+
// due to IPC messaging delay. There is some code here
67+
// that depends on this variable, but it is currently
68+
// never executed.
69+
5270
#ifdef BROWSER_PROCESS
5371
// This is included only in the Browser process, when building
5472
// the libcefpythonapp library.
5573
if (process_type.empty()) {
5674
// Empty proc type, so this must be the main browser process.
5775
App_OnBeforeCommandLineProcessing_BrowserProcess(command_line);
5876
}
59-
#endif
77+
#endif // BROWSER_PROCESS
78+
79+
// IMPORTANT: This code is currently dead due to g_debug and
80+
// LOG(INFO) issues described at the top of this
81+
// function. Command line string for subprocesses are
82+
// are currently logged in OnBeforeChildProcess().
83+
// Command line string for the main browser process
84+
// is currently never logged.
6085
std::string process_name = process_type.ToString();
6186
if (process_name.empty()) {
6287
process_name = "browser";
6388
}
6489
#ifdef BROWSER_PROCESS
6590
std::string logMessage = "[Browser process] ";
66-
#else
91+
#else // BROWSER_PROCESS
6792
std::string logMessage = "[Non-browser process] ";
68-
#endif
93+
#endif // BROWSER_PROCESS
6994
logMessage.append("Command line string for the ");
7095
logMessage.append(process_name);
7196
logMessage.append(" process: ");
7297
std::string clString = command_line->GetCommandLineString().ToString();
7398
logMessage.append(clString.c_str());
74-
// There is a bug in upstream CEF, log settings are initialized
75-
// after OnBeforeCommandLineProcessing. So if g_debug is not
76-
// checked it would always log msg even though logging info is
77-
// disabled. However this issue does not matter, because command
78-
// line is also logged in OnBeforeChildProcessLaunch().
79-
// In OnBeforeCommandLineProcessing() command line for browser
80-
// process is logged and in OnBeforeChildProcessLaunch() command
81-
// line for other processes is logged.
8299
if (g_debug) {
100+
// This code is currently never executed, see the "IMPORTANT"
101+
// comment above and comments at the top of this function.
83102
LOG(INFO) << logMessage.c_str();
84103
}
104+
105+
#ifdef OS_WIN
106+
// Set AppUserModelID (AppID) when "--app-user-model-id" switch
107+
// is provided. This fixes pinning to taskbar issues on Windows 10.
108+
// See Issue #395 for details.
109+
//
110+
// AppID here is set in all processes (Browser, Renderer, GPU, etc.).
111+
//
112+
// When compiling under Python 2.7 (VS2008) using Visual C++ for
113+
// Python, it seems you can't use WINSDK 7 header files even though
114+
// WINVER was specified in build_cpp_projects.py macros. Thus calling
115+
// using proc address. Specifying full path to Shell32.dll is not
116+
// required, in Chromium's source code full paths are not used.
117+
//
118+
// Note:
119+
// subprocess.exe is built with UNICODE defined, however the
120+
// cefpython_app library that is shared between subprocess and
121+
// python process does not define UNICODE macro. Thus it is required
122+
// to call LoadLibraryW explicitilly here.
123+
HINSTANCE shell32 = LoadLibraryW(L"shell32.dll");
124+
CefString app_id = command_line->GetSwitchValue("app-user-model-id");
125+
if (app_id.length()) {
126+
typedef HRESULT (WINAPI *SetAppUserModelID_Type)(PCWSTR);
127+
static SetAppUserModelID_Type SetAppUserModelID;
128+
SetAppUserModelID = (SetAppUserModelID_Type)GetProcAddress(
129+
shell32, "SetCurrentProcessExplicitAppUserModelID");
130+
HRESULT hr = (*SetAppUserModelID)(app_id.ToWString().c_str());
131+
if (hr == S_OK) {
132+
if (g_debug) {
133+
// This code is currently never executed, see comments
134+
// at the top of this function.
135+
LOG(INFO) << "SetCurrentProcessExplicitAppUserModelID ok";
136+
}
137+
} else {
138+
LOG(ERROR) << "SetCurrentProcessExplicitAppUserModelID failed";
139+
}
140+
}
141+
#endif // OS_WIN
85142
}
86143

87144
void CefPythonApp::OnRegisterCustomSchemes(

tools/build_cpp_projects.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,15 @@
2121
from distutils.ccompiler import new_compiler
2222
from common import *
2323
import shutil
24+
from pprint import pprint
2425

2526
# Macros
2627
MACROS = [
2728
"WIN32", "_WIN32", "_WINDOWS",
28-
("WINVER", "0x0601"), ("_WIN32_WINNT", "0x0601"), # Windows 7+
29+
# Windows 7+ minimum supported
30+
("NTDDI_VERSION", "0x06010000"),
31+
("WINVER", "0x0601"),
32+
("_WIN32_WINNT", "0x0601"),
2933
"NDEBUG", "_NDEBUG",
3034
"_CRT_SECURE_NO_WARNINGS",
3135
]
@@ -61,6 +65,7 @@
6165
def main():
6266
command_line_args()
6367
clean_build_directories_if_forced()
68+
print_compiler_options()
6469
build_cefpython_app_library()
6570
build_library(lib_name="client_handler",
6671
macros=MACROS,
@@ -90,6 +95,18 @@ def clean_build_directories_if_forced():
9095
shutil.rmtree(bdir)
9196

9297

98+
def print_compiler_options():
99+
compiler = get_compiler()
100+
print("[build_cpp.projects] Shared macros:")
101+
pprint(MACROS, indent=3, width=160)
102+
print("[build_cpp.projects] cefpython_app library macros:")
103+
pprint(cefpython_app_MACROS, indent=3, width=160)
104+
print("[build_cpp.projects] subprocess executable macros:")
105+
pprint(subprocess_MACROS, indent=3, width=160)
106+
print("[build_cpp.projects] Compiler options:")
107+
pprint(vars(compiler), indent=3, width=160)
108+
109+
93110
def get_compiler(static=False):
94111
# NOTES:
95112
# - VS2008 and VS2010 are both using distutils/msvc9compiler.py

0 commit comments

Comments
 (0)