summaryrefslogtreecommitdiff
path: root/src/vm/appxutil.cpp
blob: 0796cf9062bbfc84f5290530db75708b7668dd6d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
// 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();

                // 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

        // 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;
    }

    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;
    }
    

}