Я поддерживаю приложение, которое использует значки наложения Windows Explorer. Иногда некоторые операции требуют принудительного обновления представления исследователей для определенной папки. Я делаю это, используя следующую функцию, которая использует COM:
void RefreshExplorerView(CString strPath)
{
CComPtr<IShellWindows> pShellWindows;
CoInitialize(NULL);
if(SUCCEEDED(pShellWindows.CoCreateInstance(CLSID_ShellWindows)))
{
IDispatch* pFolder=NULL;
VARIANT variant;
V_VT(&variant) = VT_I4;
for(V_I4(&variant) = 0; pShellWindows->Item(variant, &pFolder) == S_OK; V_I4(&variant)++)
{
CComPtr<IWebBrowserApp> pWebBrowserApp;
if(SUCCEEDED(pFolder->QueryInterface(IID_PPV_ARGS(&pWebBrowserApp))))
{
BSTR LocationURL = NULL;
pWebBrowserApp->get_LocationURL(&LocationURL);
if(LocationURL != NULL && strPath.CompareNoCase(LocationURL) == 0)
{
CComPtr<IServiceProvider> pServiceProvider;
if(SUCCEEDED(pWebBrowserApp->QueryInterface(IID_PPV_ARGS(&pServiceProvider))))
{
CComPtr<IShellBrowser> pShellBrowser;
if(SUCCEEDED(pServiceProvider->QueryInterface(IID_PPV_ARGS(&pShellBrowser))))
{
IShellView* pShellView;
if(SUCCEEDED(pShellBrowser->QueryActiveShellView(&pShellView)))
{
pShellView->Refresh();
pShellView->Release();
}
}
}
}
SysFreeString(LocationURL);
}
pFolder->Release();
pFolder = NULL;
}
}
CoUninitialize();
}
Я заметил, что когда моя программа регулярно обновляет эту программу, она медленно увеличивается в размере, и UMDH показал мне, что у меня течь pFolder
а также pShellWindow
экземпляры каждый раз, когда это работает. Я не могу понять, почему на земле это происходит, поскольку, насколько я могу судить, они выпущены должным образом. Кто-нибудь может увидеть, что мне не хватает?
Вы отпускаете pShellWindows
после CoUninitialize
, что неверно.
Остальные интерфейсы вроде бы выпущены нормально. Обратите внимание, что вы можете улучшить чистоту и читабельность значительно используя CComQIPtr
вместо QueryInterface
и не используя сырые указатели вообще (BSTR
, IFoo*
) и замените их интеллектуальными автоматически выпускающими обертками.
pFolder
может тоже протекать, если Item
вызов выполнен успешно, но возвращает код, отличный от S_OK
, Опять же, использование CComPtr<IFolder>
вместо IFolder*
немедленно разрешит эту проблему, даже не обращая на это никакого внимания.
CoInitialize(NULL);
Есть несколько проблем с этим утверждением. @Roman объяснил, как можно протечь, слишком рано инициализируя. Но это также пойдет не так, как надо, так как состояние потока потока — это действительно большая сделка в COM:
Вы не проверяете возвращаемое значение CoInitialize (). Это взорвет клиентское приложение, которое вызывает эту функцию, если оно уже вызвало CoInitializeEx () и выбрало MTA вместо STA. Это приведет к сбою CoInitialize (), вы не сможете изменить состояние потока после его фиксации. Ваш вызов CoUninitialize () разнесет клиентское приложение в клочья, и все последующие COM-вызовы не будут выполнены.
Выбор STA также требует выполнения контракта для однопоточной квартиры. Который заявляет, что вы никогда не блокируете поток, вы в порядке с этим. А также что вы прокачиваете цикл сообщений. Цикл сообщений имеет решающее значение для маршалинга вызовов в однопоточную квартиру. Вы не хорошо, и вы не можете разумно гарантировать, что об этом позаботятся в подобной функции. Особенно важно для интерфейсов оболочки, подавляющее большинство из них не являются потокобезопасными. Следствием не прокачки является тупик. Вы может сойти с рук, не качая, это не гарантированный тупик. Здесь вы получите немного свободы, поскольку это, вероятно, внешние интерфейсы.
В частности, последнее требование может быть выполнено только кодом, который создал поток, вызывающий эту функцию, только он контролирует то, что поток делает, кроме вызова вашей функции. Если вы не можете получить гарантию того, что клиентское приложение правильно инициализирует COM, тогда единственная действительно безопасная вещь — это создать поток самостоятельно.