summaryrefslogtreecommitdiff
path: root/src/vm/appxutil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/appxutil.cpp')
-rw-r--r--src/vm/appxutil.cpp242
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;
+ }
+
+
+}
+
+