Backed out changeset f85447f6f56d (bug 1891145) for causing mochitest failures @...
[gecko.git] / ipc / mscom / Utils.cpp
blobd93e012587e9418586a1b146ce23c27e7b5fc639
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #if defined(MOZILLA_INTERNAL_API)
8 # include "MainThreadUtils.h"
9 # include "mozilla/dom/ContentChild.h"
10 #endif
12 #include "mozilla/ArrayUtils.h"
13 #include "mozilla/mscom/COMWrappers.h"
14 #include "mozilla/DebugOnly.h"
15 #include "mozilla/mscom/Utils.h"
16 #include "mozilla/RefPtr.h"
18 #include <objidl.h>
19 #include <winnt.h>
21 #include <utility>
23 #if defined(_MSC_VER)
24 extern "C" IMAGE_DOS_HEADER __ImageBase;
25 #endif
27 namespace mozilla {
28 namespace mscom {
30 bool IsCOMInitializedOnCurrentThread() {
31 APTTYPE aptType;
32 APTTYPEQUALIFIER aptTypeQualifier;
33 HRESULT hr = wrapped::CoGetApartmentType(&aptType, &aptTypeQualifier);
34 return hr != CO_E_NOTINITIALIZED;
37 bool IsCurrentThreadMTA() {
38 APTTYPE aptType;
39 APTTYPEQUALIFIER aptTypeQualifier;
40 HRESULT hr = wrapped::CoGetApartmentType(&aptType, &aptTypeQualifier);
41 if (FAILED(hr)) {
42 return false;
45 return aptType == APTTYPE_MTA;
48 bool IsCurrentThreadExplicitMTA() {
49 APTTYPE aptType;
50 APTTYPEQUALIFIER aptTypeQualifier;
51 HRESULT hr = wrapped::CoGetApartmentType(&aptType, &aptTypeQualifier);
52 if (FAILED(hr)) {
53 return false;
56 return aptType == APTTYPE_MTA &&
57 aptTypeQualifier != APTTYPEQUALIFIER_IMPLICIT_MTA;
60 bool IsCurrentThreadImplicitMTA() {
61 APTTYPE aptType;
62 APTTYPEQUALIFIER aptTypeQualifier;
63 HRESULT hr = wrapped::CoGetApartmentType(&aptType, &aptTypeQualifier);
64 if (FAILED(hr)) {
65 return false;
68 return aptType == APTTYPE_MTA &&
69 aptTypeQualifier == APTTYPEQUALIFIER_IMPLICIT_MTA;
72 #if defined(MOZILLA_INTERNAL_API)
73 bool IsCurrentThreadNonMainMTA() {
74 if (NS_IsMainThread()) {
75 return false;
78 return IsCurrentThreadMTA();
80 #endif // defined(MOZILLA_INTERNAL_API)
82 bool IsProxy(IUnknown* aUnknown) {
83 if (!aUnknown) {
84 return false;
87 // Only proxies implement this interface, so if it is present then we must
88 // be dealing with a proxied object.
89 RefPtr<IClientSecurity> clientSecurity;
90 HRESULT hr = aUnknown->QueryInterface(IID_IClientSecurity,
91 (void**)getter_AddRefs(clientSecurity));
92 if (SUCCEEDED(hr) || hr == RPC_E_WRONG_THREAD) {
93 return true;
95 return false;
98 bool IsValidGUID(REFGUID aCheckGuid) {
99 // This function determines whether or not aCheckGuid conforms to RFC4122
100 // as it applies to Microsoft COM.
102 BYTE variant = aCheckGuid.Data4[0];
103 if (!(variant & 0x80)) {
104 // NCS Reserved
105 return false;
107 if ((variant & 0xE0) == 0xE0) {
108 // Reserved for future use
109 return false;
111 if ((variant & 0xC0) == 0xC0) {
112 // Microsoft Reserved.
113 return true;
116 BYTE version = HIBYTE(aCheckGuid.Data3) >> 4;
117 // Other versions are specified in RFC4122 but these are the two used by COM.
118 return version == 1 || version == 4;
121 uintptr_t GetContainingModuleHandle() {
122 HMODULE thisModule = nullptr;
123 #if defined(_MSC_VER)
124 thisModule = reinterpret_cast<HMODULE>(&__ImageBase);
125 #else
126 if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
127 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
128 reinterpret_cast<LPCTSTR>(&GetContainingModuleHandle),
129 &thisModule)) {
130 return 0;
132 #endif
133 return reinterpret_cast<uintptr_t>(thisModule);
136 namespace detail {
138 long BuildRegGuidPath(REFGUID aGuid, const GuidType aGuidType, wchar_t* aBuf,
139 const size_t aBufLen) {
140 constexpr wchar_t kClsid[] = L"CLSID\\";
141 constexpr wchar_t kAppid[] = L"AppID\\";
142 constexpr wchar_t kSubkeyBase[] = L"SOFTWARE\\Classes\\";
144 // We exclude null terminators in these length calculations because we include
145 // the stringified GUID's null terminator at the end. Since kClsid and kAppid
146 // have identical lengths, we just choose one to compute this length.
147 constexpr size_t kSubkeyBaseLen = mozilla::ArrayLength(kSubkeyBase) - 1;
148 constexpr size_t kSubkeyLen =
149 kSubkeyBaseLen + mozilla::ArrayLength(kClsid) - 1;
150 // Guid length as formatted for the registry (including curlies and dashes),
151 // but excluding null terminator.
152 constexpr size_t kGuidLen = kGuidRegFormatCharLenInclNul - 1;
153 constexpr size_t kExpectedPathLenInclNul = kSubkeyLen + kGuidLen + 1;
155 if (aBufLen < kExpectedPathLenInclNul) {
156 // Buffer is too short
157 return E_INVALIDARG;
160 if (wcscpy_s(aBuf, aBufLen, kSubkeyBase)) {
161 return E_INVALIDARG;
164 const wchar_t* strGuidType = aGuidType == GuidType::CLSID ? kClsid : kAppid;
165 if (wcscat_s(aBuf, aBufLen, strGuidType)) {
166 return E_INVALIDARG;
169 int guidConversionResult =
170 ::StringFromGUID2(aGuid, &aBuf[kSubkeyLen], aBufLen - kSubkeyLen);
171 if (!guidConversionResult) {
172 return E_INVALIDARG;
175 return S_OK;
178 } // namespace detail
180 long CreateStream(const uint8_t* aInitBuf, const uint32_t aInitBufSize,
181 IStream** aOutStream) {
182 if (!aInitBufSize || !aOutStream) {
183 return E_INVALIDARG;
186 *aOutStream = nullptr;
188 HRESULT hr;
189 RefPtr<IStream> stream;
191 // If aInitBuf is null then initSize must be 0.
192 UINT initSize = aInitBuf ? aInitBufSize : 0;
193 stream = already_AddRefed<IStream>(::SHCreateMemStream(aInitBuf, initSize));
194 if (!stream) {
195 return E_OUTOFMEMORY;
198 if (!aInitBuf) {
199 // Now we'll set the required size
200 ULARGE_INTEGER newSize;
201 newSize.QuadPart = aInitBufSize;
202 hr = stream->SetSize(newSize);
203 if (FAILED(hr)) {
204 return hr;
208 // Ensure that the stream is rewound
209 LARGE_INTEGER streamOffset;
210 streamOffset.QuadPart = 0LL;
211 hr = stream->Seek(streamOffset, STREAM_SEEK_SET, nullptr);
212 if (FAILED(hr)) {
213 return hr;
216 stream.forget(aOutStream);
217 return S_OK;
220 #if defined(MOZILLA_INTERNAL_API)
222 void GUIDToString(REFGUID aGuid, nsAString& aOutString) {
223 // This buffer length is long enough to hold a GUID string that is formatted
224 // to include curly braces and dashes.
225 const int kBufLenWithNul = 39;
226 aOutString.SetLength(kBufLenWithNul);
227 int result = StringFromGUID2(aGuid, char16ptr_t(aOutString.BeginWriting()),
228 kBufLenWithNul);
229 MOZ_ASSERT(result);
230 if (result) {
231 // Truncate the terminator
232 aOutString.SetLength(result - 1);
236 // Undocumented IIDs that are relevant for diagnostic purposes
237 static const IID IID_ISCMLocalActivator = {
238 0x00000136,
239 0x0000,
240 0x0000,
241 {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
242 static const IID IID_IRundown = {
243 0x00000134,
244 0x0000,
245 0x0000,
246 {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
247 static const IID IID_IRemUnknown = {
248 0x00000131,
249 0x0000,
250 0x0000,
251 {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
252 static const IID IID_IRemUnknown2 = {
253 0x00000143,
254 0x0000,
255 0x0000,
256 {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
258 struct IIDToLiteralMapEntry {
259 constexpr IIDToLiteralMapEntry(REFIID aIid, nsLiteralCString&& aStr)
260 : mIid(aIid), mStr(std::forward<nsLiteralCString>(aStr)) {}
262 REFIID mIid;
263 const nsLiteralCString mStr;
267 * Given the name of an interface, the IID_ENTRY macro generates a pair
268 * containing a reference to the interface ID and a stringified version of
269 * the interface name.
271 * For example:
273 * {IID_ENTRY(IUnknown)}
274 * is expanded to:
275 * {IID_IUnknown, "IUnknown"_ns}
278 // clang-format off
279 # define IID_ENTRY_STRINGIFY(iface) #iface##_ns
280 # define IID_ENTRY(iface) IID_##iface, IID_ENTRY_STRINGIFY(iface)
281 // clang-format on
283 // Mapping of selected IIDs to friendly, human readable descriptions for each
284 // interface.
285 static constexpr IIDToLiteralMapEntry sIidDiagStrs[] = {
286 {IID_ENTRY(IUnknown)},
287 {IID_IRemUnknown, "cross-apartment IUnknown"_ns},
288 {IID_IRundown, "cross-apartment object management"_ns},
289 {IID_ISCMLocalActivator, "out-of-process object instantiation"_ns},
290 {IID_IRemUnknown2, "cross-apartment IUnknown"_ns}};
292 # undef IID_ENTRY
293 # undef IID_ENTRY_STRINGIFY
295 void DiagnosticNameForIID(REFIID aIid, nsACString& aOutString) {
296 // If the IID matches something in sIidDiagStrs, output its string.
297 for (const auto& curEntry : sIidDiagStrs) {
298 if (curEntry.mIid == aIid) {
299 aOutString.Assign(curEntry.mStr);
300 return;
304 // Otherwise just convert the IID to string form and output that.
305 nsAutoString strIid;
306 GUIDToString(aIid, strIid);
308 aOutString.AssignLiteral("IID ");
309 AppendUTF16toUTF8(strIid, aOutString);
312 #else
314 void GUIDToString(REFGUID aGuid,
315 wchar_t (&aOutBuf)[kGuidRegFormatCharLenInclNul]) {
316 DebugOnly<int> result =
317 ::StringFromGUID2(aGuid, aOutBuf, ArrayLength(aOutBuf));
318 MOZ_ASSERT(result);
321 #endif // defined(MOZILLA_INTERNAL_API)
323 } // namespace mscom
324 } // namespace mozilla