diff options
Diffstat (limited to 'src/vm/appxutil.cpp')
-rw-r--r-- | src/vm/appxutil.cpp | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/src/vm/appxutil.cpp b/src/vm/appxutil.cpp new file mode 100644 index 0000000000..6bc04d74cd --- /dev/null +++ b/src/vm/appxutil.cpp @@ -0,0 +1,242 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// +// +// Provides VM-specific AppX utility code. + +#include "common.h" + +#include "utilcode.h" +#include "holder.h" +#include "volatile.h" +#include "appxutil.h" +#include "ex.h" + +#include "Windows.ApplicationModel.h" +#include "Windows.ApplicationModel.Core.h" + +namespace AppX +{ + //----------------------------------------------------------------------------------- + // This is a small helper class designed to ensure that the current thread is + // RoInitialized for the lifetime of the holder. Use this holder only if code does + // not store any WinRT interfaces in locations that will out-live the holder + // itself. + + class RoInitializeHolder + { + public: + enum ThreadingModel + { + MultiThreaded, // Require multi-threaded model + SingleThreaded, // Require single-threaded model + AnyThreadedMultiPreferred // Any threading model is ok; + // prefer multi-threaded model + }; + + RoInitializeHolder( + ThreadingModel threadingModel) // desired/preferred apartment model + { + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END + + HRESULT hr = S_OK; + + { + GCX_PREEMP(); + LeaveRuntimeHolder lrh(::RoInitialize); + + // Prefer MultiThreaded when AnyThreadedMultiPreferred is specified. + hr = ::RoInitialize((threadingModel == SingleThreaded) ? RO_INIT_SINGLETHREADED + : RO_INIT_MULTITHREADED); + } + + // Success means that the thread's RoInitialize ref count has been incremented, + // and must be paired with a call to RoUnintialize. + _uninitRequired = SUCCEEDED(hr); + + if (FAILED(hr)) + { + // Throw if: + // 1. RoInitialize failed for any reason other than RPC_E_CHANGED_MODE + // 2. RoInitialize failed with RPC_E_CHANGED_MODE and caller will not + // accept a different apartment model. + if (hr != RPC_E_CHANGED_MODE || threadingModel != AnyThreadedMultiPreferred) + { + // Note: throwing here will cause us to skip the dtor, but will only + // do so when SUCCEEDED(hr) is FALSE, which means that _uninitRequired + // is also FALSE so there is no RoInitialize refcount leak here. + _ASSERTE(!_uninitRequired); + + ThrowHR(hr); + } + } + } + + // Ensures RoUninitialize is called (if needed) before holder falls out of scope. + ~RoInitializeHolder() + { + LIMITED_METHOD_CONTRACT; + if (_uninitRequired) + { + _uninitRequired = false; + ::RoUninitialize(); + } + } + + private: + bool _uninitRequired; // Is a call to RoUnitialize required? + }; + + //----------------------------------------------------------------------------------- + + HRESULT IsAppXDesignModeWorker(bool * pfResult) + { + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END + + HRESULT hr = S_OK; + + boolean fDesignModeEnabled = false; + + // Delayloaded entrypoint may throw. + EX_TRY + { + // Ensure that thread is initialized for WinRT; either apt model will work for this API. + RoInitializeHolder hRoInit(RoInitializeHolder::AnyThreadedMultiPreferred); + + ReleaseHolder<ABI::Windows::ApplicationModel::IDesignModeStatics> pIDesignMode; + IfFailThrow(clr::winrt::GetActivationFactory( + RuntimeClass_Windows_ApplicationModel_DesignMode, pIDesignMode)); + + IfFailThrow(pIDesignMode->get_DesignModeEnabled(&fDesignModeEnabled)); + } + EX_CATCH_HRESULT(hr) + IfFailRet(hr); + + if (!!fDesignModeEnabled) + { + *pfResult = true; + return S_OK; + } + + *pfResult = false; + return S_OK; + } + + //----------------------------------------------------------------------------------- + // Returns true if running in an AppX process with DevMode enabled. + + bool IsAppXDesignMode() + { + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END + +#ifdef FEATURE_CORECLR + // CoreCLR does not have proper support for AppX design mode. Once/if it has one, it should not need + // any special casing like desktop. Avoid the expensive check completely. + return false; +#else + // DevMode does not change over the lifetime of a process and is expensive to compute + // Cache the first answer and return it once computed; idempotent so races are fine + static enum + { + CachedAppxMode_Unknown, + CachedAppxMode_Normal, + CachedAppxMode_Design + } + s_cachedAppxMode = CachedAppxMode_Unknown; + + bool result = false; + + switch (s_cachedAppxMode) + { + case CachedAppxMode_Unknown: + if (SUCCEEDED(IsAppXDesignModeWorker(&result))) + { // Cache the result on success; otherwise use the default value of false. + s_cachedAppxMode = result ? CachedAppxMode_Design : CachedAppxMode_Normal; + } + break; + + case CachedAppxMode_Normal: + result = false; + break; + + case CachedAppxMode_Design: + result = true; + break; + } + +#ifdef _DEBUG + bool dbg_result = false; + _ASSERTE(FAILED(IsAppXDesignModeWorker(&dbg_result)) || dbg_result == result); +#endif + + return result; +#endif // FEATURE_CORECLR + } + + HRESULT GetApplicationId(LPCWSTR& rString) + { + LIMITED_METHOD_CONTRACT; + + // the PRAID is a static value for the life of the process. the reason for caching is + // because the watson bucketing code requires this value during unhandled exception + // processing and due to the contracts in that code it cannot tolerate the switch to + // preemptive mode when calling out to WinRT. + static LPCWSTR s_wzPraid = nullptr; + + HRESULT hr = S_OK; + + if (s_wzPraid == nullptr) + { + ReleaseHolder<ABI::Windows::ApplicationModel::Core::ICoreApplication> coreApp; + + hr = clr::winrt::GetActivationFactory(RuntimeClass_Windows_ApplicationModel_Core_CoreApplication, coreApp); + + if (SUCCEEDED(hr)) + { + WinRtString winrtAppId; + hr = coreApp->get_Id(winrtAppId.Address()); + + if (SUCCEEDED(hr)) + { + LPCWSTR wzPraid = DuplicateString(winrtAppId.GetRawBuffer(), winrtAppId.size()); + if (wzPraid) + { + if (InterlockedCompareExchangeT(&s_wzPraid, wzPraid, nullptr) != nullptr) + delete[] wzPraid; + } + else + { + hr = E_OUTOFMEMORY; + } + } + } + } + + rString = s_wzPraid; + + return hr; + } + + +} + + |