tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

WindowsMessageLoop.cpp (37114B)


      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/. */
      6 
      7 #include "mozilla/DebugOnly.h"
      8 
      9 #include "WindowsMessageLoop.h"
     10 #include "Neutering.h"
     11 #include "MessageChannel.h"
     12 
     13 #include "nsServiceManagerUtils.h"
     14 #include "nsString.h"
     15 #include "WinUtils.h"
     16 
     17 #include "mozilla/dom/JSExecutionManager.h"
     18 #include "mozilla/gfx/Logging.h"
     19 #include "mozilla/ipc/ProtocolUtils.h"
     20 #include "mozilla/mscom/Utils.h"
     21 #include "mozilla/StaticPtr.h"
     22 #include "mozilla/UniquePtr.h"
     23 #include "mozilla/WindowsProcessMitigations.h"
     24 
     25 using namespace mozilla;
     26 using namespace mozilla::ipc;
     27 using namespace mozilla::ipc::windows;
     28 
     29 /**
     30 * The Windows-only code below exists to solve a general problem with deadlocks
     31 * that we experience when sending synchronous IPC messages to processes that
     32 * contain native windows (i.e. HWNDs). Windows (the OS) sends synchronous
     33 * messages between parent and child HWNDs in multiple circumstances (e.g.
     34 * WM_PARENTNOTIFY, WM_NCACTIVATE, etc.), even when those HWNDs are controlled
     35 * by different threads or different processes. Thus we can very easily end up
     36 * in a deadlock by a call stack like the following:
     37 *
     38 * Process A:
     39 *   - CreateWindow(...) creates a "parent" HWND.
     40 *   - SendCreateChildWidget(HWND) is a sync IPC message that sends the "parent"
     41 *         HWND over to Process B. Process A blocks until a response is received
     42 *         from Process B.
     43 *
     44 * Process B:
     45 *   - RecvCreateWidget(HWND) gets the "parent" HWND from Process A.
     46 *   - CreateWindow(..., HWND) creates a "child" HWND with the parent from
     47 *         process A.
     48 *   - Windows (the OS) generates a WM_PARENTNOTIFY message that is sent
     49 *         synchronously to Process A. Process B blocks until a response is
     50 *         received from Process A. Process A, however, is blocked and cannot
     51 *         process the message. Both processes are deadlocked.
     52 *
     53 * The example above has a few different workarounds (e.g. setting the
     54 * WS_EX_NOPARENTNOTIFY style on the child window) but the general problem is
     55 * persists. Once two HWNDs are parented we must not block their owning
     56 * threads when manipulating either HWND.
     57 *
     58 * Windows requires any application that hosts native HWNDs to always process
     59 * messages or risk deadlock. Given our architecture the only way to meet
     60 * Windows' requirement and allow for synchronous IPC messages is to pump a
     61 * miniature message loop during a sync IPC call. We avoid processing any
     62 * queued messages during the loop (with one exception, see below), but
     63 * "nonqueued" messages (see
     64 * http://msdn.microsoft.com/en-us/library/ms644927(VS.85).aspx under the
     65 * section "Nonqueued messages") cannot be avoided. Those messages are trapped
     66 * in a special window procedure where we can either ignore the message or
     67 * process it in some fashion.
     68 *
     69 * Queued and "non-queued" messages will be processed during Interrupt calls if
     70 * modal UI related api calls block an Interrupt in-call in the child. To
     71 * prevent windows from freezing, and to allow concurrent processing of critical
     72 * events (such as painting), we spin a native event dispatch loop while
     73 * these in-calls are blocked.
     74 */
     75 
     76 #if defined(ACCESSIBILITY)
     77 // pulled from accessibility's win utils
     78 extern const wchar_t* kPropNameTabContent;
     79 #endif
     80 
     81 // widget related message id constants we need to defer, see nsAppShell.
     82 extern UINT sAppShellGeckoMsgId;
     83 
     84 namespace {
     85 
     86 const wchar_t kOldWndProcProp[] = L"MozillaIPCOldWndProc";
     87 
     88 // This isn't defined before Windows XP.
     89 enum { WM_XP_THEMECHANGED = 0x031A };
     90 
     91 static StaticAutoPtr<AutoTArray<HWND, 20>> gNeuteredWindows;
     92 
     93 typedef nsTArray<UniquePtr<DeferredMessage>> DeferredMessageArray;
     94 DeferredMessageArray* gDeferredMessages = nullptr;
     95 
     96 HHOOK gDeferredGetMsgHook = nullptr;
     97 HHOOK gDeferredCallWndProcHook = nullptr;
     98 
     99 DWORD gUIThreadId = 0;
    100 HWND gCOMWindow = 0;
    101 // Once initialized, gWinEventHook is never unhooked. We save the handle so
    102 // that we can check whether or not the hook is initialized.
    103 HWINEVENTHOOK gWinEventHook = nullptr;
    104 const wchar_t kCOMWindowClassName[] = L"OleMainThreadWndClass";
    105 
    106 // WM_GETOBJECT id pulled from uia headers
    107 #define MOZOBJID_UIAROOT -25
    108 
    109 HWND FindCOMWindow() {
    110  MOZ_ASSERT(gUIThreadId);
    111 
    112  HWND last = 0;
    113  while (
    114      (last = FindWindowExW(HWND_MESSAGE, last, kCOMWindowClassName, NULL))) {
    115    if (GetWindowThreadProcessId(last, NULL) == gUIThreadId) {
    116      return last;
    117    }
    118  }
    119 
    120  return (HWND)0;
    121 }
    122 
    123 void CALLBACK WinEventHook(HWINEVENTHOOK aWinEventHook, DWORD aEvent,
    124                           HWND aHwnd, LONG aIdObject, LONG aIdChild,
    125                           DWORD aEventThread, DWORD aMsEventTime) {
    126  MOZ_ASSERT(aWinEventHook == gWinEventHook);
    127  MOZ_ASSERT(gUIThreadId == aEventThread);
    128  switch (aEvent) {
    129    case EVENT_OBJECT_CREATE: {
    130      if (aIdObject != OBJID_WINDOW || aIdChild != CHILDID_SELF) {
    131        // Not an event we're interested in
    132        return;
    133      }
    134      wchar_t classBuf[256] = {0};
    135      int result = ::GetClassNameW(aHwnd, classBuf, std::size(classBuf));
    136      if (result != (std::size(kCOMWindowClassName) - 1) ||
    137          wcsncmp(kCOMWindowClassName, classBuf, result)) {
    138        // Not a class we're interested in
    139        return;
    140      }
    141      MOZ_ASSERT(FindCOMWindow() == aHwnd);
    142      gCOMWindow = aHwnd;
    143      break;
    144    }
    145    case EVENT_OBJECT_DESTROY: {
    146      if (aHwnd == gCOMWindow && aIdObject == OBJID_WINDOW) {
    147        MOZ_ASSERT(aIdChild == CHILDID_SELF);
    148        gCOMWindow = 0;
    149      }
    150      break;
    151    }
    152    default: {
    153      return;
    154    }
    155  }
    156 }
    157 
    158 LRESULT CALLBACK DeferredMessageHook(int nCode, WPARAM wParam, LPARAM lParam) {
    159  // XXX This function is called for *both* the WH_CALLWNDPROC hook and the
    160  //     WH_GETMESSAGE hook, but they have different parameters. We don't
    161  //     use any of them except nCode which has the same meaning.
    162 
    163  // Only run deferred messages if all of these conditions are met:
    164  //   1. The |nCode| indicates that this hook should do something.
    165  //   2. We have deferred messages to run.
    166  //   3. We're not being called from the PeekMessage within the WaitFor*Notify
    167  //      function (indicated with MessageChannel::IsPumpingMessages). We really
    168  //      only want to run after returning to the main event loop.
    169  if (nCode >= 0 && gDeferredMessages && !MessageChannel::IsPumpingMessages()) {
    170    NS_ASSERTION(gDeferredGetMsgHook && gDeferredCallWndProcHook,
    171                 "These hooks must be set if we're being called!");
    172    NS_ASSERTION(gDeferredMessages->Length(), "No deferred messages?!");
    173 
    174    // Unset hooks first, in case we reenter below.
    175    UnhookWindowsHookEx(gDeferredGetMsgHook);
    176    UnhookWindowsHookEx(gDeferredCallWndProcHook);
    177    gDeferredGetMsgHook = 0;
    178    gDeferredCallWndProcHook = 0;
    179 
    180    // Unset the global and make sure we delete it when we're done here.
    181    auto messages = WrapUnique(gDeferredMessages);
    182    gDeferredMessages = nullptr;
    183 
    184    // Run all the deferred messages in order.
    185    uint32_t count = messages->Length();
    186    for (uint32_t index = 0; index < count; index++) {
    187      messages->ElementAt(index)->Run();
    188    }
    189  }
    190 
    191  // Always call the next hook.
    192  return CallNextHookEx(nullptr, nCode, wParam, lParam);
    193 }
    194 
    195 void ScheduleDeferredMessageRun() {
    196  if (gDeferredMessages && !(gDeferredGetMsgHook && gDeferredCallWndProcHook)) {
    197    NS_ASSERTION(gDeferredMessages->Length(), "No deferred messages?!");
    198 
    199    gDeferredGetMsgHook = ::SetWindowsHookEx(WH_GETMESSAGE, DeferredMessageHook,
    200                                             nullptr, gUIThreadId);
    201    gDeferredCallWndProcHook = ::SetWindowsHookEx(
    202        WH_CALLWNDPROC, DeferredMessageHook, nullptr, gUIThreadId);
    203    NS_ASSERTION(gDeferredGetMsgHook && gDeferredCallWndProcHook,
    204                 "Failed to set hooks!");
    205  }
    206 }
    207 
    208 static void DumpNeuteredMessage(HWND hwnd, UINT uMsg) {
    209 #ifdef DEBUG
    210  nsAutoCString log("Received \"nonqueued\" ");
    211  // classify messages
    212  if (uMsg < WM_USER) {
    213    const char* msgText = mozilla::widget::WinUtils::WinEventToEventName(uMsg);
    214    if (msgText) {
    215      log.AppendPrintf("ui message \"%s\"", msgText);
    216    } else {
    217      log.AppendPrintf("ui message (0x%X)", uMsg);
    218    }
    219  } else if (uMsg >= WM_USER && uMsg < WM_APP) {
    220    log.AppendPrintf("WM_USER message (0x%X)", uMsg);
    221  } else if (uMsg >= WM_APP && uMsg < 0xC000) {
    222    log.AppendPrintf("WM_APP message (0x%X)", uMsg);
    223  } else if (uMsg >= 0xC000 && uMsg < 0x10000) {
    224    log.AppendPrintf("registered windows message (0x%X)", uMsg);
    225  } else {
    226    log.AppendPrintf("system message (0x%X)", uMsg);
    227  }
    228 
    229  log.AppendLiteral(" during a synchronous IPC message for window ");
    230  log.AppendPrintf("0x%p", hwnd);
    231 
    232  wchar_t className[256] = {0};
    233  if (GetClassNameW(hwnd, className, sizeof(className) - 1) > 0) {
    234    log.AppendLiteral(" (\"");
    235    log.Append(NS_ConvertUTF16toUTF8((char16_t*)className));
    236    log.AppendLiteral("\")");
    237  }
    238 
    239  log.AppendLiteral(
    240      ", sending it to DefWindowProc instead of the normal "
    241      "window procedure.");
    242  NS_ERROR(log.get());
    243 #endif
    244 }
    245 
    246 LRESULT
    247 ProcessOrDeferMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    248  UniquePtr<DeferredMessage> deferred;
    249 
    250  // Most messages ask for 0 to be returned if the message is processed.
    251  LRESULT res = 0;
    252 
    253  switch (uMsg) {
    254    // Messages that can be deferred as-is. These must not contain pointers in
    255    // their wParam or lParam arguments!
    256    case WM_ACTIVATE:
    257    case WM_ACTIVATEAPP:
    258    case WM_CANCELMODE:
    259    case WM_CAPTURECHANGED:
    260    case WM_CHILDACTIVATE:
    261    case WM_DESTROY:
    262    case WM_ENABLE:
    263    case WM_IME_NOTIFY:
    264    case WM_IME_SETCONTEXT:
    265    case WM_KILLFOCUS:
    266    case WM_MOUSEWHEEL:
    267    case WM_NCDESTROY:
    268    case WM_PARENTNOTIFY:
    269    case WM_SETFOCUS:
    270    case WM_SYSCOMMAND:
    271    case WM_DISPLAYCHANGE:
    272    case WM_SHOWWINDOW:  // Intentional fall-through.
    273    case WM_XP_THEMECHANGED: {
    274      deferred = MakeUnique<DeferredSendMessage>(hwnd, uMsg, wParam, lParam);
    275      break;
    276    }
    277 
    278    case WM_DEVICECHANGE:
    279    case WM_POWERBROADCAST:
    280    case WM_NCACTIVATE:  // Intentional fall-through.
    281    case WM_SETCURSOR: {
    282      // Friggin unconventional return value...
    283      res = TRUE;
    284      deferred = MakeUnique<DeferredSendMessage>(hwnd, uMsg, wParam, lParam);
    285      break;
    286    }
    287 
    288    case WM_MOUSEACTIVATE: {
    289      res = MA_NOACTIVATE;
    290      deferred = MakeUnique<DeferredSendMessage>(hwnd, uMsg, wParam, lParam);
    291      break;
    292    }
    293 
    294    // These messages need to use the RedrawWindow function to generate the
    295    // right kind of message. We can't simply fake them as the MSDN docs say
    296    // explicitly that paint messages should not be sent by an application.
    297    case WM_ERASEBKGND: {
    298      UINT flags = RDW_INVALIDATE | RDW_ERASE | RDW_NOINTERNALPAINT |
    299                   RDW_NOFRAME | RDW_NOCHILDREN | RDW_ERASENOW;
    300      deferred = MakeUnique<DeferredRedrawMessage>(hwnd, flags);
    301      break;
    302    }
    303 
    304    // This message will generate a WM_PAINT message if there are invalid
    305    // areas.
    306    case WM_PAINT: {
    307      deferred = MakeUnique<DeferredUpdateMessage>(hwnd);
    308      break;
    309    }
    310 
    311    // This message holds a string in its lParam that we must copy.
    312    case WM_SETTINGCHANGE: {
    313      deferred =
    314          MakeUnique<DeferredSettingChangeMessage>(hwnd, uMsg, wParam, lParam);
    315      break;
    316    }
    317 
    318    // These messages are faked via a call to SetWindowPos.
    319    case WM_WINDOWPOSCHANGED: {
    320      deferred = MakeUnique<DeferredWindowPosMessage>(hwnd, lParam);
    321      break;
    322    }
    323    case WM_NCCALCSIZE: {
    324      deferred =
    325          MakeUnique<DeferredWindowPosMessage>(hwnd, lParam, true, wParam);
    326      break;
    327    }
    328 
    329    case WM_COPYDATA: {
    330      deferred =
    331          MakeUnique<DeferredCopyDataMessage>(hwnd, uMsg, wParam, lParam);
    332      res = TRUE;
    333      break;
    334    }
    335 
    336    case WM_STYLECHANGED: {
    337      deferred = MakeUnique<DeferredStyleChangeMessage>(hwnd, wParam, lParam);
    338      break;
    339    }
    340 
    341    case WM_SETICON: {
    342      deferred = MakeUnique<DeferredSetIconMessage>(hwnd, uMsg, wParam, lParam);
    343      break;
    344    }
    345 
    346    // Messages that are safe to pass to DefWindowProc go here.
    347    case WM_ENTERIDLE:
    348    case WM_GETICON:
    349    case WM_NCPAINT:  // (never trap nc paint events)
    350    case WM_GETMINMAXINFO:
    351    case WM_GETTEXT:
    352    case WM_NCHITTEST:
    353    case WM_STYLECHANGING:  // Intentional fall-through.
    354    case WM_WINDOWPOSCHANGING:
    355    case WM_GETTEXTLENGTH: {
    356      return DefWindowProc(hwnd, uMsg, wParam, lParam);
    357    }
    358 
    359    // Just return, prevents DefWindowProc from messaging the window
    360    // syncronously with other events, which may be deferred. Prevents
    361    // random shutdown of aero composition on the window.
    362    case WM_SYNCPAINT:
    363      return 0;
    364 
    365    // This message causes QuickTime to make re-entrant calls.
    366    // Simply discarding it doesn't seem to hurt anything.
    367    case WM_APP - 1:
    368      return 0;
    369 
    370      // We only support a query for our IAccessible or UIA pointers.
    371      // This should be safe, and needs to be sync.
    372 #if defined(ACCESSIBILITY)
    373    case WM_GETOBJECT: {
    374      LONG objId = static_cast<LONG>(lParam);
    375      if (objId == OBJID_CLIENT || objId == MOZOBJID_UIAROOT) {
    376        WNDPROC oldWndProc = (WNDPROC)GetProp(hwnd, kOldWndProcProp);
    377        if (oldWndProc) {
    378          return CallWindowProcW(oldWndProc, hwnd, uMsg, wParam, lParam);
    379        }
    380      }
    381      return DefWindowProc(hwnd, uMsg, wParam, lParam);
    382    }
    383 #endif  // ACCESSIBILITY
    384 
    385    default: {
    386      // Unknown messages only are logged in debug builds and sent to
    387      // DefWindowProc.
    388      if (uMsg && uMsg == sAppShellGeckoMsgId) {
    389        // Widget's registered native event callback
    390        deferred = MakeUnique<DeferredSendMessage>(hwnd, uMsg, wParam, lParam);
    391      }
    392    }
    393  }
    394 
    395  // No deferred message was created and we land here, this is an
    396  // unhandled message.
    397  if (!deferred) {
    398    DumpNeuteredMessage(hwnd, uMsg);
    399    return DefWindowProc(hwnd, uMsg, wParam, lParam);
    400  }
    401 
    402  // Create the deferred message array if it doesn't exist already.
    403  if (!gDeferredMessages) {
    404    gDeferredMessages = new DeferredMessageArray(20);
    405  }
    406 
    407  // Save for later. The array takes ownership of |deferred|.
    408  gDeferredMessages->AppendElement(std::move(deferred));
    409  return res;
    410 }
    411 
    412 }  // namespace
    413 
    414 LRESULT CALLBACK NeuteredWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
    415                                    LPARAM lParam) {
    416  WNDPROC oldWndProc = (WNDPROC)GetProp(hwnd, kOldWndProcProp);
    417  if (!oldWndProc) {
    418    // We should really never ever get here.
    419    NS_ERROR("No old wndproc!");
    420    return DefWindowProc(hwnd, uMsg, wParam, lParam);
    421  }
    422 
    423  // See if we care about this message. We may either ignore it, send it to
    424  // DefWindowProc, or defer it for later.
    425  return ProcessOrDeferMessage(hwnd, uMsg, wParam, lParam);
    426 }
    427 
    428 namespace {
    429 
    430 static bool WindowIsDeferredWindow(HWND hWnd) {
    431  if (!IsWindow(hWnd)) {
    432    NS_WARNING("Window has died!");
    433    return false;
    434  }
    435 
    436  char16_t buffer[256] = {0};
    437  int length = GetClassNameW(hWnd, (wchar_t*)buffer, sizeof(buffer) - 1);
    438  if (length <= 0) {
    439    NS_WARNING("Failed to get class name!");
    440    return false;
    441  }
    442 
    443 #if defined(ACCESSIBILITY)
    444  // Tab content creates a window that responds to accessible WM_GETOBJECT
    445  // calls. This window can safely be ignored.
    446  if (::GetPropW(hWnd, kPropNameTabContent)) {
    447    return false;
    448  }
    449 #endif
    450 
    451  // Common mozilla windows we must defer messages to.
    452  nsDependentString className(buffer, length);
    453  if (StringBeginsWith(className, u"Mozilla"_ns) ||
    454      StringBeginsWith(className, u"Gecko"_ns) ||
    455      className.EqualsLiteral("nsToolkitClass") ||
    456      className.EqualsLiteral("nsAppShell:EventWindowClass")) {
    457    return true;
    458  }
    459 
    460  return false;
    461 }
    462 
    463 bool NeuterWindowProcedure(HWND hWnd) {
    464  if (!WindowIsDeferredWindow(hWnd)) {
    465    // Some other kind of window, skip.
    466    return false;
    467  }
    468 
    469  NS_ASSERTION(!GetProp(hWnd, kOldWndProcProp), "This should always be null!");
    470 
    471  // It's possible to get nullptr out of SetWindowLongPtr, and the only way to
    472  // know if that's a valid old value is to use GetLastError. Clear the error
    473  // here so we can tell.
    474  SetLastError(ERROR_SUCCESS);
    475 
    476  LONG_PTR currentWndProc =
    477      SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)NeuteredWindowProc);
    478  if (!currentWndProc) {
    479    if (ERROR_SUCCESS == GetLastError()) {
    480      // No error, so we set something and must therefore reset it.
    481      SetWindowLongPtr(hWnd, GWLP_WNDPROC, currentWndProc);
    482    }
    483    return false;
    484  }
    485 
    486  NS_ASSERTION(currentWndProc != (LONG_PTR)NeuteredWindowProc,
    487               "This shouldn't be possible!");
    488 
    489  if (!SetProp(hWnd, kOldWndProcProp, (HANDLE)currentWndProc)) {
    490    // Cleanup
    491    NS_WARNING("SetProp failed!");
    492    SetWindowLongPtr(hWnd, GWLP_WNDPROC, currentWndProc);
    493    RemovePropW(hWnd, kOldWndProcProp);
    494    return false;
    495  }
    496 
    497  return true;
    498 }
    499 
    500 void RestoreWindowProcedure(HWND hWnd) {
    501  NS_ASSERTION(WindowIsDeferredWindow(hWnd),
    502               "Not a deferred window, this shouldn't be in our list!");
    503  LONG_PTR oldWndProc = (LONG_PTR)GetProp(hWnd, kOldWndProcProp);
    504  if (oldWndProc) {
    505    NS_ASSERTION(oldWndProc != (LONG_PTR)NeuteredWindowProc,
    506                 "This shouldn't be possible!");
    507 
    508    DebugOnly<LONG_PTR> currentWndProc =
    509        SetWindowLongPtr(hWnd, GWLP_WNDPROC, oldWndProc);
    510    NS_ASSERTION(currentWndProc == (LONG_PTR)NeuteredWindowProc,
    511                 "This should never be switched out from under us!");
    512  }
    513  RemovePropW(hWnd, kOldWndProcProp);
    514 }
    515 
    516 LRESULT CALLBACK CallWindowProcedureHook(int nCode, WPARAM wParam,
    517                                         LPARAM lParam) {
    518  if (nCode >= 0) {
    519    NS_ASSERTION(gNeuteredWindows, "This should never be null!");
    520 
    521    HWND hWnd = reinterpret_cast<CWPSTRUCT*>(lParam)->hwnd;
    522 
    523    if (!gNeuteredWindows->Contains(hWnd) &&
    524        !SuppressedNeuteringRegion::IsNeuteringSuppressed() &&
    525        NeuterWindowProcedure(hWnd)) {
    526      // XXX(Bug 1631371) Check if this should use a fallible operation as it
    527      // pretended earlier.
    528      gNeuteredWindows->AppendElement(hWnd);
    529    }
    530  }
    531  return CallNextHookEx(nullptr, nCode, wParam, lParam);
    532 }
    533 
    534 inline void AssertWindowIsNotNeutered(HWND hWnd) {
    535 #ifdef DEBUG
    536  // Make sure our neutered window hook isn't still in place.
    537  LONG_PTR wndproc = GetWindowLongPtr(hWnd, GWLP_WNDPROC);
    538  NS_ASSERTION(wndproc != (LONG_PTR)NeuteredWindowProc, "Window is neutered!");
    539 #endif
    540 }
    541 
    542 void UnhookNeuteredWindows() {
    543  if (!gNeuteredWindows) return;
    544  uint32_t count = gNeuteredWindows->Length();
    545  for (uint32_t index = 0; index < count; index++) {
    546    RestoreWindowProcedure(gNeuteredWindows->ElementAt(index));
    547  }
    548  gNeuteredWindows->Clear();
    549 }
    550 
    551 // This timeout stuff assumes a sane value of mTimeoutMs (less than the overflow
    552 // value for GetTickCount(), which is something like 50 days). It uses the
    553 // cheapest (and least accurate) method supported by Windows 2000.
    554 
    555 struct TimeoutData {
    556  DWORD startTicks;
    557  DWORD targetTicks;
    558 };
    559 
    560 void InitTimeoutData(TimeoutData* aData, int32_t aTimeoutMs) {
    561  aData->startTicks = GetTickCount();
    562  if (!aData->startTicks) {
    563    // How unlikely is this!
    564    aData->startTicks++;
    565  }
    566  aData->targetTicks = aData->startTicks + aTimeoutMs;
    567 }
    568 
    569 bool TimeoutHasExpired(const TimeoutData& aData) {
    570  if (!aData.startTicks) {
    571    return false;
    572  }
    573 
    574  DWORD now = GetTickCount();
    575 
    576  if (aData.targetTicks < aData.startTicks) {
    577    // Overflow
    578    return now < aData.startTicks && now >= aData.targetTicks;
    579  }
    580  return now >= aData.targetTicks;
    581 }
    582 
    583 }  // namespace
    584 
    585 namespace mozilla {
    586 namespace ipc {
    587 namespace windows {
    588 
    589 void InitUIThread() {
    590  if (!XRE_UseNativeEventProcessing()) {
    591    return;
    592  }
    593  // If we aren't setup before a call to NotifyWorkerThread, we'll hang
    594  // on startup.
    595  if (!gUIThreadId) {
    596    gUIThreadId = GetCurrentThreadId();
    597  }
    598 
    599  MOZ_ASSERT(gUIThreadId);
    600  MOZ_ASSERT(gUIThreadId == GetCurrentThreadId(),
    601             "Called InitUIThread multiple times on different threads!");
    602 
    603  if (!gWinEventHook && !mscom::IsCurrentThreadMTA()) {
    604    gWinEventHook = SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_DESTROY,
    605                                    NULL, &WinEventHook, GetCurrentProcessId(),
    606                                    gUIThreadId, WINEVENT_OUTOFCONTEXT);
    607    MOZ_ASSERT(gWinEventHook);
    608 
    609    // We need to execute this after setting the hook in case the OLE window
    610    // already existed.
    611    gCOMWindow = FindCOMWindow();
    612  }
    613 }
    614 
    615 }  // namespace windows
    616 }  // namespace ipc
    617 }  // namespace mozilla
    618 
    619 MessageChannel::SyncStackFrame::SyncStackFrame(MessageChannel* channel)
    620    : mSpinNestedEvents(false),
    621      mListenerNotified(false),
    622      mChannel(channel),
    623      mPrev(mChannel->mTopFrame),
    624      mStaticPrev(sStaticTopFrame) {
    625  // Only track stack frames when Windows message deferral behavior
    626  // is request for the channel.
    627  if (!(mChannel->GetChannelFlags() & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) {
    628    return;
    629  }
    630 
    631  mChannel->mTopFrame = this;
    632  sStaticTopFrame = this;
    633 
    634  if (!mStaticPrev) {
    635    NS_ASSERTION(!gNeuteredWindows, "Should only set this once!");
    636    gNeuteredWindows = new AutoTArray<HWND, 20>();
    637  }
    638 }
    639 
    640 MessageChannel::SyncStackFrame::~SyncStackFrame() {
    641  if (!(mChannel->GetChannelFlags() & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) {
    642    return;
    643  }
    644 
    645  NS_ASSERTION(this == mChannel->mTopFrame,
    646               "Mismatched interrupt stack frames");
    647  NS_ASSERTION(this == sStaticTopFrame,
    648               "Mismatched static Interrupt stack frames");
    649 
    650  mChannel->mTopFrame = mPrev;
    651  sStaticTopFrame = mStaticPrev;
    652 
    653  if (!mStaticPrev) {
    654    NS_ASSERTION(gNeuteredWindows, "Bad pointer!");
    655    gNeuteredWindows = nullptr;
    656  }
    657 }
    658 
    659 MessageChannel::SyncStackFrame* MessageChannel::sStaticTopFrame;
    660 
    661 // nsAppShell's notification that gecko events are being processed.
    662 // If we are here and there is an Interrupt Incall active, we are spinning
    663 // a nested gecko event loop. In which case the remote process needs
    664 // to know about it.
    665 void /* static */
    666 MessageChannel::NotifyGeckoEventDispatch() {
    667  // sStaticTopFrame is only valid for Interrupt channels
    668  if (!sStaticTopFrame || sStaticTopFrame->mListenerNotified) return;
    669 
    670  sStaticTopFrame->mListenerNotified = true;
    671  MessageChannel* channel =
    672      static_cast<MessageChannel*>(sStaticTopFrame->mChannel);
    673  channel->Listener()->ProcessRemoteNativeEventsInInterruptCall();
    674 }
    675 
    676 // invoked by the module that receives the spin event loop
    677 // message.
    678 void MessageChannel::ProcessNativeEventsInInterruptCall() {
    679  NS_ASSERTION(GetCurrentThreadId() == gUIThreadId,
    680               "Shouldn't be on a non-main thread in here!");
    681  if (!mTopFrame) {
    682    NS_ERROR("Spin logic error: no Interrupt frame");
    683    return;
    684  }
    685 
    686  mTopFrame->mSpinNestedEvents = true;
    687 }
    688 
    689 static HHOOK gWindowHook;
    690 
    691 static inline void StartNeutering() {
    692  if (!gUIThreadId) {
    693    mozilla::ipc::windows::InitUIThread();
    694  }
    695  MOZ_ASSERT(gUIThreadId);
    696  MOZ_ASSERT(!gWindowHook);
    697  NS_ASSERTION(!MessageChannel::IsPumpingMessages(),
    698               "Shouldn't be pumping already!");
    699  MessageChannel::SetIsPumpingMessages(true);
    700  gWindowHook = ::SetWindowsHookEx(WH_CALLWNDPROC, CallWindowProcedureHook,
    701                                   nullptr, gUIThreadId);
    702  NS_ASSERTION(gWindowHook, "Failed to set hook!");
    703 }
    704 
    705 static void StopNeutering() {
    706  MOZ_ASSERT(MessageChannel::IsPumpingMessages());
    707  ::UnhookWindowsHookEx(gWindowHook);
    708  gWindowHook = NULL;
    709  ::UnhookNeuteredWindows();
    710  // Before returning we need to set a hook to run any deferred messages that
    711  // we received during the IPC call. The hook will unset itself as soon as
    712  // someone else calls GetMessage, PeekMessage, or runs code that generates
    713  // a "nonqueued" message.
    714  ::ScheduleDeferredMessageRun();
    715  MessageChannel::SetIsPumpingMessages(false);
    716 }
    717 
    718 NeuteredWindowRegion::NeuteredWindowRegion(bool aDoNeuter)
    719    : mNeuteredByThis(!gWindowHook && aDoNeuter &&
    720                      XRE_UseNativeEventProcessing()) {
    721  if (mNeuteredByThis) {
    722    StartNeutering();
    723  }
    724 }
    725 
    726 NeuteredWindowRegion::~NeuteredWindowRegion() {
    727  if (gWindowHook && mNeuteredByThis) {
    728    StopNeutering();
    729  }
    730 }
    731 
    732 void NeuteredWindowRegion::PumpOnce() {
    733  if (!gWindowHook) {
    734    // This should be a no-op if nothing has been neutered.
    735    return;
    736  }
    737 
    738  MSG msg = {0};
    739  // Pump any COM messages so that we don't hang due to STA marshaling.
    740  if (gCOMWindow && ::PeekMessageW(&msg, gCOMWindow, 0, 0, PM_REMOVE)) {
    741    ::TranslateMessage(&msg);
    742    ::DispatchMessageW(&msg);
    743  }
    744  // Expunge any nonqueued messages on the current thread.
    745  ::PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE);
    746 }
    747 
    748 DeneuteredWindowRegion::DeneuteredWindowRegion()
    749    : mReneuter(gWindowHook != NULL) {
    750  if (mReneuter) {
    751    StopNeutering();
    752  }
    753 }
    754 
    755 DeneuteredWindowRegion::~DeneuteredWindowRegion() {
    756  if (mReneuter) {
    757    StartNeutering();
    758  }
    759 }
    760 
    761 SuppressedNeuteringRegion::SuppressedNeuteringRegion()
    762    : mReenable(::gUIThreadId == ::GetCurrentThreadId() && ::gWindowHook) {
    763  if (mReenable) {
    764    MOZ_ASSERT(!sSuppressNeutering);
    765    sSuppressNeutering = true;
    766  }
    767 }
    768 
    769 SuppressedNeuteringRegion::~SuppressedNeuteringRegion() {
    770  if (mReenable) {
    771    MOZ_ASSERT(sSuppressNeutering);
    772    sSuppressNeutering = false;
    773  }
    774 }
    775 
    776 bool SuppressedNeuteringRegion::sSuppressNeutering = false;
    777 
    778 bool MessageChannel::WaitForSyncNotify() {
    779  mMonitor->AssertCurrentThreadOwns();
    780 
    781  if (!gUIThreadId) {
    782    mozilla::ipc::windows::InitUIThread();
    783  }
    784 
    785  // Use a blocking wait if this channel does not require
    786  // Windows message deferral behavior.
    787  if (!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) {
    788    TimeDuration timeout = (kNoTimeout == mTimeoutMs)
    789                               ? TimeDuration::Forever()
    790                               : TimeDuration::FromMilliseconds(mTimeoutMs);
    791 
    792    MOZ_ASSERT(!mIsSyncWaitingOnNonMainThread);
    793    mIsSyncWaitingOnNonMainThread = true;
    794 
    795    CVStatus status = mMonitor->Wait(timeout);
    796 
    797    MOZ_ASSERT(mIsSyncWaitingOnNonMainThread);
    798    mIsSyncWaitingOnNonMainThread = false;
    799 
    800    // If the timeout didn't expire, we know we received an event. The
    801    // converse is not true.
    802    return WaitResponse(status == CVStatus::Timeout);
    803  }
    804 
    805  NS_ASSERTION(
    806      mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION,
    807      "Shouldn't be here for channels that don't use message deferral!");
    808  NS_ASSERTION(mTopFrame, "No top frame!");
    809 
    810  MonitorAutoUnlock unlock(*mMonitor);
    811 
    812  bool timedout = false;
    813 
    814  UINT_PTR timerId = 0;
    815  TimeoutData timeoutData = {0};
    816 
    817  if (mTimeoutMs != kNoTimeout) {
    818    InitTimeoutData(&timeoutData, mTimeoutMs);
    819 
    820    // We only do this to ensure that we won't get stuck in
    821    // MsgWaitForMultipleObjects below.
    822    timerId = SetTimer(nullptr, 0, mTimeoutMs, nullptr);
    823    NS_ASSERTION(timerId, "SetTimer failed!");
    824  }
    825 
    826  NeuteredWindowRegion neuteredRgn(true);
    827 
    828  {
    829    while (1) {
    830      MSG msg = {0};
    831      // Don't get wrapped up in here if the child connection dies.
    832      {
    833        MonitorAutoLock lock(*mMonitor);
    834        if (!Connected()) {
    835          break;
    836        }
    837      }
    838 
    839      // Wait until we have a message in the queue. MSDN docs are a bit unclear
    840      // but it seems that windows from two different threads (and it should be
    841      // noted that a thread in another process counts as a "different thread")
    842      // will implicitly have their message queues attached if they are parented
    843      // to one another. This wait call, then, will return for a message
    844      // delivered to *either* thread.
    845      DWORD result =
    846          MsgWaitForMultipleObjects(1, &mEvent, FALSE, INFINITE, QS_ALLINPUT);
    847      if (result == WAIT_OBJECT_0) {
    848        // Our NotifyWorkerThread event was signaled
    849        BOOL success = ResetEvent(mEvent);
    850        if (!success) {
    851          gfxDevCrash(mozilla::gfx::LogReason::MessageChannelInvalidHandle)
    852              << "WindowsMessageChannel::WaitForSyncNotify failed to reset "
    853                 "event. GetLastError: "
    854              << GetLastError();
    855        }
    856        break;
    857      } else if (result != (WAIT_OBJECT_0 + 1)) {
    858        NS_ERROR("Wait failed!");
    859        break;
    860      }
    861 
    862      if (TimeoutHasExpired(timeoutData)) {
    863        // A timeout was specified and we've passed it. Break out.
    864        timedout = true;
    865        break;
    866      }
    867 
    868      // The only way to know on which thread the message was delivered is to
    869      // use some logic on the return values of GetQueueStatus and PeekMessage.
    870      // PeekMessage will return false if there are no "queued" messages, but it
    871      // will run all "nonqueued" messages before returning. So if PeekMessage
    872      // returns false and there are no "nonqueued" messages that were run then
    873      // we know that the message we woke for was intended for a window on
    874      // another thread.
    875      bool haveSentMessagesPending =
    876          (HIWORD(GetQueueStatus(QS_SENDMESSAGE)) & QS_SENDMESSAGE) != 0;
    877 
    878      // Either of the PeekMessage calls below will actually process all
    879      // "nonqueued" messages that are pending before returning. If we have
    880      // "nonqueued" messages pending then we should have switched out all the
    881      // window procedures above. In that case this PeekMessage call won't
    882      // actually cause any mozilla code (or plugin code) to run.
    883 
    884      // We have to manually pump all COM messages *after* looking at the queue
    885      // queue status but before yielding our thread below.
    886      if (gCOMWindow) {
    887        if (PeekMessageW(&msg, gCOMWindow, 0, 0, PM_REMOVE)) {
    888          TranslateMessage(&msg);
    889          ::DispatchMessageW(&msg);
    890        }
    891      }
    892 
    893      // If the following PeekMessage call fails to return a message for us (and
    894      // returns false) and we didn't run any "nonqueued" messages then we must
    895      // have woken up for a message designated for a window in another thread.
    896      // If we loop immediately then we could enter a tight loop, so we'll give
    897      // up our time slice here to let the child process its message.
    898      if (!PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE) &&
    899          !haveSentMessagesPending) {
    900        // Message was for child, we should wait a bit.
    901        SwitchToThread();
    902      }
    903    }
    904  }
    905 
    906  if (timerId) {
    907    KillTimer(nullptr, timerId);
    908    timerId = 0;
    909  }
    910 
    911  return WaitResponse(timedout);
    912 }
    913 
    914 void MessageChannel::NotifyWorkerThread() {
    915  mMonitor->AssertCurrentThreadOwns();
    916 
    917  if (mIsSyncWaitingOnNonMainThread) {
    918    mMonitor->Notify();
    919    return;
    920  }
    921 
    922  MOZ_RELEASE_ASSERT(mEvent, "No signal event to set, this is really bad!");
    923  if (!SetEvent(mEvent)) {
    924    NS_WARNING("Failed to set NotifyWorkerThread event!");
    925    gfxDevCrash(mozilla::gfx::LogReason::MessageChannelInvalidHandle)
    926        << "WindowsMessageChannel failed to SetEvent. GetLastError: "
    927        << GetLastError();
    928  }
    929 }
    930 
    931 void DeferredSendMessage::Run() {
    932  AssertWindowIsNotNeutered(hWnd);
    933  if (!IsWindow(hWnd)) {
    934    NS_ERROR("Invalid window!");
    935    return;
    936  }
    937 
    938  WNDPROC wndproc =
    939      reinterpret_cast<WNDPROC>(GetWindowLongPtr(hWnd, GWLP_WNDPROC));
    940  if (!wndproc) {
    941    NS_ERROR("Invalid window procedure!");
    942    return;
    943  }
    944 
    945  CallWindowProc(wndproc, hWnd, message, wParam, lParam);
    946 }
    947 
    948 void DeferredRedrawMessage::Run() {
    949  AssertWindowIsNotNeutered(hWnd);
    950  if (!IsWindow(hWnd)) {
    951    NS_ERROR("Invalid window!");
    952    return;
    953  }
    954 
    955 #ifdef DEBUG
    956  BOOL ret =
    957 #endif
    958      RedrawWindow(hWnd, nullptr, nullptr, flags);
    959  NS_ASSERTION(ret, "RedrawWindow failed!");
    960 }
    961 
    962 DeferredUpdateMessage::DeferredUpdateMessage(HWND aHWnd) {
    963  mWnd = aHWnd;
    964  if (!GetUpdateRect(mWnd, &mUpdateRect, FALSE)) {
    965    memset(&mUpdateRect, 0, sizeof(RECT));
    966    return;
    967  }
    968  ValidateRect(mWnd, &mUpdateRect);
    969 }
    970 
    971 void DeferredUpdateMessage::Run() {
    972  AssertWindowIsNotNeutered(mWnd);
    973  if (!IsWindow(mWnd)) {
    974    NS_ERROR("Invalid window!");
    975    return;
    976  }
    977 
    978  InvalidateRect(mWnd, &mUpdateRect, FALSE);
    979 #ifdef DEBUG
    980  BOOL ret =
    981 #endif
    982      UpdateWindow(mWnd);
    983  NS_ASSERTION(ret, "UpdateWindow failed!");
    984 }
    985 
    986 DeferredSettingChangeMessage::DeferredSettingChangeMessage(HWND aHWnd,
    987                                                           UINT aMessage,
    988                                                           WPARAM aWParam,
    989                                                           LPARAM aLParam)
    990    : DeferredSendMessage(aHWnd, aMessage, aWParam, aLParam) {
    991  NS_ASSERTION(aMessage == WM_SETTINGCHANGE, "Wrong message type!");
    992  if (aLParam) {
    993    lParamString = _wcsdup(reinterpret_cast<const wchar_t*>(aLParam));
    994    lParam = reinterpret_cast<LPARAM>(lParamString);
    995  } else {
    996    lParamString = nullptr;
    997    lParam = 0;
    998  }
    999 }
   1000 
   1001 DeferredSettingChangeMessage::~DeferredSettingChangeMessage() {
   1002  free(lParamString);
   1003 }
   1004 
   1005 DeferredWindowPosMessage::DeferredWindowPosMessage(HWND aHWnd, LPARAM aLParam,
   1006                                                   bool aForCalcSize,
   1007                                                   WPARAM aWParam) {
   1008  if (aForCalcSize) {
   1009    if (aWParam) {
   1010      NCCALCSIZE_PARAMS* arg = reinterpret_cast<NCCALCSIZE_PARAMS*>(aLParam);
   1011      memcpy(&windowPos, arg->lppos, sizeof(windowPos));
   1012 
   1013      NS_ASSERTION(aHWnd == windowPos.hwnd, "Mismatched hwnds!");
   1014    } else {
   1015      RECT* arg = reinterpret_cast<RECT*>(aLParam);
   1016      windowPos.hwnd = aHWnd;
   1017      windowPos.hwndInsertAfter = nullptr;
   1018      windowPos.x = arg->left;
   1019      windowPos.y = arg->top;
   1020      windowPos.cx = arg->right - arg->left;
   1021      windowPos.cy = arg->bottom - arg->top;
   1022 
   1023      NS_ASSERTION(arg->right >= arg->left && arg->bottom >= arg->top,
   1024                   "Negative width or height!");
   1025    }
   1026    windowPos.flags = SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOOWNERZORDER |
   1027                      SWP_NOZORDER | SWP_DEFERERASE | SWP_NOSENDCHANGING;
   1028  } else {
   1029    // Not for WM_NCCALCSIZE
   1030    WINDOWPOS* arg = reinterpret_cast<WINDOWPOS*>(aLParam);
   1031    memcpy(&windowPos, arg, sizeof(windowPos));
   1032 
   1033    NS_ASSERTION(aHWnd == windowPos.hwnd, "Mismatched hwnds!");
   1034 
   1035    // Windows sends in some private flags sometimes that we can't simply copy.
   1036    // Filter here.
   1037    UINT mask = SWP_ASYNCWINDOWPOS | SWP_DEFERERASE | SWP_DRAWFRAME |
   1038                SWP_FRAMECHANGED | SWP_HIDEWINDOW | SWP_NOACTIVATE |
   1039                SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOREDRAW |
   1040                SWP_NOREPOSITION | SWP_NOSENDCHANGING | SWP_NOSIZE |
   1041                SWP_NOZORDER | SWP_SHOWWINDOW;
   1042    windowPos.flags &= mask;
   1043  }
   1044 }
   1045 
   1046 void DeferredWindowPosMessage::Run() {
   1047  AssertWindowIsNotNeutered(windowPos.hwnd);
   1048  if (!IsWindow(windowPos.hwnd)) {
   1049    NS_ERROR("Invalid window!");
   1050    return;
   1051  }
   1052 
   1053  if (!IsWindow(windowPos.hwndInsertAfter)) {
   1054    NS_WARNING("ZOrder change cannot be honored");
   1055    windowPos.hwndInsertAfter = 0;
   1056    windowPos.flags |= SWP_NOZORDER;
   1057  }
   1058 
   1059 #ifdef DEBUG
   1060  BOOL ret =
   1061 #endif
   1062      SetWindowPos(windowPos.hwnd, windowPos.hwndInsertAfter, windowPos.x,
   1063                   windowPos.y, windowPos.cx, windowPos.cy, windowPos.flags);
   1064  NS_ASSERTION(ret, "SetWindowPos failed!");
   1065 }
   1066 
   1067 DeferredCopyDataMessage::DeferredCopyDataMessage(HWND aHWnd, UINT aMessage,
   1068                                                 WPARAM aWParam, LPARAM aLParam)
   1069    : DeferredSendMessage(aHWnd, aMessage, aWParam, aLParam) {
   1070  NS_ASSERTION(IsWindow(reinterpret_cast<HWND>(aWParam)), "Bad window!");
   1071 
   1072  COPYDATASTRUCT* source = reinterpret_cast<COPYDATASTRUCT*>(aLParam);
   1073  NS_ASSERTION(source, "Should never be null!");
   1074 
   1075  copyData.dwData = source->dwData;
   1076  copyData.cbData = source->cbData;
   1077 
   1078  if (source->cbData) {
   1079    copyData.lpData = malloc(source->cbData);
   1080    if (copyData.lpData) {
   1081      memcpy(copyData.lpData, source->lpData, source->cbData);
   1082    } else {
   1083      NS_ERROR("Out of memory?!");
   1084      copyData.cbData = 0;
   1085    }
   1086  } else {
   1087    copyData.lpData = nullptr;
   1088  }
   1089 
   1090  lParam = reinterpret_cast<LPARAM>(&copyData);
   1091 }
   1092 
   1093 DeferredCopyDataMessage::~DeferredCopyDataMessage() { free(copyData.lpData); }
   1094 
   1095 DeferredStyleChangeMessage::DeferredStyleChangeMessage(HWND aHWnd,
   1096                                                       WPARAM aWParam,
   1097                                                       LPARAM aLParam)
   1098    : hWnd(aHWnd) {
   1099  index = static_cast<int>(aWParam);
   1100  style = reinterpret_cast<STYLESTRUCT*>(aLParam)->styleNew;
   1101 }
   1102 
   1103 void DeferredStyleChangeMessage::Run() { SetWindowLongPtr(hWnd, index, style); }
   1104 
   1105 DeferredSetIconMessage::DeferredSetIconMessage(HWND aHWnd, UINT aMessage,
   1106                                               WPARAM aWParam, LPARAM aLParam)
   1107    : DeferredSendMessage(aHWnd, aMessage, aWParam, aLParam) {
   1108  NS_ASSERTION(aMessage == WM_SETICON, "Wrong message type!");
   1109 }
   1110 
   1111 void DeferredSetIconMessage::Run() {
   1112  AssertWindowIsNotNeutered(hWnd);
   1113  if (!IsWindow(hWnd)) {
   1114    NS_ERROR("Invalid window!");
   1115    return;
   1116  }
   1117 
   1118  WNDPROC wndproc =
   1119      reinterpret_cast<WNDPROC>(GetWindowLongPtr(hWnd, GWLP_WNDPROC));
   1120  if (!wndproc) {
   1121    NS_ERROR("Invalid window procedure!");
   1122    return;
   1123  }
   1124 
   1125  HICON hOld = reinterpret_cast<HICON>(
   1126      CallWindowProc(wndproc, hWnd, message, wParam, lParam));
   1127  if (hOld) {
   1128    DestroyIcon(hOld);
   1129  }
   1130 }