Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
michael@0 | 4 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | |
michael@0 | 7 | #include "CEHHelper.h" |
michael@0 | 8 | |
michael@0 | 9 | #include <objbase.h> |
michael@0 | 10 | #include <combaseapi.h> |
michael@0 | 11 | #include <atlcore.h> |
michael@0 | 12 | #include <atlstr.h> |
michael@0 | 13 | #include <wininet.h> |
michael@0 | 14 | #include <shlobj.h> |
michael@0 | 15 | #include <shlwapi.h> |
michael@0 | 16 | #include <propkey.h> |
michael@0 | 17 | #include <propvarutil.h> |
michael@0 | 18 | #include <stdio.h> |
michael@0 | 19 | #include <stdlib.h> |
michael@0 | 20 | #include <strsafe.h> |
michael@0 | 21 | #include <io.h> |
michael@0 | 22 | #include <shellapi.h> |
michael@0 | 23 | |
michael@0 | 24 | #ifdef SHOW_CONSOLE |
michael@0 | 25 | #define DEBUG_DELAY_SHUTDOWN 1 |
michael@0 | 26 | #endif |
michael@0 | 27 | |
michael@0 | 28 | // Heartbeat timer duration used while waiting for an incoming request. |
michael@0 | 29 | #define HEARTBEAT_MSEC 250 |
michael@0 | 30 | // Total number of heartbeats we wait before giving up and shutting down. |
michael@0 | 31 | #define REQUEST_WAIT_TIMEOUT 30 |
michael@0 | 32 | // Pulled from desktop browser's shell |
michael@0 | 33 | #define APP_REG_NAME L"Firefox" |
michael@0 | 34 | |
michael@0 | 35 | const WCHAR* kFirefoxExe = L"firefox.exe"; |
michael@0 | 36 | static const WCHAR* kDefaultMetroBrowserIDPathKey = L"FirefoxURL"; |
michael@0 | 37 | static const WCHAR* kMetroRestartCmdLine = L"--metro-restart"; |
michael@0 | 38 | static const WCHAR* kMetroUpdateCmdLine = L"--metro-update"; |
michael@0 | 39 | static const WCHAR* kDesktopRestartCmdLine = L"--desktop-restart"; |
michael@0 | 40 | static const WCHAR* kNsisLaunchCmdLine = L"--launchmetro"; |
michael@0 | 41 | static const WCHAR* kExplorerLaunchCmdLine = L"-Embedding"; |
michael@0 | 42 | |
michael@0 | 43 | static bool GetDefaultBrowserPath(CStringW& aPathBuffer); |
michael@0 | 44 | |
michael@0 | 45 | /* |
michael@0 | 46 | * Retrieve our module dir path. |
michael@0 | 47 | * |
michael@0 | 48 | * @aPathBuffer Buffer to fill |
michael@0 | 49 | */ |
michael@0 | 50 | static bool GetModulePath(CStringW& aPathBuffer) |
michael@0 | 51 | { |
michael@0 | 52 | WCHAR buffer[MAX_PATH]; |
michael@0 | 53 | memset(buffer, 0, sizeof(buffer)); |
michael@0 | 54 | |
michael@0 | 55 | if (!GetModuleFileName(nullptr, buffer, MAX_PATH)) { |
michael@0 | 56 | Log(L"GetModuleFileName failed."); |
michael@0 | 57 | return false; |
michael@0 | 58 | } |
michael@0 | 59 | |
michael@0 | 60 | WCHAR* slash = wcsrchr(buffer, '\\'); |
michael@0 | 61 | if (!slash) |
michael@0 | 62 | return false; |
michael@0 | 63 | *slash = '\0'; |
michael@0 | 64 | |
michael@0 | 65 | aPathBuffer = buffer; |
michael@0 | 66 | return true; |
michael@0 | 67 | } |
michael@0 | 68 | |
michael@0 | 69 | |
michael@0 | 70 | template <class T>void SafeRelease(T **ppT) |
michael@0 | 71 | { |
michael@0 | 72 | if (*ppT) { |
michael@0 | 73 | (*ppT)->Release(); |
michael@0 | 74 | *ppT = nullptr; |
michael@0 | 75 | } |
michael@0 | 76 | } |
michael@0 | 77 | |
michael@0 | 78 | template <class T> HRESULT SetInterface(T **ppT, IUnknown *punk) |
michael@0 | 79 | { |
michael@0 | 80 | SafeRelease(ppT); |
michael@0 | 81 | return punk ? punk->QueryInterface(ppT) : E_NOINTERFACE; |
michael@0 | 82 | } |
michael@0 | 83 | |
michael@0 | 84 | class __declspec(uuid("5100FEC1-212B-4BF5-9BF8-3E650FD794A3")) |
michael@0 | 85 | CExecuteCommandVerb : public IExecuteCommand, |
michael@0 | 86 | public IObjectWithSelection, |
michael@0 | 87 | public IInitializeCommand, |
michael@0 | 88 | public IObjectWithSite, |
michael@0 | 89 | public IExecuteCommandApplicationHostEnvironment |
michael@0 | 90 | { |
michael@0 | 91 | public: |
michael@0 | 92 | |
michael@0 | 93 | CExecuteCommandVerb() : |
michael@0 | 94 | mRef(0), |
michael@0 | 95 | mShellItemArray(nullptr), |
michael@0 | 96 | mUnkSite(nullptr), |
michael@0 | 97 | mTargetIsFileSystemLink(false), |
michael@0 | 98 | mTargetIsDefaultBrowser(false), |
michael@0 | 99 | mTargetIsBrowser(false), |
michael@0 | 100 | mRequestType(DEFAULT_LAUNCH), |
michael@0 | 101 | mRequestMet(false), |
michael@0 | 102 | mDelayedLaunchType(NONE), |
michael@0 | 103 | mVerb(L"open") |
michael@0 | 104 | { |
michael@0 | 105 | } |
michael@0 | 106 | |
michael@0 | 107 | ~CExecuteCommandVerb() |
michael@0 | 108 | { |
michael@0 | 109 | } |
michael@0 | 110 | |
michael@0 | 111 | bool RequestMet() { return mRequestMet; } |
michael@0 | 112 | void SetRequestMet(); |
michael@0 | 113 | long RefCount() { return mRef; } |
michael@0 | 114 | void HeartBeat(); |
michael@0 | 115 | |
michael@0 | 116 | // IUnknown |
michael@0 | 117 | IFACEMETHODIMP QueryInterface(REFIID aRefID, void **aInt) |
michael@0 | 118 | { |
michael@0 | 119 | static const QITAB qit[] = { |
michael@0 | 120 | QITABENT(CExecuteCommandVerb, IExecuteCommand), |
michael@0 | 121 | QITABENT(CExecuteCommandVerb, IObjectWithSelection), |
michael@0 | 122 | QITABENT(CExecuteCommandVerb, IInitializeCommand), |
michael@0 | 123 | QITABENT(CExecuteCommandVerb, IObjectWithSite), |
michael@0 | 124 | QITABENT(CExecuteCommandVerb, IExecuteCommandApplicationHostEnvironment), |
michael@0 | 125 | { 0 }, |
michael@0 | 126 | }; |
michael@0 | 127 | return QISearch(this, qit, aRefID, aInt); |
michael@0 | 128 | } |
michael@0 | 129 | |
michael@0 | 130 | IFACEMETHODIMP_(ULONG) AddRef() |
michael@0 | 131 | { |
michael@0 | 132 | return InterlockedIncrement(&mRef); |
michael@0 | 133 | } |
michael@0 | 134 | |
michael@0 | 135 | IFACEMETHODIMP_(ULONG) Release() |
michael@0 | 136 | { |
michael@0 | 137 | long cRef = InterlockedDecrement(&mRef); |
michael@0 | 138 | if (!cRef) { |
michael@0 | 139 | delete this; |
michael@0 | 140 | } |
michael@0 | 141 | return cRef; |
michael@0 | 142 | } |
michael@0 | 143 | |
michael@0 | 144 | // IExecuteCommand |
michael@0 | 145 | IFACEMETHODIMP SetKeyState(DWORD aKeyState) |
michael@0 | 146 | { |
michael@0 | 147 | mKeyState = aKeyState; |
michael@0 | 148 | return S_OK; |
michael@0 | 149 | } |
michael@0 | 150 | |
michael@0 | 151 | IFACEMETHODIMP SetParameters(PCWSTR aParameters) |
michael@0 | 152 | { |
michael@0 | 153 | Log(L"SetParameters: '%s'", aParameters); |
michael@0 | 154 | |
michael@0 | 155 | if (!_wcsicmp(aParameters, kMetroRestartCmdLine)) { |
michael@0 | 156 | mRequestType = METRO_RESTART; |
michael@0 | 157 | } else if (_wcsicmp(aParameters, kMetroUpdateCmdLine) == 0) { |
michael@0 | 158 | mRequestType = METRO_UPDATE; |
michael@0 | 159 | } else if (_wcsicmp(aParameters, kDesktopRestartCmdLine) == 0) { |
michael@0 | 160 | mRequestType = DESKTOP_RESTART; |
michael@0 | 161 | } else { |
michael@0 | 162 | mParameters = aParameters; |
michael@0 | 163 | } |
michael@0 | 164 | return S_OK; |
michael@0 | 165 | } |
michael@0 | 166 | |
michael@0 | 167 | IFACEMETHODIMP SetPosition(POINT aPoint) |
michael@0 | 168 | { return S_OK; } |
michael@0 | 169 | |
michael@0 | 170 | IFACEMETHODIMP SetShowWindow(int aShowFlag) |
michael@0 | 171 | { return S_OK; } |
michael@0 | 172 | |
michael@0 | 173 | IFACEMETHODIMP SetNoShowUI(BOOL aNoUI) |
michael@0 | 174 | { return S_OK; } |
michael@0 | 175 | |
michael@0 | 176 | IFACEMETHODIMP SetDirectory(PCWSTR aDirPath) |
michael@0 | 177 | { return S_OK; } |
michael@0 | 178 | |
michael@0 | 179 | IFACEMETHODIMP Execute(); |
michael@0 | 180 | |
michael@0 | 181 | // IObjectWithSelection |
michael@0 | 182 | IFACEMETHODIMP SetSelection(IShellItemArray *aArray) |
michael@0 | 183 | { |
michael@0 | 184 | if (!aArray) { |
michael@0 | 185 | return E_FAIL; |
michael@0 | 186 | } |
michael@0 | 187 | |
michael@0 | 188 | SetInterface(&mShellItemArray, aArray); |
michael@0 | 189 | |
michael@0 | 190 | DWORD count = 0; |
michael@0 | 191 | aArray->GetCount(&count); |
michael@0 | 192 | if (!count) { |
michael@0 | 193 | return E_FAIL; |
michael@0 | 194 | } |
michael@0 | 195 | |
michael@0 | 196 | #ifdef SHOW_CONSOLE |
michael@0 | 197 | Log(L"SetSelection param count: %d", count); |
michael@0 | 198 | for (DWORD idx = 0; idx < count; idx++) { |
michael@0 | 199 | IShellItem* item = nullptr; |
michael@0 | 200 | if (SUCCEEDED(aArray->GetItemAt(idx, &item))) { |
michael@0 | 201 | LPWSTR str = nullptr; |
michael@0 | 202 | if (FAILED(item->GetDisplayName(SIGDN_FILESYSPATH, &str))) { |
michael@0 | 203 | if (FAILED(item->GetDisplayName(SIGDN_URL, &str))) { |
michael@0 | 204 | Log(L"Failed to get a shell item array item."); |
michael@0 | 205 | item->Release(); |
michael@0 | 206 | continue; |
michael@0 | 207 | } |
michael@0 | 208 | } |
michael@0 | 209 | item->Release(); |
michael@0 | 210 | Log(L"SetSelection param: '%s'", str); |
michael@0 | 211 | CoTaskMemFree(str); |
michael@0 | 212 | } |
michael@0 | 213 | } |
michael@0 | 214 | #endif |
michael@0 | 215 | |
michael@0 | 216 | IShellItem* item = nullptr; |
michael@0 | 217 | if (FAILED(aArray->GetItemAt(0, &item))) { |
michael@0 | 218 | return E_FAIL; |
michael@0 | 219 | } |
michael@0 | 220 | |
michael@0 | 221 | bool isFileSystem = false; |
michael@0 | 222 | if (!SetTargetPath(item) || !mTarget.GetLength()) { |
michael@0 | 223 | Log(L"SetTargetPath failed."); |
michael@0 | 224 | return E_FAIL; |
michael@0 | 225 | } |
michael@0 | 226 | item->Release(); |
michael@0 | 227 | |
michael@0 | 228 | Log(L"SetSelection target: %s", mTarget); |
michael@0 | 229 | return S_OK; |
michael@0 | 230 | } |
michael@0 | 231 | |
michael@0 | 232 | IFACEMETHODIMP GetSelection(REFIID aRefID, void **aInt) |
michael@0 | 233 | { |
michael@0 | 234 | *aInt = nullptr; |
michael@0 | 235 | return mShellItemArray ? mShellItemArray->QueryInterface(aRefID, aInt) : E_FAIL; |
michael@0 | 236 | } |
michael@0 | 237 | |
michael@0 | 238 | // IInitializeCommand |
michael@0 | 239 | IFACEMETHODIMP Initialize(PCWSTR aVerb, IPropertyBag* aPropBag) |
michael@0 | 240 | { |
michael@0 | 241 | if (!aVerb) |
michael@0 | 242 | return E_FAIL; |
michael@0 | 243 | // 'open', 'edit', etc. Based on our registry settings |
michael@0 | 244 | Log(L"Initialize(%s)", aVerb); |
michael@0 | 245 | mVerb = aVerb; |
michael@0 | 246 | return S_OK; |
michael@0 | 247 | } |
michael@0 | 248 | |
michael@0 | 249 | // IObjectWithSite |
michael@0 | 250 | IFACEMETHODIMP SetSite(IUnknown *aUnkSite) |
michael@0 | 251 | { |
michael@0 | 252 | SetInterface(&mUnkSite, aUnkSite); |
michael@0 | 253 | return S_OK; |
michael@0 | 254 | } |
michael@0 | 255 | |
michael@0 | 256 | IFACEMETHODIMP GetSite(REFIID aRefID, void **aInt) |
michael@0 | 257 | { |
michael@0 | 258 | *aInt = nullptr; |
michael@0 | 259 | return mUnkSite ? mUnkSite->QueryInterface(aRefID, aInt) : E_FAIL; |
michael@0 | 260 | } |
michael@0 | 261 | |
michael@0 | 262 | // IExecuteCommandApplicationHostEnvironment |
michael@0 | 263 | IFACEMETHODIMP GetValue(AHE_TYPE *aLaunchType) |
michael@0 | 264 | { |
michael@0 | 265 | Log(L"IExecuteCommandApplicationHostEnvironment::GetValue()"); |
michael@0 | 266 | *aLaunchType = GetLaunchType(); |
michael@0 | 267 | return S_OK; |
michael@0 | 268 | } |
michael@0 | 269 | |
michael@0 | 270 | /** |
michael@0 | 271 | * Choose the appropriate launch type based on the user's previously chosen |
michael@0 | 272 | * host environment, along with system constraints. |
michael@0 | 273 | * |
michael@0 | 274 | * AHE_DESKTOP = 0, AHE_IMMERSIVE = 1 |
michael@0 | 275 | */ |
michael@0 | 276 | AHE_TYPE GetLaunchType() |
michael@0 | 277 | { |
michael@0 | 278 | AHE_TYPE ahe = GetLastAHE(); |
michael@0 | 279 | Log(L"Previous AHE: %d", ahe); |
michael@0 | 280 | |
michael@0 | 281 | // Default launch settings from GetLastAHE() can be overriden by |
michael@0 | 282 | // custom parameter values we receive. |
michael@0 | 283 | if (mRequestType == DESKTOP_RESTART) { |
michael@0 | 284 | Log(L"Restarting in desktop host environment."); |
michael@0 | 285 | return AHE_DESKTOP; |
michael@0 | 286 | } else if (mRequestType == METRO_RESTART) { |
michael@0 | 287 | Log(L"Restarting in metro host environment."); |
michael@0 | 288 | ahe = AHE_IMMERSIVE; |
michael@0 | 289 | } else if (mRequestType == METRO_UPDATE) { |
michael@0 | 290 | // Shouldn't happen from GetValue above, but might from other calls. |
michael@0 | 291 | ahe = AHE_IMMERSIVE; |
michael@0 | 292 | } |
michael@0 | 293 | |
michael@0 | 294 | if (ahe == AHE_IMMERSIVE) { |
michael@0 | 295 | if (!IsDefaultBrowser()) { |
michael@0 | 296 | Log(L"returning AHE_DESKTOP because we are not the default browser"); |
michael@0 | 297 | return AHE_DESKTOP; |
michael@0 | 298 | } |
michael@0 | 299 | |
michael@0 | 300 | if (!IsDX10Available()) { |
michael@0 | 301 | Log(L"returning AHE_DESKTOP because DX10 is not available"); |
michael@0 | 302 | return AHE_DESKTOP; |
michael@0 | 303 | } |
michael@0 | 304 | } |
michael@0 | 305 | return ahe; |
michael@0 | 306 | } |
michael@0 | 307 | |
michael@0 | 308 | bool DefaultLaunchIsDesktop() |
michael@0 | 309 | { |
michael@0 | 310 | return GetLaunchType() == AHE_DESKTOP; |
michael@0 | 311 | } |
michael@0 | 312 | |
michael@0 | 313 | bool DefaultLaunchIsMetro() |
michael@0 | 314 | { |
michael@0 | 315 | return GetLaunchType() == AHE_IMMERSIVE; |
michael@0 | 316 | } |
michael@0 | 317 | |
michael@0 | 318 | /* |
michael@0 | 319 | * Retrieve the target path if it is the default browser |
michael@0 | 320 | * or if not default, retreives the target path if it is a firefox browser |
michael@0 | 321 | * or if the target is not firefox, relies on a hack to get the |
michael@0 | 322 | * 'module dir path\firefox.exe' |
michael@0 | 323 | * The reason why it's not good to rely on the CEH path is because there is |
michael@0 | 324 | * no guarantee win8 will use the CEH at our expected path. It has an in |
michael@0 | 325 | * memory cache even if the registry is updated for the CEH path. |
michael@0 | 326 | * |
michael@0 | 327 | * @aPathBuffer Buffer to fill |
michael@0 | 328 | */ |
michael@0 | 329 | bool GetDesktopBrowserPath(CStringW& aPathBuffer) |
michael@0 | 330 | { |
michael@0 | 331 | // If the target was the default browser itself then return early. Otherwise |
michael@0 | 332 | // rely on a hack to check CEH path and calculate it relative to it. |
michael@0 | 333 | |
michael@0 | 334 | if (mTargetIsDefaultBrowser || mTargetIsBrowser) { |
michael@0 | 335 | aPathBuffer = mTarget; |
michael@0 | 336 | return true; |
michael@0 | 337 | } |
michael@0 | 338 | |
michael@0 | 339 | if (!GetModulePath(aPathBuffer)) |
michael@0 | 340 | return false; |
michael@0 | 341 | |
michael@0 | 342 | // ceh.exe sits in dist/bin root with the desktop browser. Since this |
michael@0 | 343 | // is a firefox only component, this hardcoded filename is ok. |
michael@0 | 344 | aPathBuffer.Append(L"\\"); |
michael@0 | 345 | aPathBuffer.Append(kFirefoxExe); |
michael@0 | 346 | return true; |
michael@0 | 347 | } |
michael@0 | 348 | |
michael@0 | 349 | bool IsDefaultBrowser() |
michael@0 | 350 | { |
michael@0 | 351 | IApplicationAssociationRegistration* pAAR; |
michael@0 | 352 | HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration, |
michael@0 | 353 | nullptr, |
michael@0 | 354 | CLSCTX_INPROC, |
michael@0 | 355 | IID_IApplicationAssociationRegistration, |
michael@0 | 356 | (void**)&pAAR); |
michael@0 | 357 | if (FAILED(hr)) |
michael@0 | 358 | return false; |
michael@0 | 359 | |
michael@0 | 360 | BOOL res = FALSE; |
michael@0 | 361 | hr = pAAR->QueryAppIsDefaultAll(AL_EFFECTIVE, |
michael@0 | 362 | APP_REG_NAME, |
michael@0 | 363 | &res); |
michael@0 | 364 | Log(L"QueryAppIsDefaultAll: %d", res); |
michael@0 | 365 | if (!res) { |
michael@0 | 366 | pAAR->Release(); |
michael@0 | 367 | return false; |
michael@0 | 368 | } |
michael@0 | 369 | // Make sure the Prog ID matches what we have |
michael@0 | 370 | LPWSTR registeredApp; |
michael@0 | 371 | hr = pAAR->QueryCurrentDefault(L"http", AT_URLPROTOCOL, AL_EFFECTIVE, |
michael@0 | 372 | ®isteredApp); |
michael@0 | 373 | pAAR->Release(); |
michael@0 | 374 | Log(L"QueryCurrentDefault: %X", hr); |
michael@0 | 375 | if (FAILED(hr)) |
michael@0 | 376 | return false; |
michael@0 | 377 | |
michael@0 | 378 | Log(L"registeredApp=%s", registeredApp); |
michael@0 | 379 | bool result = !wcsicmp(registeredApp, kDefaultMetroBrowserIDPathKey); |
michael@0 | 380 | CoTaskMemFree(registeredApp); |
michael@0 | 381 | if (!result) |
michael@0 | 382 | return false; |
michael@0 | 383 | |
michael@0 | 384 | // If the registry points another browser's path, |
michael@0 | 385 | // activating the Metro browser will fail. So fallback to the desktop. |
michael@0 | 386 | CStringW selfPath; |
michael@0 | 387 | GetDesktopBrowserPath(selfPath); |
michael@0 | 388 | CStringW browserPath; |
michael@0 | 389 | GetDefaultBrowserPath(browserPath); |
michael@0 | 390 | |
michael@0 | 391 | return !selfPath.CompareNoCase(browserPath); |
michael@0 | 392 | } |
michael@0 | 393 | |
michael@0 | 394 | /* |
michael@0 | 395 | * Helper for nsis installer when it wants to launch the |
michael@0 | 396 | * default metro browser. |
michael@0 | 397 | */ |
michael@0 | 398 | void CommandLineMetroLaunch() |
michael@0 | 399 | { |
michael@0 | 400 | mTargetIsDefaultBrowser = true; |
michael@0 | 401 | LaunchMetroBrowser(); |
michael@0 | 402 | } |
michael@0 | 403 | |
michael@0 | 404 | private: |
michael@0 | 405 | void LaunchDesktopBrowser(); |
michael@0 | 406 | bool LaunchMetroBrowser(); |
michael@0 | 407 | bool SetTargetPath(IShellItem* aItem); |
michael@0 | 408 | bool TestForUpdateLock(); |
michael@0 | 409 | |
michael@0 | 410 | /* |
michael@0 | 411 | * Defines the type of startup request we receive. |
michael@0 | 412 | */ |
michael@0 | 413 | enum RequestType { |
michael@0 | 414 | DEFAULT_LAUNCH, |
michael@0 | 415 | DESKTOP_RESTART, |
michael@0 | 416 | METRO_RESTART, |
michael@0 | 417 | METRO_UPDATE, |
michael@0 | 418 | }; |
michael@0 | 419 | |
michael@0 | 420 | RequestType mRequestType; |
michael@0 | 421 | |
michael@0 | 422 | /* |
michael@0 | 423 | * Defines the type of delayed launch we might do. |
michael@0 | 424 | */ |
michael@0 | 425 | enum DelayedLaunchType { |
michael@0 | 426 | NONE, |
michael@0 | 427 | DESKTOP, |
michael@0 | 428 | METRO, |
michael@0 | 429 | }; |
michael@0 | 430 | |
michael@0 | 431 | DelayedLaunchType mDelayedLaunchType; |
michael@0 | 432 | |
michael@0 | 433 | long mRef; |
michael@0 | 434 | IShellItemArray *mShellItemArray; |
michael@0 | 435 | IUnknown *mUnkSite; |
michael@0 | 436 | CStringW mVerb; |
michael@0 | 437 | CStringW mTarget; |
michael@0 | 438 | CStringW mParameters; |
michael@0 | 439 | bool mTargetIsFileSystemLink; |
michael@0 | 440 | bool mTargetIsDefaultBrowser; |
michael@0 | 441 | bool mTargetIsBrowser; |
michael@0 | 442 | DWORD mKeyState; |
michael@0 | 443 | bool mRequestMet; |
michael@0 | 444 | }; |
michael@0 | 445 | |
michael@0 | 446 | /* |
michael@0 | 447 | * Retrieve the current default browser's path. |
michael@0 | 448 | * |
michael@0 | 449 | * @aPathBuffer Buffer to fill |
michael@0 | 450 | */ |
michael@0 | 451 | static bool GetDefaultBrowserPath(CStringW& aPathBuffer) |
michael@0 | 452 | { |
michael@0 | 453 | WCHAR buffer[MAX_PATH]; |
michael@0 | 454 | DWORD length = MAX_PATH; |
michael@0 | 455 | |
michael@0 | 456 | if (FAILED(AssocQueryStringW(ASSOCF_NOTRUNCATE | ASSOCF_INIT_IGNOREUNKNOWN, |
michael@0 | 457 | ASSOCSTR_EXECUTABLE, |
michael@0 | 458 | kDefaultMetroBrowserIDPathKey, nullptr, |
michael@0 | 459 | buffer, &length))) { |
michael@0 | 460 | Log(L"AssocQueryString failed."); |
michael@0 | 461 | return false; |
michael@0 | 462 | } |
michael@0 | 463 | |
michael@0 | 464 | // sanity check |
michael@0 | 465 | if (lstrcmpiW(PathFindFileNameW(buffer), kFirefoxExe)) |
michael@0 | 466 | return false; |
michael@0 | 467 | |
michael@0 | 468 | aPathBuffer = buffer; |
michael@0 | 469 | return true; |
michael@0 | 470 | } |
michael@0 | 471 | |
michael@0 | 472 | /* |
michael@0 | 473 | * Retrieve the app model id of the firefox metro browser. |
michael@0 | 474 | * |
michael@0 | 475 | * @aPathBuffer Buffer to fill |
michael@0 | 476 | * @aCharLength Length of buffer to fill in characters |
michael@0 | 477 | */ |
michael@0 | 478 | template <size_t N> |
michael@0 | 479 | static bool GetDefaultBrowserAppModelID(WCHAR (&aIDBuffer)[N]) |
michael@0 | 480 | { |
michael@0 | 481 | HKEY key; |
michael@0 | 482 | if (RegOpenKeyExW(HKEY_CLASSES_ROOT, kDefaultMetroBrowserIDPathKey, |
michael@0 | 483 | 0, KEY_READ, &key) != ERROR_SUCCESS) { |
michael@0 | 484 | return false; |
michael@0 | 485 | } |
michael@0 | 486 | DWORD len = sizeof(aIDBuffer); |
michael@0 | 487 | memset(aIDBuffer, 0, len); |
michael@0 | 488 | if (RegQueryValueExW(key, L"AppUserModelID", nullptr, nullptr, |
michael@0 | 489 | (LPBYTE)aIDBuffer, &len) != ERROR_SUCCESS || !len) { |
michael@0 | 490 | RegCloseKey(key); |
michael@0 | 491 | return false; |
michael@0 | 492 | } |
michael@0 | 493 | RegCloseKey(key); |
michael@0 | 494 | return true; |
michael@0 | 495 | } |
michael@0 | 496 | |
michael@0 | 497 | namespace { |
michael@0 | 498 | const FORMATETC kPlainTextFormat = |
michael@0 | 499 | {CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; |
michael@0 | 500 | const FORMATETC kPlainTextWFormat = |
michael@0 | 501 | {CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; |
michael@0 | 502 | } |
michael@0 | 503 | |
michael@0 | 504 | bool HasPlainText(IDataObject* aDataObj) { |
michael@0 | 505 | return SUCCEEDED(aDataObj->QueryGetData((FORMATETC*)&kPlainTextWFormat)) || |
michael@0 | 506 | SUCCEEDED(aDataObj->QueryGetData((FORMATETC*)&kPlainTextFormat)); |
michael@0 | 507 | } |
michael@0 | 508 | |
michael@0 | 509 | bool GetPlainText(IDataObject* aDataObj, CStringW& cstrText) |
michael@0 | 510 | { |
michael@0 | 511 | if (!HasPlainText(aDataObj)) |
michael@0 | 512 | return false; |
michael@0 | 513 | |
michael@0 | 514 | STGMEDIUM store; |
michael@0 | 515 | |
michael@0 | 516 | // unicode text |
michael@0 | 517 | if (SUCCEEDED(aDataObj->GetData((FORMATETC*)&kPlainTextWFormat, &store))) { |
michael@0 | 518 | // makes a copy |
michael@0 | 519 | cstrText = static_cast<LPCWSTR>(GlobalLock(store.hGlobal)); |
michael@0 | 520 | GlobalUnlock(store.hGlobal); |
michael@0 | 521 | ReleaseStgMedium(&store); |
michael@0 | 522 | return true; |
michael@0 | 523 | } |
michael@0 | 524 | |
michael@0 | 525 | // ascii text |
michael@0 | 526 | if (SUCCEEDED(aDataObj->GetData((FORMATETC*)&kPlainTextFormat, &store))) { |
michael@0 | 527 | // makes a copy |
michael@0 | 528 | cstrText = static_cast<char*>(GlobalLock(store.hGlobal)); |
michael@0 | 529 | GlobalUnlock(store.hGlobal); |
michael@0 | 530 | ReleaseStgMedium(&store); |
michael@0 | 531 | return true; |
michael@0 | 532 | } |
michael@0 | 533 | |
michael@0 | 534 | return false; |
michael@0 | 535 | } |
michael@0 | 536 | |
michael@0 | 537 | /* |
michael@0 | 538 | * Updates the current target based on the contents of |
michael@0 | 539 | * a shell item. |
michael@0 | 540 | */ |
michael@0 | 541 | bool CExecuteCommandVerb::SetTargetPath(IShellItem* aItem) |
michael@0 | 542 | { |
michael@0 | 543 | if (!aItem) |
michael@0 | 544 | return false; |
michael@0 | 545 | |
michael@0 | 546 | CString cstrText; |
michael@0 | 547 | CComPtr<IDataObject> object; |
michael@0 | 548 | // Check the underlying data object first to insure we get |
michael@0 | 549 | // absolute uri. See chromium bug 157184. |
michael@0 | 550 | if (SUCCEEDED(aItem->BindToHandler(nullptr, BHID_DataObject, |
michael@0 | 551 | IID_IDataObject, |
michael@0 | 552 | reinterpret_cast<void**>(&object))) && |
michael@0 | 553 | GetPlainText(object, cstrText)) { |
michael@0 | 554 | wchar_t scheme[16]; |
michael@0 | 555 | URL_COMPONENTS components = {0}; |
michael@0 | 556 | components.lpszScheme = scheme; |
michael@0 | 557 | components.dwSchemeLength = sizeof(scheme)/sizeof(scheme[0]); |
michael@0 | 558 | components.dwStructSize = sizeof(components); |
michael@0 | 559 | // note, more advanced use may have issues with paths with spaces. |
michael@0 | 560 | if (!InternetCrackUrlW(cstrText, 0, 0, &components)) { |
michael@0 | 561 | Log(L"Failed to identify object text '%s'", cstrText); |
michael@0 | 562 | return false; |
michael@0 | 563 | } |
michael@0 | 564 | |
michael@0 | 565 | mTargetIsFileSystemLink = (components.nScheme == INTERNET_SCHEME_FILE); |
michael@0 | 566 | mTarget = cstrText; |
michael@0 | 567 | |
michael@0 | 568 | return true; |
michael@0 | 569 | } |
michael@0 | 570 | |
michael@0 | 571 | Log(L"No data object or data object has no text."); |
michael@0 | 572 | |
michael@0 | 573 | // Use the shell item display name |
michael@0 | 574 | LPWSTR str = nullptr; |
michael@0 | 575 | mTargetIsFileSystemLink = true; |
michael@0 | 576 | if (FAILED(aItem->GetDisplayName(SIGDN_FILESYSPATH, &str))) { |
michael@0 | 577 | mTargetIsFileSystemLink = false; |
michael@0 | 578 | if (FAILED(aItem->GetDisplayName(SIGDN_URL, &str))) { |
michael@0 | 579 | Log(L"Failed to get parameter string."); |
michael@0 | 580 | return false; |
michael@0 | 581 | } |
michael@0 | 582 | } |
michael@0 | 583 | mTarget = str; |
michael@0 | 584 | CoTaskMemFree(str); |
michael@0 | 585 | |
michael@0 | 586 | CStringW defaultPath; |
michael@0 | 587 | GetDefaultBrowserPath(defaultPath); |
michael@0 | 588 | mTargetIsDefaultBrowser = !mTarget.CompareNoCase(defaultPath); |
michael@0 | 589 | |
michael@0 | 590 | size_t browserEXELen = wcslen(kFirefoxExe); |
michael@0 | 591 | mTargetIsBrowser = mTarget.GetLength() >= browserEXELen && |
michael@0 | 592 | !mTarget.Right(browserEXELen).CompareNoCase(kFirefoxExe); |
michael@0 | 593 | |
michael@0 | 594 | return true; |
michael@0 | 595 | } |
michael@0 | 596 | |
michael@0 | 597 | /* |
michael@0 | 598 | * Desktop launch - Launch the destop browser to display the current |
michael@0 | 599 | * target using shellexecute. |
michael@0 | 600 | */ |
michael@0 | 601 | void LaunchDesktopBrowserWithParams(CStringW& aBrowserPath, CStringW& aVerb, |
michael@0 | 602 | CStringW& aTarget, CStringW& aParameters, |
michael@0 | 603 | bool aTargetIsDefaultBrowser, bool aTargetIsBrowser) |
michael@0 | 604 | { |
michael@0 | 605 | // If a taskbar shortcut, link or local file is clicked, the target will |
michael@0 | 606 | // be the browser exe or file. Don't pass in -url for the target if the |
michael@0 | 607 | // target is known to be a browser. Otherwise, one instance of Firefox |
michael@0 | 608 | // will try to open another instance. |
michael@0 | 609 | CStringW params; |
michael@0 | 610 | if (!aTargetIsDefaultBrowser && !aTargetIsBrowser && !aTarget.IsEmpty()) { |
michael@0 | 611 | // Fallback to the module path if it failed to get the default browser. |
michael@0 | 612 | GetDefaultBrowserPath(aBrowserPath); |
michael@0 | 613 | params += "-url "; |
michael@0 | 614 | params += "\""; |
michael@0 | 615 | params += aTarget; |
michael@0 | 616 | params += "\""; |
michael@0 | 617 | } |
michael@0 | 618 | |
michael@0 | 619 | // Tack on any extra parameters we received (for example -profilemanager) |
michael@0 | 620 | if (!aParameters.IsEmpty()) { |
michael@0 | 621 | params += " "; |
michael@0 | 622 | params += aParameters; |
michael@0 | 623 | } |
michael@0 | 624 | |
michael@0 | 625 | Log(L"Desktop Launch: verb:'%s' exe:'%s' params:'%s'", aVerb, aBrowserPath, params); |
michael@0 | 626 | |
michael@0 | 627 | // Relaunch in Desktop mode uses a special URL to trick Windows into |
michael@0 | 628 | // switching environments. We shouldn't actually try to open this URL. |
michael@0 | 629 | if (!_wcsicmp(aTarget, L"http://-desktop/")) { |
michael@0 | 630 | // Ignore any params and just launch on desktop |
michael@0 | 631 | params.Empty(); |
michael@0 | 632 | } |
michael@0 | 633 | |
michael@0 | 634 | PROCESS_INFORMATION procInfo; |
michael@0 | 635 | STARTUPINFO startInfo; |
michael@0 | 636 | memset(&procInfo, 0, sizeof(PROCESS_INFORMATION)); |
michael@0 | 637 | memset(&startInfo, 0, sizeof(STARTUPINFO)); |
michael@0 | 638 | |
michael@0 | 639 | startInfo.cb = sizeof(STARTUPINFO); |
michael@0 | 640 | startInfo.dwFlags = STARTF_USESHOWWINDOW; |
michael@0 | 641 | startInfo.wShowWindow = SW_SHOWNORMAL; |
michael@0 | 642 | |
michael@0 | 643 | BOOL result = |
michael@0 | 644 | CreateProcessW(aBrowserPath, static_cast<LPWSTR>(params.GetBuffer()), |
michael@0 | 645 | NULL, NULL, FALSE, 0, NULL, NULL, &startInfo, &procInfo); |
michael@0 | 646 | if (!result) { |
michael@0 | 647 | Log(L"CreateProcess failed! (%d)", GetLastError()); |
michael@0 | 648 | return; |
michael@0 | 649 | } |
michael@0 | 650 | // Hand off foreground/focus rights to the browser we create. If we don't |
michael@0 | 651 | // do this the ceh will keep ownership causing desktop firefox to launch |
michael@0 | 652 | // deactivated. |
michael@0 | 653 | if (!AllowSetForegroundWindow(procInfo.dwProcessId)) { |
michael@0 | 654 | Log(L"AllowSetForegroundWindow failed! (%d)", GetLastError()); |
michael@0 | 655 | } |
michael@0 | 656 | CloseHandle(procInfo.hThread); |
michael@0 | 657 | CloseHandle(procInfo.hProcess); |
michael@0 | 658 | Log(L"Desktop browser process id: %d", procInfo.dwProcessId); |
michael@0 | 659 | } |
michael@0 | 660 | |
michael@0 | 661 | void |
michael@0 | 662 | CExecuteCommandVerb::LaunchDesktopBrowser() |
michael@0 | 663 | { |
michael@0 | 664 | CStringW browserPath; |
michael@0 | 665 | if (!GetDesktopBrowserPath(browserPath)) { |
michael@0 | 666 | return; |
michael@0 | 667 | } |
michael@0 | 668 | |
michael@0 | 669 | LaunchDesktopBrowserWithParams(browserPath, mVerb, mTarget, mParameters, |
michael@0 | 670 | mTargetIsDefaultBrowser, mTargetIsBrowser); |
michael@0 | 671 | } |
michael@0 | 672 | |
michael@0 | 673 | void |
michael@0 | 674 | CExecuteCommandVerb::HeartBeat() |
michael@0 | 675 | { |
michael@0 | 676 | if (mRequestType == METRO_UPDATE && mDelayedLaunchType == DESKTOP && |
michael@0 | 677 | !IsMetroProcessRunning()) { |
michael@0 | 678 | mDelayedLaunchType = NONE; |
michael@0 | 679 | LaunchDesktopBrowser(); |
michael@0 | 680 | SetRequestMet(); |
michael@0 | 681 | } |
michael@0 | 682 | if (mDelayedLaunchType == METRO && !TestForUpdateLock()) { |
michael@0 | 683 | mDelayedLaunchType = NONE; |
michael@0 | 684 | LaunchMetroBrowser(); |
michael@0 | 685 | SetRequestMet(); |
michael@0 | 686 | } |
michael@0 | 687 | } |
michael@0 | 688 | |
michael@0 | 689 | bool |
michael@0 | 690 | CExecuteCommandVerb::TestForUpdateLock() |
michael@0 | 691 | { |
michael@0 | 692 | CStringW browserPath; |
michael@0 | 693 | if (!GetDefaultBrowserPath(browserPath)) { |
michael@0 | 694 | return false; |
michael@0 | 695 | } |
michael@0 | 696 | |
michael@0 | 697 | HANDLE hFile = CreateFileW(browserPath, |
michael@0 | 698 | FILE_EXECUTE, FILE_SHARE_READ|FILE_SHARE_WRITE, |
michael@0 | 699 | nullptr, OPEN_EXISTING, 0, nullptr); |
michael@0 | 700 | if (hFile != INVALID_HANDLE_VALUE) { |
michael@0 | 701 | CloseHandle(hFile); |
michael@0 | 702 | return false; |
michael@0 | 703 | } |
michael@0 | 704 | return true; |
michael@0 | 705 | } |
michael@0 | 706 | |
michael@0 | 707 | bool |
michael@0 | 708 | CExecuteCommandVerb::LaunchMetroBrowser() |
michael@0 | 709 | { |
michael@0 | 710 | HRESULT hr; |
michael@0 | 711 | |
michael@0 | 712 | CComPtr<IApplicationActivationManager> activateMgr; |
michael@0 | 713 | hr = activateMgr.CoCreateInstance(CLSID_ApplicationActivationManager, |
michael@0 | 714 | nullptr, CLSCTX_LOCAL_SERVER); |
michael@0 | 715 | if (FAILED(hr)) { |
michael@0 | 716 | Log(L"CoCreateInstance failed, launching on desktop."); |
michael@0 | 717 | return false; |
michael@0 | 718 | } |
michael@0 | 719 | |
michael@0 | 720 | // Hand off focus rights to the out-of-process activation server. This will |
michael@0 | 721 | // fail if we don't have the rights to begin with. Log but don't bail. |
michael@0 | 722 | hr = CoAllowSetForegroundWindow(activateMgr, nullptr); |
michael@0 | 723 | if (FAILED(hr)) { |
michael@0 | 724 | Log(L"CoAllowSetForegroundWindow result %X", hr); |
michael@0 | 725 | } |
michael@0 | 726 | |
michael@0 | 727 | WCHAR appModelID[256]; |
michael@0 | 728 | if (!GetDefaultBrowserAppModelID(appModelID)) { |
michael@0 | 729 | Log(L"GetDefaultBrowserAppModelID failed."); |
michael@0 | 730 | return false; |
michael@0 | 731 | } |
michael@0 | 732 | |
michael@0 | 733 | Log(L"Metro Launch: verb:'%s' appid:'%s' params:'%s'", mVerb, appModelID, mTarget); |
michael@0 | 734 | |
michael@0 | 735 | // shortcuts to the application |
michael@0 | 736 | DWORD processID; |
michael@0 | 737 | if (mTargetIsDefaultBrowser) { |
michael@0 | 738 | hr = activateMgr->ActivateApplication(appModelID, L"", AO_NONE, &processID); |
michael@0 | 739 | Log(L"ActivateApplication result %X", hr); |
michael@0 | 740 | // files |
michael@0 | 741 | } else if (mTargetIsFileSystemLink) { |
michael@0 | 742 | hr = activateMgr->ActivateForFile(appModelID, mShellItemArray, mVerb, &processID); |
michael@0 | 743 | Log(L"ActivateForFile result %X", hr); |
michael@0 | 744 | // protocols |
michael@0 | 745 | } else { |
michael@0 | 746 | hr = activateMgr->ActivateForProtocol(appModelID, mShellItemArray, &processID); |
michael@0 | 747 | Log(L"ActivateForProtocol result %X", hr); |
michael@0 | 748 | } |
michael@0 | 749 | return true; |
michael@0 | 750 | } |
michael@0 | 751 | |
michael@0 | 752 | void CExecuteCommandVerb::SetRequestMet() |
michael@0 | 753 | { |
michael@0 | 754 | SafeRelease(&mShellItemArray); |
michael@0 | 755 | SafeRelease(&mUnkSite); |
michael@0 | 756 | mRequestMet = true; |
michael@0 | 757 | Log(L"Request met, exiting."); |
michael@0 | 758 | } |
michael@0 | 759 | |
michael@0 | 760 | IFACEMETHODIMP CExecuteCommandVerb::Execute() |
michael@0 | 761 | { |
michael@0 | 762 | Log(L"Execute()"); |
michael@0 | 763 | |
michael@0 | 764 | if (!mTarget.GetLength()) { |
michael@0 | 765 | // We shut down when this flips to true |
michael@0 | 766 | SetRequestMet(); |
michael@0 | 767 | return E_FAIL; |
michael@0 | 768 | } |
michael@0 | 769 | |
michael@0 | 770 | if (!IsDX10Available()) { |
michael@0 | 771 | Log(L"Can't launch in metro due to missing hardware acceleration features."); |
michael@0 | 772 | mRequestType = DESKTOP_RESTART; |
michael@0 | 773 | } |
michael@0 | 774 | |
michael@0 | 775 | // Deal with metro restart for an update - launch desktop with a command |
michael@0 | 776 | // that tells it to run updater then launch the metro browser. |
michael@0 | 777 | if (mRequestType == METRO_UPDATE) { |
michael@0 | 778 | // We'll complete this in the heart beat callback from the main msg loop. |
michael@0 | 779 | // We do this because the last browser instance makes this call to Execute |
michael@0 | 780 | // sync. So we want to make sure it's completely shutdown before we do |
michael@0 | 781 | // the update. |
michael@0 | 782 | mParameters = kMetroUpdateCmdLine; |
michael@0 | 783 | mDelayedLaunchType = DESKTOP; |
michael@0 | 784 | return S_OK; |
michael@0 | 785 | } |
michael@0 | 786 | |
michael@0 | 787 | // Launch on the desktop |
michael@0 | 788 | if (mRequestType == DESKTOP_RESTART || |
michael@0 | 789 | (mRequestType == DEFAULT_LAUNCH && DefaultLaunchIsDesktop())) { |
michael@0 | 790 | LaunchDesktopBrowser(); |
michael@0 | 791 | SetRequestMet(); |
michael@0 | 792 | return S_OK; |
michael@0 | 793 | } |
michael@0 | 794 | |
michael@0 | 795 | // If we have an update in the works, don't try to activate yet, |
michael@0 | 796 | // delay until the lock is removed. |
michael@0 | 797 | if (TestForUpdateLock()) { |
michael@0 | 798 | mDelayedLaunchType = METRO; |
michael@0 | 799 | return S_OK; |
michael@0 | 800 | } |
michael@0 | 801 | |
michael@0 | 802 | LaunchMetroBrowser(); |
michael@0 | 803 | SetRequestMet(); |
michael@0 | 804 | return S_OK; |
michael@0 | 805 | } |
michael@0 | 806 | |
michael@0 | 807 | class ClassFactory : public IClassFactory |
michael@0 | 808 | { |
michael@0 | 809 | public: |
michael@0 | 810 | ClassFactory(IUnknown *punkObject); |
michael@0 | 811 | ~ClassFactory(); |
michael@0 | 812 | STDMETHODIMP Register(CLSCTX classContent, REGCLS classUse); |
michael@0 | 813 | STDMETHODIMP QueryInterface(REFIID riid, void **ppv); |
michael@0 | 814 | STDMETHODIMP_(ULONG) AddRef() { return 2; } |
michael@0 | 815 | STDMETHODIMP_(ULONG) Release() { return 1; } |
michael@0 | 816 | STDMETHODIMP CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv); |
michael@0 | 817 | STDMETHODIMP LockServer(BOOL); |
michael@0 | 818 | private: |
michael@0 | 819 | IUnknown* mUnkObject; |
michael@0 | 820 | DWORD mRegID; |
michael@0 | 821 | }; |
michael@0 | 822 | |
michael@0 | 823 | ClassFactory::ClassFactory(IUnknown* aUnkObj) : |
michael@0 | 824 | mUnkObject(aUnkObj), |
michael@0 | 825 | mRegID(0) |
michael@0 | 826 | { |
michael@0 | 827 | if (mUnkObject) { |
michael@0 | 828 | mUnkObject->AddRef(); |
michael@0 | 829 | } |
michael@0 | 830 | } |
michael@0 | 831 | |
michael@0 | 832 | ClassFactory::~ClassFactory() |
michael@0 | 833 | { |
michael@0 | 834 | if (mRegID) { |
michael@0 | 835 | CoRevokeClassObject(mRegID); |
michael@0 | 836 | } |
michael@0 | 837 | mUnkObject->Release(); |
michael@0 | 838 | } |
michael@0 | 839 | |
michael@0 | 840 | STDMETHODIMP |
michael@0 | 841 | ClassFactory::Register(CLSCTX aClass, REGCLS aUse) |
michael@0 | 842 | { |
michael@0 | 843 | return CoRegisterClassObject(__uuidof(CExecuteCommandVerb), |
michael@0 | 844 | static_cast<IClassFactory *>(this), |
michael@0 | 845 | aClass, aUse, &mRegID); |
michael@0 | 846 | } |
michael@0 | 847 | |
michael@0 | 848 | STDMETHODIMP |
michael@0 | 849 | ClassFactory::QueryInterface(REFIID riid, void **ppv) |
michael@0 | 850 | { |
michael@0 | 851 | IUnknown *punk = nullptr; |
michael@0 | 852 | if (riid == IID_IUnknown || riid == IID_IClassFactory) { |
michael@0 | 853 | punk = static_cast<IClassFactory*>(this); |
michael@0 | 854 | } |
michael@0 | 855 | *ppv = punk; |
michael@0 | 856 | if (punk) { |
michael@0 | 857 | punk->AddRef(); |
michael@0 | 858 | return S_OK; |
michael@0 | 859 | } else { |
michael@0 | 860 | return E_NOINTERFACE; |
michael@0 | 861 | } |
michael@0 | 862 | } |
michael@0 | 863 | |
michael@0 | 864 | STDMETHODIMP |
michael@0 | 865 | ClassFactory::CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv) |
michael@0 | 866 | { |
michael@0 | 867 | *ppv = nullptr; |
michael@0 | 868 | if (punkOuter) |
michael@0 | 869 | return CLASS_E_NOAGGREGATION; |
michael@0 | 870 | return mUnkObject->QueryInterface(riid, ppv); |
michael@0 | 871 | } |
michael@0 | 872 | |
michael@0 | 873 | LONG gObjRefCnt; |
michael@0 | 874 | |
michael@0 | 875 | STDMETHODIMP |
michael@0 | 876 | ClassFactory::LockServer(BOOL fLock) |
michael@0 | 877 | { |
michael@0 | 878 | if (fLock) |
michael@0 | 879 | InterlockedIncrement(&gObjRefCnt); |
michael@0 | 880 | else |
michael@0 | 881 | InterlockedDecrement(&gObjRefCnt); |
michael@0 | 882 | Log(L"ClassFactory::LockServer() %d", gObjRefCnt); |
michael@0 | 883 | return S_OK; |
michael@0 | 884 | } |
michael@0 | 885 | |
michael@0 | 886 | int APIENTRY wWinMain(HINSTANCE, HINSTANCE, PWSTR pszCmdLine, int) |
michael@0 | 887 | { |
michael@0 | 888 | #if defined(SHOW_CONSOLE) |
michael@0 | 889 | SetupConsole(); |
michael@0 | 890 | #endif |
michael@0 | 891 | |
michael@0 | 892 | // nsis installer uses this as a helper to launch metro |
michael@0 | 893 | if (pszCmdLine && StrStrI(pszCmdLine, kNsisLaunchCmdLine)) |
michael@0 | 894 | { |
michael@0 | 895 | CoInitialize(nullptr); |
michael@0 | 896 | CExecuteCommandVerb *pHandler = new CExecuteCommandVerb(); |
michael@0 | 897 | if (!pHandler) |
michael@0 | 898 | return E_OUTOFMEMORY; |
michael@0 | 899 | pHandler->CommandLineMetroLaunch(); |
michael@0 | 900 | delete pHandler; |
michael@0 | 901 | CoUninitialize(); |
michael@0 | 902 | return 0; |
michael@0 | 903 | } |
michael@0 | 904 | |
michael@0 | 905 | if (!wcslen(pszCmdLine) || StrStrI(pszCmdLine, kExplorerLaunchCmdLine)) |
michael@0 | 906 | { |
michael@0 | 907 | CoInitialize(nullptr); |
michael@0 | 908 | |
michael@0 | 909 | CExecuteCommandVerb *pHandler = new CExecuteCommandVerb(); |
michael@0 | 910 | if (!pHandler) |
michael@0 | 911 | return E_OUTOFMEMORY; |
michael@0 | 912 | |
michael@0 | 913 | IUnknown* ppi; |
michael@0 | 914 | pHandler->QueryInterface(IID_IUnknown, (void**)&ppi); |
michael@0 | 915 | if (!ppi) |
michael@0 | 916 | return E_FAIL; |
michael@0 | 917 | |
michael@0 | 918 | ClassFactory classFactory(ppi); |
michael@0 | 919 | ppi->Release(); |
michael@0 | 920 | ppi = nullptr; |
michael@0 | 921 | |
michael@0 | 922 | // REGCLS_SINGLEUSE insures we only get used once and then discarded. |
michael@0 | 923 | if (FAILED(classFactory.Register(CLSCTX_LOCAL_SERVER, REGCLS_SINGLEUSE))) |
michael@0 | 924 | return -1; |
michael@0 | 925 | |
michael@0 | 926 | if (!SetTimer(nullptr, 1, HEARTBEAT_MSEC, nullptr)) { |
michael@0 | 927 | Log(L"Failed to set timer, can't process request."); |
michael@0 | 928 | return -1; |
michael@0 | 929 | } |
michael@0 | 930 | |
michael@0 | 931 | MSG msg; |
michael@0 | 932 | long beatCount = 0; |
michael@0 | 933 | while (GetMessage(&msg, 0, 0, 0) > 0) { |
michael@0 | 934 | if (msg.message == WM_TIMER) { |
michael@0 | 935 | pHandler->HeartBeat(); |
michael@0 | 936 | if (++beatCount > REQUEST_WAIT_TIMEOUT || |
michael@0 | 937 | (pHandler->RequestMet() && pHandler->RefCount() < 2)) { |
michael@0 | 938 | break; |
michael@0 | 939 | } |
michael@0 | 940 | } |
michael@0 | 941 | TranslateMessage(&msg); |
michael@0 | 942 | DispatchMessage(&msg); |
michael@0 | 943 | } |
michael@0 | 944 | |
michael@0 | 945 | #ifdef DEBUG_DELAY_SHUTDOWN |
michael@0 | 946 | Sleep(10000); |
michael@0 | 947 | #endif |
michael@0 | 948 | CoUninitialize(); |
michael@0 | 949 | return 0; |
michael@0 | 950 | } |
michael@0 | 951 | return 0; |
michael@0 | 952 | } |