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 https://mozilla.org/MPL/2.0/. */
6 
7 #ifndef mozilla_WinHeaderOnlyUtils_h
8 #define mozilla_WinHeaderOnlyUtils_h
9 
10 #include <windows.h>
11 #include <winerror.h>
12 #include <winnt.h>
13 #include <winternl.h>
14 #include <objbase.h>
15 
16 #include <stdlib.h>
17 
18 #include "mozilla/Assertions.h"
19 #include "mozilla/Attributes.h"
20 #include "mozilla/DynamicallyLinkedFunctionPtr.h"
21 #include "mozilla/Maybe.h"
22 #include "mozilla/Result.h"
23 #include "mozilla/Tuple.h"
24 #include "mozilla/UniquePtr.h"
25 #include "mozilla/WindowsVersion.h"
26 #include "nsWindowsHelpers.h"
27 
28 #if defined(MOZILLA_INTERNAL_API)
29 #  include "nsIFile.h"
30 #  include "nsString.h"
31 #endif  // defined(MOZILLA_INTERNAL_API)
32 
33 /**
34  * This header is intended for self-contained, header-only, utility code for
35  * Win32. It may be used outside of xul.dll, in places such as firefox.exe or
36  * mozglue.dll. If your code creates dependencies on Mozilla libraries, you
37  * should put it elsewhere.
38  */
39 
40 #if _WIN32_WINNT < _WIN32_WINNT_WIN8
41 typedef struct _FILE_ID_INFO {
42   ULONGLONG VolumeSerialNumber;
43   FILE_ID_128 FileId;
44 } FILE_ID_INFO;
45 
46 #  define FileIdInfo ((FILE_INFO_BY_HANDLE_CLASS)18)
47 
48 #endif  // _WIN32_WINNT < _WIN32_WINNT_WIN8
49 
50 #if !defined(STATUS_SUCCESS)
51 #  define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
52 #endif  // !defined(STATUS_SUCCESS)
53 
54 namespace mozilla {
55 
56 class WindowsError final {
57  private:
58   // HRESULT and NTSTATUS are both typedefs of LONG, so we cannot use
59   // overloading to properly differentiate between the two. Instead we'll use
60   // static functions to convert the various error types to HRESULTs before
61   // instantiating.
WindowsError(HRESULT aHResult)62   explicit WindowsError(HRESULT aHResult) : mHResult(aHResult) {}
63 
64  public:
65   using UniqueString = UniquePtr<WCHAR[], LocalFreeDeleter>;
66 
FromNtStatus(NTSTATUS aNtStatus)67   static WindowsError FromNtStatus(NTSTATUS aNtStatus) {
68     if (aNtStatus == STATUS_SUCCESS) {
69       // Special case: we don't want to set FACILITY_NT_BIT
70       // (HRESULT_FROM_NT does not handle this case, unlike HRESULT_FROM_WIN32)
71       return WindowsError(S_OK);
72     }
73 
74     return WindowsError(HRESULT_FROM_NT(aNtStatus));
75   }
76 
FromHResult(HRESULT aHResult)77   static WindowsError FromHResult(HRESULT aHResult) {
78     return WindowsError(aHResult);
79   }
80 
FromWin32Error(DWORD aWin32Err)81   static WindowsError FromWin32Error(DWORD aWin32Err) {
82     return WindowsError(HRESULT_FROM_WIN32(aWin32Err));
83   }
84 
FromLastError()85   static WindowsError FromLastError() {
86     return FromWin32Error(::GetLastError());
87   }
88 
CreateSuccess()89   static WindowsError CreateSuccess() { return WindowsError(S_OK); }
90 
CreateGeneric()91   static WindowsError CreateGeneric() {
92     return FromWin32Error(ERROR_UNIDENTIFIED_ERROR);
93   }
94 
IsSuccess()95   bool IsSuccess() const { return SUCCEEDED(mHResult); }
96 
IsFailure()97   bool IsFailure() const { return FAILED(mHResult); }
98 
IsAvailableAsWin32Error()99   bool IsAvailableAsWin32Error() const {
100     return IsAvailableAsNtStatus() ||
101            HRESULT_FACILITY(mHResult) == FACILITY_WIN32;
102   }
103 
IsAvailableAsNtStatus()104   bool IsAvailableAsNtStatus() const {
105     return mHResult == S_OK || (mHResult & FACILITY_NT_BIT);
106   }
107 
IsAvailableAsHResult()108   bool IsAvailableAsHResult() const { return true; }
109 
AsString()110   UniqueString AsString() const {
111     LPWSTR rawMsgBuf = nullptr;
112     constexpr DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
113                             FORMAT_MESSAGE_FROM_SYSTEM |
114                             FORMAT_MESSAGE_IGNORE_INSERTS;
115     DWORD result =
116         ::FormatMessageW(flags, nullptr, mHResult, 0,
117                          reinterpret_cast<LPWSTR>(&rawMsgBuf), 0, nullptr);
118     if (!result) {
119       return nullptr;
120     }
121 
122     return UniqueString(rawMsgBuf);
123   }
124 
AsHResult()125   HRESULT AsHResult() const { return mHResult; }
126 
127   // Not all HRESULTs are convertible to Win32 Errors, so we use Maybe
AsWin32Error()128   Maybe<DWORD> AsWin32Error() const {
129     if (mHResult == S_OK) {
130       return Some(static_cast<DWORD>(ERROR_SUCCESS));
131     }
132 
133     if (HRESULT_FACILITY(mHResult) == FACILITY_WIN32) {
134       // This is the inverse of HRESULT_FROM_WIN32
135       return Some(static_cast<DWORD>(HRESULT_CODE(mHResult)));
136     }
137 
138     // The NTSTATUS facility is a special case and thus does not utilize the
139     // HRESULT_FACILITY and HRESULT_CODE macros.
140     if (mHResult & FACILITY_NT_BIT) {
141       return Some(NtStatusToWin32Error(
142           static_cast<NTSTATUS>(mHResult & ~FACILITY_NT_BIT)));
143     }
144 
145     return Nothing();
146   }
147 
148   // Not all HRESULTs are convertible to NTSTATUS, so we use Maybe
AsNtStatus()149   Maybe<NTSTATUS> AsNtStatus() const {
150     if (mHResult == S_OK) {
151       return Some(STATUS_SUCCESS);
152     }
153 
154     // The NTSTATUS facility is a special case and thus does not utilize the
155     // HRESULT_FACILITY and HRESULT_CODE macros.
156     if (mHResult & FACILITY_NT_BIT) {
157       return Some(static_cast<NTSTATUS>(mHResult & ~FACILITY_NT_BIT));
158     }
159 
160     return Nothing();
161   }
162 
163   bool operator==(const WindowsError& aOther) const {
164     return mHResult == aOther.mHResult;
165   }
166 
167   bool operator!=(const WindowsError& aOther) const {
168     return mHResult != aOther.mHResult;
169   }
170 
NtStatusToWin32Error(NTSTATUS aNtStatus)171   static DWORD NtStatusToWin32Error(NTSTATUS aNtStatus) {
172     static const StaticDynamicallyLinkedFunctionPtr<decltype(
173         &RtlNtStatusToDosError)>
174         pRtlNtStatusToDosError(L"ntdll.dll", "RtlNtStatusToDosError");
175 
176     MOZ_ASSERT(!!pRtlNtStatusToDosError);
177     if (!pRtlNtStatusToDosError) {
178       return ERROR_UNIDENTIFIED_ERROR;
179     }
180 
181     return pRtlNtStatusToDosError(aNtStatus);
182   }
183 
184  private:
185   // We store the error code as an HRESULT because they can encode both Win32
186   // error codes and NTSTATUS codes.
187   HRESULT mHResult;
188 };
189 
190 template <typename T>
191 using WindowsErrorResult = Result<T, WindowsError>;
192 
193 struct LauncherError {
LauncherErrorLauncherError194   LauncherError(const char* aFile, int aLine, WindowsError aWin32Error)
195       : mFile(aFile), mLine(aLine), mError(aWin32Error) {}
196 
197   const char* mFile;
198   int mLine;
199   WindowsError mError;
200 
201   bool operator==(const LauncherError& aOther) const {
202     return mError == aOther.mError;
203   }
204 
205   bool operator!=(const LauncherError& aOther) const {
206     return mError != aOther.mError;
207   }
208 
209   bool operator==(const WindowsError& aOther) const { return mError == aOther; }
210 
211   bool operator!=(const WindowsError& aOther) const { return mError != aOther; }
212 };
213 
214 #if defined(MOZILLA_INTERNAL_API)
215 
216 template <typename T>
217 using LauncherResult = WindowsErrorResult<T>;
218 
219 template <typename T>
220 using LauncherResultWithLineInfo = Result<T, LauncherError>;
221 
222 using WindowsErrorType = WindowsError;
223 
224 #else
225 
226 template <typename T>
227 using LauncherResult = Result<T, LauncherError>;
228 
229 template <typename T>
230 using LauncherResultWithLineInfo = LauncherResult<T>;
231 
232 using WindowsErrorType = LauncherError;
233 
234 #endif  // defined(MOZILLA_INTERNAL_API)
235 
236 using LauncherVoidResult = LauncherResult<Ok>;
237 
238 using LauncherVoidResultWithLineInfo = LauncherResultWithLineInfo<Ok>;
239 
240 #if defined(MOZILLA_INTERNAL_API)
241 
242 #  define LAUNCHER_ERROR_GENERIC() \
243     ::mozilla::Err(::mozilla::WindowsError::CreateGeneric())
244 
245 #  define LAUNCHER_ERROR_FROM_WIN32(err) \
246     ::mozilla::Err(::mozilla::WindowsError::FromWin32Error(err))
247 
248 #  define LAUNCHER_ERROR_FROM_LAST() \
249     ::mozilla::Err(::mozilla::WindowsError::FromLastError())
250 
251 #  define LAUNCHER_ERROR_FROM_NTSTATUS(ntstatus) \
252     ::mozilla::Err(::mozilla::WindowsError::FromNtStatus(ntstatus))
253 
254 #  define LAUNCHER_ERROR_FROM_HRESULT(hresult) \
255     ::mozilla::Err(::mozilla::WindowsError::FromHResult(hresult))
256 
257 #  define LAUNCHER_ERROR_FROM_MOZ_WINDOWS_ERROR(err) ::mozilla::Err(err)
258 
259 #else
260 
261 #  define LAUNCHER_ERROR_GENERIC()           \
262     ::mozilla::Err(::mozilla::LauncherError( \
263         __FILE__, __LINE__, ::mozilla::WindowsError::CreateGeneric()))
264 
265 #  define LAUNCHER_ERROR_FROM_WIN32(err)     \
266     ::mozilla::Err(::mozilla::LauncherError( \
267         __FILE__, __LINE__, ::mozilla::WindowsError::FromWin32Error(err)))
268 
269 #  define LAUNCHER_ERROR_FROM_LAST()         \
270     ::mozilla::Err(::mozilla::LauncherError( \
271         __FILE__, __LINE__, ::mozilla::WindowsError::FromLastError()))
272 
273 #  define LAUNCHER_ERROR_FROM_NTSTATUS(ntstatus) \
274     ::mozilla::Err(::mozilla::LauncherError(     \
275         __FILE__, __LINE__, ::mozilla::WindowsError::FromNtStatus(ntstatus)))
276 
277 #  define LAUNCHER_ERROR_FROM_HRESULT(hresult) \
278     ::mozilla::Err(::mozilla::LauncherError(   \
279         __FILE__, __LINE__, ::mozilla::WindowsError::FromHResult(hresult)))
280 
281 // This macro wraps the supplied WindowsError with a LauncherError
282 #  define LAUNCHER_ERROR_FROM_MOZ_WINDOWS_ERROR(err) \
283     ::mozilla::Err(::mozilla::LauncherError(__FILE__, __LINE__, err))
284 
285 #endif  // defined(MOZILLA_INTERNAL_API)
286 
287 // This macro enables copying of a mozilla::LauncherError from a
288 // mozilla::LauncherResult<Foo> into a mozilla::LauncherResult<Bar>
289 #define LAUNCHER_ERROR_FROM_RESULT(result) ::mozilla::Err(result.inspectErr())
290 
291 // How long to wait for a created process to become available for input,
292 // to prevent that process's windows being forced to the background.
293 // This is used across update, restart, and the launcher.
294 const DWORD kWaitForInputIdleTimeoutMS = 10 * 1000;
295 
296 /**
297  * Wait for a child GUI process to become "idle." Idle means that the process
298  * has created its message queue and has begun waiting for user input.
299  *
300  * Note that this must only be used when the child process is going to display
301  * GUI! Otherwise you're going to be waiting for a very long time ;-)
302  *
303  * @return true if we successfully waited for input idle;
304  *         false if we timed out or failed to wait.
305  */
306 inline bool WaitForInputIdle(HANDLE aProcess,
307                              DWORD aTimeoutMs = kWaitForInputIdleTimeoutMS) {
308   const DWORD kSleepTimeMs = 10;
309   const DWORD waitStart = aTimeoutMs == INFINITE ? 0 : ::GetTickCount();
310   DWORD elapsed = 0;
311 
312   while (true) {
313     if (aTimeoutMs != INFINITE) {
314       elapsed = ::GetTickCount() - waitStart;
315     }
316 
317     if (elapsed >= aTimeoutMs) {
318       return false;
319     }
320 
321     // ::WaitForInputIdle() doesn't always set the last-error code on failure
322     ::SetLastError(ERROR_SUCCESS);
323 
324     DWORD waitResult = ::WaitForInputIdle(aProcess, aTimeoutMs - elapsed);
325     if (!waitResult) {
326       return true;
327     }
328 
329     if (waitResult == WAIT_FAILED &&
330         ::GetLastError() == ERROR_NOT_GUI_PROCESS) {
331       ::Sleep(kSleepTimeMs);
332       continue;
333     }
334 
335     return false;
336   }
337 }
338 
339 enum class PathType {
340   eNtPath,
341   eDosPath,
342 };
343 
344 class FileUniqueId final {
345  public:
FileUniqueId(const wchar_t * aPath,PathType aPathType)346   explicit FileUniqueId(const wchar_t* aPath, PathType aPathType)
347       : mId(FILE_ID_INFO()) {
348     if (!aPath) {
349       mId = LAUNCHER_ERROR_FROM_HRESULT(E_INVALIDARG);
350       return;
351     }
352 
353     nsAutoHandle file;
354 
355     switch (aPathType) {
356       default:
357         mId = LAUNCHER_ERROR_FROM_HRESULT(E_INVALIDARG);
358         MOZ_ASSERT_UNREACHABLE("Unhandled PathType");
359         return;
360 
361       case PathType::eNtPath: {
362         UNICODE_STRING unicodeString;
363         ::RtlInitUnicodeString(&unicodeString, aPath);
364         OBJECT_ATTRIBUTES objectAttributes;
365         InitializeObjectAttributes(&objectAttributes, &unicodeString,
366                                    OBJ_CASE_INSENSITIVE, nullptr, nullptr);
367         IO_STATUS_BLOCK ioStatus = {};
368         HANDLE ntHandle;
369         NTSTATUS status = ::NtOpenFile(
370             &ntHandle, SYNCHRONIZE | FILE_READ_ATTRIBUTES, &objectAttributes,
371             &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
372             FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT);
373         // We don't need to check |ntHandle| for INVALID_HANDLE_VALUE here,
374         // as that value is set by the Win32 layer.
375         if (!NT_SUCCESS(status)) {
376           mId = LAUNCHER_ERROR_FROM_NTSTATUS(status);
377           return;
378         }
379 
380         file.own(ntHandle);
381         break;
382       }
383 
384       case PathType::eDosPath: {
385         file.own(::CreateFileW(
386             aPath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
387             nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr));
388         if (file == INVALID_HANDLE_VALUE) {
389           mId = LAUNCHER_ERROR_FROM_LAST();
390           return;
391         }
392 
393         break;
394       }
395     }
396 
397     GetId(file);
398   }
399 
FileUniqueId(const nsAutoHandle & aFile)400   explicit FileUniqueId(const nsAutoHandle& aFile) : mId(FILE_ID_INFO()) {
401     GetId(aFile);
402   }
403 
FileUniqueId(const FileUniqueId & aOther)404   FileUniqueId(const FileUniqueId& aOther) : mId(aOther.mId) {}
405 
406   ~FileUniqueId() = default;
407 
IsError()408   bool IsError() const { return mId.isErr(); }
409 
GetError()410   const WindowsErrorType& GetError() const { return mId.inspectErr(); }
411 
412   FileUniqueId& operator=(const FileUniqueId& aOther) {
413     mId = aOther.mId;
414     return *this;
415   }
416 
417   FileUniqueId(FileUniqueId&& aOther) = default;
418   FileUniqueId& operator=(FileUniqueId&& aOther) = delete;
419 
420   bool operator==(const FileUniqueId& aOther) const {
421     return mId.isOk() && aOther.mId.isOk() &&
422            !memcmp(&mId.inspect(), &aOther.mId.inspect(), sizeof(FILE_ID_INFO));
423   }
424 
425   bool operator!=(const FileUniqueId& aOther) const {
426     return !((*this) == aOther);
427   }
428 
429  private:
GetId(const nsAutoHandle & aFile)430   void GetId(const nsAutoHandle& aFile) {
431     FILE_ID_INFO fileIdInfo = {};
432     if (IsWin8OrLater()) {
433       if (::GetFileInformationByHandleEx(aFile.get(), FileIdInfo, &fileIdInfo,
434                                          sizeof(fileIdInfo))) {
435         mId = fileIdInfo;
436         return;
437       }
438       // Only NTFS and ReFS support FileIdInfo. So we have to fallback if
439       // GetFileInformationByHandleEx failed.
440     }
441 
442     BY_HANDLE_FILE_INFORMATION info = {};
443     if (!::GetFileInformationByHandle(aFile.get(), &info)) {
444       mId = LAUNCHER_ERROR_FROM_LAST();
445       return;
446     }
447 
448     fileIdInfo.VolumeSerialNumber = info.dwVolumeSerialNumber;
449     memcpy(&fileIdInfo.FileId.Identifier[0], &info.nFileIndexLow,
450            sizeof(DWORD));
451     memcpy(&fileIdInfo.FileId.Identifier[sizeof(DWORD)], &info.nFileIndexHigh,
452            sizeof(DWORD));
453     mId = fileIdInfo;
454   }
455 
456  private:
457   LauncherResult<FILE_ID_INFO> mId;
458 };
459 
460 class MOZ_RAII AutoVirtualProtect final {
461  public:
462   AutoVirtualProtect(void* aAddress, size_t aLength, DWORD aProtFlags,
463                      HANDLE aTargetProcess = ::GetCurrentProcess())
mAddress(aAddress)464       : mAddress(aAddress),
465         mLength(aLength),
466         mTargetProcess(aTargetProcess),
467         mPrevProt(0),
468         mError(WindowsError::CreateSuccess()) {
469     if (!::VirtualProtectEx(aTargetProcess, aAddress, aLength, aProtFlags,
470                             &mPrevProt)) {
471       mError = WindowsError::FromLastError();
472     }
473   }
474 
~AutoVirtualProtect()475   ~AutoVirtualProtect() {
476     if (mError.IsFailure()) {
477       return;
478     }
479 
480     ::VirtualProtectEx(mTargetProcess, mAddress, mLength, mPrevProt,
481                        &mPrevProt);
482   }
483 
484   explicit operator bool() const { return mError.IsSuccess(); }
485 
GetError()486   WindowsError GetError() const { return mError; }
487 
PrevProt()488   DWORD PrevProt() const { return mPrevProt; }
489 
490   AutoVirtualProtect(const AutoVirtualProtect&) = delete;
491   AutoVirtualProtect(AutoVirtualProtect&&) = delete;
492   AutoVirtualProtect& operator=(const AutoVirtualProtect&) = delete;
493   AutoVirtualProtect& operator=(AutoVirtualProtect&&) = delete;
494 
495  private:
496   void* mAddress;
497   size_t mLength;
498   HANDLE mTargetProcess;
499   DWORD mPrevProt;
500   WindowsError mError;
501 };
502 
GetFullModulePath(HMODULE aModule)503 inline UniquePtr<wchar_t[]> GetFullModulePath(HMODULE aModule) {
504   DWORD bufLen = MAX_PATH;
505   mozilla::UniquePtr<wchar_t[]> buf;
506   DWORD retLen;
507 
508   while (true) {
509     buf = mozilla::MakeUnique<wchar_t[]>(bufLen);
510     retLen = ::GetModuleFileNameW(aModule, buf.get(), bufLen);
511     if (!retLen) {
512       return nullptr;
513     }
514 
515     if (retLen == bufLen && ::GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
516       bufLen *= 2;
517       continue;
518     }
519 
520     break;
521   }
522 
523   // Upon success, retLen *excludes* the null character
524   ++retLen;
525 
526   // Since we're likely to have a bunch of unused space in buf, let's
527   // reallocate a string to the actual size of the file name.
528   auto result = mozilla::MakeUnique<wchar_t[]>(retLen);
529   if (wcscpy_s(result.get(), retLen, buf.get())) {
530     return nullptr;
531   }
532 
533   return result;
534 }
535 
GetFullBinaryPath()536 inline UniquePtr<wchar_t[]> GetFullBinaryPath() {
537   return GetFullModulePath(nullptr);
538 }
539 
540 class ModuleVersion final {
541  public:
ModuleVersion()542   constexpr ModuleVersion() : mVersion(0ULL) {}
543 
ModuleVersion(const VS_FIXEDFILEINFO & aFixedInfo)544   explicit ModuleVersion(const VS_FIXEDFILEINFO& aFixedInfo)
545       : mVersion((static_cast<uint64_t>(aFixedInfo.dwFileVersionMS) << 32) |
546                  static_cast<uint64_t>(aFixedInfo.dwFileVersionLS)) {}
547 
ModuleVersion(const uint64_t aVersion)548   explicit ModuleVersion(const uint64_t aVersion) : mVersion(aVersion) {}
549 
ModuleVersion(const ModuleVersion & aOther)550   ModuleVersion(const ModuleVersion& aOther) : mVersion(aOther.mVersion) {}
551 
AsInteger()552   uint64_t AsInteger() const { return mVersion; }
553 
uint64_t()554   operator uint64_t() const { return AsInteger(); }
555 
AsTuple()556   Tuple<uint16_t, uint16_t, uint16_t, uint16_t> AsTuple() const {
557     uint16_t major = static_cast<uint16_t>((mVersion >> 48) & 0xFFFFU);
558     uint16_t minor = static_cast<uint16_t>((mVersion >> 32) & 0xFFFFU);
559     uint16_t patch = static_cast<uint16_t>((mVersion >> 16) & 0xFFFFU);
560     uint16_t build = static_cast<uint16_t>(mVersion & 0xFFFFU);
561 
562     return MakeTuple(major, minor, patch, build);
563   }
564 
565   explicit operator bool() const { return !!mVersion; }
566 
567   bool operator<(const ModuleVersion& aOther) const {
568     return mVersion < aOther.mVersion;
569   }
570 
571   bool operator<(const uint64_t& aOther) const { return mVersion < aOther; }
572 
573   ModuleVersion& operator=(const uint64_t aIntVersion) {
574     mVersion = aIntVersion;
575     return *this;
576   }
577 
578  private:
579   uint64_t mVersion;
580 };
581 
GetModuleVersion(const wchar_t * aModuleFullPath)582 inline LauncherResult<ModuleVersion> GetModuleVersion(
583     const wchar_t* aModuleFullPath) {
584   DWORD verInfoLen = ::GetFileVersionInfoSizeW(aModuleFullPath, nullptr);
585   if (!verInfoLen) {
586     return LAUNCHER_ERROR_FROM_LAST();
587   }
588 
589   auto verInfoBuf = MakeUnique<BYTE[]>(verInfoLen);
590   if (!::GetFileVersionInfoW(aModuleFullPath, 0, verInfoLen,
591                              verInfoBuf.get())) {
592     return LAUNCHER_ERROR_FROM_LAST();
593   }
594 
595   UINT fixedInfoLen;
596   VS_FIXEDFILEINFO* fixedInfo = nullptr;
597   if (!::VerQueryValueW(verInfoBuf.get(), L"\\",
598                         reinterpret_cast<LPVOID*>(&fixedInfo), &fixedInfoLen)) {
599     // VerQueryValue may fail if the resource does not exist. This is not an
600     // error; we'll return 0 in this case.
601     return ModuleVersion(0ULL);
602   }
603 
604   return ModuleVersion(*fixedInfo);
605 }
606 
GetModuleVersion(HMODULE aModule)607 inline LauncherResult<ModuleVersion> GetModuleVersion(HMODULE aModule) {
608   UniquePtr<wchar_t[]> fullPath(GetFullModulePath(aModule));
609   if (!fullPath) {
610     return LAUNCHER_ERROR_GENERIC();
611   }
612 
613   return GetModuleVersion(fullPath.get());
614 }
615 
616 #if defined(MOZILLA_INTERNAL_API)
GetModuleVersion(nsIFile * aFile)617 inline LauncherResult<ModuleVersion> GetModuleVersion(nsIFile* aFile) {
618   if (!aFile) {
619     return LAUNCHER_ERROR_FROM_HRESULT(E_INVALIDARG);
620   }
621 
622   nsAutoString fullPath;
623   nsresult rv = aFile->GetPath(fullPath);
624   if (NS_FAILED(rv)) {
625     return LAUNCHER_ERROR_GENERIC();
626   }
627 
628   return GetModuleVersion(fullPath.get());
629 }
630 #endif  // defined(MOZILLA_INTERNAL_API)
631 
632 struct CoTaskMemFreeDeleter {
operatorCoTaskMemFreeDeleter633   void operator()(void* aPtr) { ::CoTaskMemFree(aPtr); }
634 };
635 
GetElevationType(const nsAutoHandle & aToken)636 inline LauncherResult<TOKEN_ELEVATION_TYPE> GetElevationType(
637     const nsAutoHandle& aToken) {
638   DWORD retLen;
639   TOKEN_ELEVATION_TYPE elevationType;
640   if (!::GetTokenInformation(aToken.get(), TokenElevationType, &elevationType,
641                              sizeof(elevationType), &retLen)) {
642     return LAUNCHER_ERROR_FROM_LAST();
643   }
644 
645   return elevationType;
646 }
647 
648 }  // namespace mozilla
649 
650 #endif  // mozilla_WinHeaderOnlyUtils_h
651