Унаследовано приложение на основе MFC, в котором размещены как оконные, так и безоконные элементы управления ActiveX. В этом приложении пользователь может создавать страницы SCADA, на которых он / она может динамически создавать и удалять эти элементы управления AX. Элементы управления в основном простые виджеты, такие как кнопки, индикации, линии, круги и т. Д.
После обновления до более новой версии Visual Studio (2017) программа теперь падает при удалении элементов управления AX. Изначально это был VC6.
В частности, большинство сбоев происходит при попытке удалить безоконный элемент управления AX.
Если эти элементы управления AX размещены в другом хост-приложении, например Internet Explorer, то сбой не происходит.
Он также может аварийно завершить работу при выходе из программы, поскольку тогда все элементы управления AX на странице также удаляются по одному.
Более конкретно, порядок, в котором создаются объекты, имеет значение. Если вы удалите объект без окон, и следующий объект, находящийся в фокусе, будет оконным объектом, вы в безопасности: сбоя не происходит. Я думаю, что код фреймворка «Маршрут событий здесь» затем выбирает оконный объект в качестве допустимой цели для оконных сообщений, и с этого момента все будет хорошо. Однако, если новый объект также без окон, вам не повезло.
Обратите внимание, что мы создаем все элементы управления, используя WS_DISABLED
флаг, в противном случае виджет кнопки, например, начнет реагировать на щелчки мыши, когда пользователь изменяет размеры и перемещается вокруг виджета.
Когда после удаления элемента управления AX без окна возникает GPF, отладчик прерывается и останавливается в файле контейнера OLE. occcont.cpp
функция COleControlContainer::HandleWindowlessMessage
, В этот момент он хочет позвонить m_pSiteFocus->m_pWindowlessObject->OnWindowMessage
,
Это в контексте окна «контейнера», в котором размещены все элементы управления AX. Сообщение Window собирается направить на текущий элемент управления AX.
Но здесь выбран неправильный пункт назначения, потому что m_pWindowlessObject
является 0xdddddddd
(что означает освобождение памяти).
Мне кажется, что объект фокуса сайта больше не нужно выбирать, потому что весь его контент был уничтожен вместе с удаленным элементом управления AX!
Забавно, что действие удаления самого элемента управления не вызывает никаких (прямых) проблем. Косвенным образом сообщения окна «post delete» теперь хотят быть доставлены в недействительный контейнер управления OLE. И даже когда программа закрывается, она все равно может аварийно завершить работу из-за более раннего удаления управляющего действия AX.
Кстати: элемент управления AX создается с CWnd::CreateControl
и удаляется снова с вызовом CWnd::DestroyWindow()
,
В качестве обходного пути, сразу после этого вызова окна уничтожения, я заставляю нарушающий указатель на нулевой указатель:
COleControlContainer * pContainer = m_wndContainer.GetControlContainer();
const COleControlContainer * pFreedMemory(reinterpret_cast<COleControlContainer*>(0xdddddddd));
if (pContainer != nullptr && pContainer != pFreedMemory) {
// After we delete the control, the library wants to focus another control.
// But if there is no more control or the next control is also windowless/disabled
// it will bump into a stale pointer (0xdddddddd). The code does check for a null pointer,
// so we force a null pointer.
pContainer->m_pSiteFocus = 0;
}
Теперь программа больше не падает, но это ужасно!
Таким образом, вопрос: я делаю что-то не так, сталкиваюсь ли я с ошибкой в фреймворке или, возможно, с ошибкой в элементе управления AX без окон?
Обновление № 1:
Нашел этот фрагмент кода в МФЦ occsite.cpp
BOOL COleControlSite::DestroyControl()
...
//VBBUG: VB controls will crash if IOleObject::Close is called on them
// when they have focus (and unfortunately, deactivating them does not
// always move the focus). To work around this problem, we always hide
// the control before closing it.
ShowWindow(SW_HIDE);
...
Очевидно, это обходной путь для аналогичной (если не той же) проблемы.
Обновление № 2:
Я обнаружил, что для элементов управления AX их деструктор даже не вызывали. Затем действительно нашел указатель интерфейса с подсчетом ссылок, который не был должным образом обработан. Как только iinspectable предсказано в комментариях!
Задача ещё не решена.
Других решений пока нет …