| Index: chrome/app/chrome_exe_main_app_win.cc
|
| diff --git a/chrome/app/chrome_exe_main_app_win.cc b/chrome/app/chrome_exe_main_app_win.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..25d33e97885bdf2e0f8d9af527dfe290f6f7e76c
|
| --- /dev/null
|
| +++ b/chrome/app/chrome_exe_main_app_win.cc
|
| @@ -0,0 +1,315 @@
|
| +// Copyright 2016 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "chrome/app/chrome_exe_main_app_win.h"
|
| +
|
| +#include <memory>
|
| +
|
| +#include "base/win/scoped_co_mem.h"
|
| +#include "chrome/installer/util/install_util.h"
|
| +#include "chrome/installer/util/shell_util.h"
|
| +
|
| +// 2017/04/04: For this to work we also need
|
| +// HKEY_CLASSES_ROOT\CLSID\{E65AECC7-DD9B-4D14-A4ED-73A5BEF1187A}\LocalServer32
|
| +// to be assigned command line to launch Chrome.
|
| +
|
| +#if 0
|
| +// #include <roapi.h>
|
| +// #include <wchar.h>
|
| +#include <NotificationActivationCallback.h>
|
| +#include <propvarutil.h>
|
| +#include <psapi.h>
|
| +#include <shobjidl.h>
|
| +#include <wrl.h>
|
| +
|
| +#include <cstring>
|
| +#include <memory>
|
| +#include <string>
|
| +
|
| +namespace mswr = Microsoft::WRL;
|
| +// namespace mswrw = Microsoft::WRL::Wrappers;
|
| +
|
| +// namespace winapp = ABI::Windows::ApplicationModel;
|
| +// namespace winxaml = ABI::Windows::UI::Xaml;
|
| +// namespace winact = ABI::Windows::ApplicationModel::Activation;
|
| +// namespace winfoundtn = ABI::Windows::Foundation;
|
| +// namespace winui = ABI::Windows::UI;
|
| +
|
| +#else
|
| +
|
| +// #include <SDKDDKVer.h>
|
| +#include <Windows.h>
|
| +#include <Psapi.h>
|
| +// #include <strsafe.h>
|
| +#include <ShObjIdl.h>
|
| +#include <Shlobj.h>
|
| +#include <Pathcch.h>
|
| +#include <propvarutil.h>
|
| +#include <propkey.h>
|
| +#include <wrl.h>
|
| +#include <wrl\wrappers\corewrappers.h>
|
| +#include <windows.ui.notifications.h>
|
| +#include "NotificationActivationCallback.h"
|
| +
|
| +// using namespace ABI::Windows::Data::Xml::Dom;
|
| +using namespace ABI::Windows::UI::Notifications;
|
| +using namespace Microsoft::WRL;
|
| +using namespace Microsoft::WRL::Wrappers;
|
| +
|
| +#endif
|
| +
|
| +namespace {
|
| +
|
| +base::string16 GetChromeAppId() {
|
| + bool is_per_user_install = InstallUtil::IsPerUserInstall();
|
| + base::string16 appid = ShellUtil::GetBrowserModelId(is_per_user_install);
|
| + DVLOG(1) << "Chrome Appid is " << appid.c_str();
|
| + return appid;
|
| +}
|
| +
|
| +
|
| +// Name: System.AppUserModel.ToastActivatorCLSID --
|
| +// PKEY_AppUserModel_ToastActivatorCLSID
|
| +// Type: Guid -- VT_CLSID
|
| +// FormatID: {9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}, 26
|
| +//
|
| +// Used to CoCreate an INotificationActivationCallback interface to notify
|
| +// about toast activations.
|
| +EXTERN_C const PROPERTYKEY DECLSPEC_SELECTANY
|
| + PKEY_AppUserModel_ToastActivatorCLSID = {
|
| + {0x9F4C2855,
|
| + 0x9F79,
|
| + 0x4B39,
|
| + {0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3}},
|
| + 26};
|
| +
|
| +struct CoTaskMemStringTraits {
|
| + typedef PWSTR Type;
|
| +
|
| + inline static bool Close(_In_ Type h) throw() {
|
| + ::CoTaskMemFree(h);
|
| + return true;
|
| + }
|
| +
|
| + inline static Type GetInvalidValue() throw() {
|
| + return nullptr;
|
| + }
|
| +};
|
| +typedef HandleT<CoTaskMemStringTraits> CoTaskMemString;
|
| +
|
| +// For the app to be activated from Action Center, it needs to provide a COM
|
| +// server to be called
|
| +// when the notification is activated. The CLSID of the object needs to be
|
| +// registered with the
|
| +// OS via its shortcut so that it knows who to call later.
|
| +class DECLSPEC_UUID("E65AECC7-DD9B-4D14-A4ED-73A5BEF1187A")
|
| + NotificationActivator WrlSealed
|
| + : public RuntimeClass<RuntimeClassFlags<ClassicCom>,
|
| + INotificationActivationCallback> {
|
| + public:
|
| + HRESULT STDMETHODCALLTYPE Activate(
|
| + _In_ LPCWSTR /*appUserModelId*/,
|
| + _In_ LPCWSTR invokedArgs,
|
| + /*_In_reads_(dataCount)*/ const NOTIFICATION_USER_INPUT_DATA* data,
|
| + ULONG dataCount) override {
|
| + ::MessageBoxW(NULL, invokedArgs, L"", MB_OK);
|
| + return S_OK;
|
| + }
|
| +};
|
| +CoCreatableClass(NotificationActivator);
|
| +
|
| +// In order to display toasts, a desktop application must have a shortcut on the
|
| +// Start menu.
|
| +// Also, an AppUserModelID must be set on that shortcut.
|
| +//
|
| +// For the app to be activated from Action Center, it needs to register a COM
|
| +// server with the OS
|
| +// and register the CLSID of that COM server on the shortcut.
|
| +//
|
| +// The shortcut should be created as part of the installer. The following code
|
| +// shows how to create
|
| +// a shortcut and assign the AppUserModelID and ToastActivatorCLSID properties
|
| +// using Windows APIs.
|
| +//
|
| +// Included in this project is a wxs file that be used with the WiX toolkit
|
| +// to make an installer that creates the necessary shortcut. One or the other
|
| +// should be used.
|
| +//
|
| +// This sample doesn't clean up the shortcut or COM registration.
|
| +
|
| +_Use_decl_annotations_ HRESULT
|
| +InstallShortcut(PCWSTR shortcutPath, PCWSTR exePath, PCWSTR arguments) {
|
| + MessageBoxW(NULL, L"InstallShortcut()", L"Title", MB_OK);
|
| + ComPtr<IShellLink> shellLink;
|
| + HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
|
| + IID_PPV_ARGS(&shellLink));
|
| +
|
| + base::string16 app_id = GetChromeAppId();
|
| +
|
| + if (SUCCEEDED(hr)) {
|
| + hr = shellLink->SetPath(exePath);
|
| +
|
| + if (SUCCEEDED(hr)) {
|
| + hr = shellLink->SetArguments(arguments);
|
| +
|
| + if (SUCCEEDED(hr)) {
|
| + ComPtr<IPropertyStore> propertyStore;
|
| +
|
| + hr = shellLink.As(&propertyStore);
|
| + if (SUCCEEDED(hr)) {
|
| + PROPVARIANT propVar;
|
| + propVar.vt = VT_LPWSTR;
|
| + propVar.pwszVal = const_cast<PWSTR>(
|
| + app_id.c_str()); // for _In_ scenarios, we don't need a copy
|
| + hr = propertyStore->SetValue(PKEY_AppUserModel_ID, propVar);
|
| + if (SUCCEEDED(hr)) {
|
| + propVar.vt = VT_CLSID;
|
| + propVar.puuid =
|
| + const_cast<CLSID*>(&__uuidof(NotificationActivator));
|
| + hr = propertyStore->SetValue(PKEY_AppUserModel_ToastActivatorCLSID,
|
| + propVar);
|
| + if (SUCCEEDED(hr)) {
|
| + hr = propertyStore->Commit();
|
| + if (SUCCEEDED(hr)) {
|
| + ComPtr<IPersistFile> persistFile;
|
| + hr = shellLink.As(&persistFile);
|
| + if (SUCCEEDED(hr)) {
|
| + hr = persistFile->Save(shortcutPath, TRUE);
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| + if (!SUCCEEDED(hr)) {
|
| + MessageBoxA(NULL, "InstallShortcut() failed.", "Title", MB_OK);
|
| + }
|
| + return hr;
|
| +}
|
| +
|
| +_Use_decl_annotations_ HRESULT
|
| +RegisterComServer(PCWSTR exePath) {
|
| + // We don't need to worry about overflow here as ::GetModuleFileName won't
|
| + // return anything bigger than the max file system path (much fewer than max
|
| + // of DWORD).
|
| + DWORD dataSize = static_cast<DWORD>((::wcslen(exePath) + 1) * sizeof(WCHAR));
|
| +
|
| + // In this sample, the app UI is registered to launch when the COM callback is
|
| + // needed.
|
| + // Other options might be to launch a background process instead that then
|
| + // decides to launch
|
| + // the UI if needed by that particular notification.
|
| + return HRESULT_FROM_WIN32(::RegSetKeyValue(
|
| + HKEY_CURRENT_USER,
|
| + LR"(SOFTWARE\Classes\CLSID\{E65AECC7-DD9B-4D14-A4ED-73A5BEF1187A}"
|
| + LR"\LocalServer32)",
|
| + nullptr, REG_SZ, reinterpret_cast<const BYTE*>(exePath), dataSize));
|
| +}
|
| +
|
| +HRESULT RegisterAppForNotificationSupport() {
|
| + CoTaskMemString appData;
|
| +
|
| + auto hr = ::SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, nullptr,
|
| + appData.GetAddressOf());
|
| + if (SUCCEEDED(hr)) {
|
| + wchar_t shortcutPath[MAX_PATH];
|
| + hr = ::PathCchCombine(
|
| + shortcutPath, ARRAYSIZE(shortcutPath), appData.Get(),
|
| + LR"(Microsoft\Windows\Start Menu\Programs\Tester\tester.lnk)");
|
| + if (SUCCEEDED(hr)) {
|
| + // MessageBoxW(NULL, shortcutPath, L"Title", MB_OK);
|
| + DWORD attributes = ::GetFileAttributes(shortcutPath);
|
| + bool fileExists = attributes < 0xFFFFFFF;
|
| + if (!fileExists) {
|
| + wchar_t exePath[MAX_PATH];
|
| + DWORD charWritten =
|
| + ::GetModuleFileName(nullptr, exePath, ARRAYSIZE(exePath));
|
| + MessageBoxW(NULL, exePath, L"Title", MB_OK);
|
| + hr = charWritten > 0 ? S_OK : HRESULT_FROM_WIN32(::GetLastError());
|
| + if (SUCCEEDED(hr)) {
|
| + const wchar_t* arguments = L"--enable-features=NativeNotifications "
|
| + L"--user-data-dir=R:\\p";
|
| + hr = InstallShortcut(shortcutPath, exePath, arguments);
|
| + if (SUCCEEDED(hr)) {
|
| + hr = RegisterComServer(exePath);
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| + if (!SUCCEEDED(hr)) {
|
| + MessageBoxA(NULL, "RegisterAppForNotificationSupport() failed.", "Title",
|
| + MB_OK);
|
| + }
|
| + return hr;
|
| +}
|
| +
|
| +// Register activator for notifications
|
| +HRESULT RegisterActivator() {
|
| + // Module<OutOfProc> needs a callback registered before it can be used.
|
| + // Since we don't care about when it shuts down, we'll pass an empty lambda
|
| + // here.
|
| + Module<OutOfProc>::Create([] {});
|
| +
|
| + // If a local server process only hosts the COM object then COM expects
|
| + // the COM server host to shutdown when the references drop to zero.
|
| + // Since the user might still be using the program after activating the
|
| + // notification,
|
| + // we don't want to shutdown immediately. Incrementing the object count tells
|
| + // COM that
|
| + // we aren't done yet.
|
| + Module<OutOfProc>::GetModule().IncrementObjectCount();
|
| +
|
| + return Module<OutOfProc>::GetModule().RegisterObjects();
|
| +}
|
| +
|
| +// Unregister our activator COM object
|
| +void UnregisterActivator() {
|
| + Module<OutOfProc>::GetModule().UnregisterObjects();
|
| +
|
| + Module<OutOfProc>::GetModule().DecrementObjectCount();
|
| +}
|
| +
|
| +class HackyRegister {
|
| + public:
|
| + HackyRegister() {
|
| + }
|
| + HRESULT Run() {
|
| + HRESULT hr = RegisterActivator();
|
| + if (SUCCEEDED(hr))
|
| + has_reg = true;
|
| + return hr;
|
| + }
|
| + ~HackyRegister() {
|
| + if (has_reg)
|
| + UnregisterActivator();
|
| + }
|
| + private:
|
| + bool has_reg = false;
|
| +};
|
| +
|
| +std::unique_ptr<HackyRegister> my_reg;
|
| +
|
| +} // namespace
|
| +
|
| +void PrepareChromeForWindows10() {
|
| + RoInitializeWrapper winRtInitializer(RO_INIT_MULTITHREADED);
|
| +
|
| + // Experimental code: Attempt to respond to notificaiton events, using idea
|
| + // from https://github.com/WindowsNotifications/desktop-toasts
|
| + HRESULT hr = winRtInitializer;
|
| + if (!SUCCEEDED(hr))
|
| + return;
|
| +
|
| + hr = RegisterAppForNotificationSupport();
|
| + if (SUCCEEDED(hr)) {
|
| + my_reg.reset(new HackyRegister());
|
| + hr = my_reg->Run();
|
| + if (SUCCEEDED(hr)) {
|
| + // ::MessageBoxA(NULL, "Registered", "Title", MB_OK);
|
| + }
|
| + }
|
| +}
|
|
|