это ошибка windows, в версии 1803. минимальный код для воспроизведения:
if (0 <= CoInitialize(0))
{
SHELLEXECUTEINFO sei = {
sizeof(sei), 0, 0, 0, L"notepad.exe", 0, 0, SW_SHOW
};
ShellExecuteEx( &sei );
CoUninitialize();
}
после выполнения этого кода может просматривать дескрипторы процесса notepad.exe и первого потока - эти дескрипторы, конечно, не должны существовать(быть закрытым), не закрытые ключи
\REGISTRY\MACHINE\SOFTWARE\Classes\.exe
\REGISTRY\MACHINE\SOFTWARE\Classes\exefile
, также после этого вызова происходят утечки в частной памяти.
конечно, эта ошибка вызывает постоянные утечки ресурсов в explorer.exe и любой процесс, который использует ShellExecute[Ex]
, точно исследует эту ошибку - здесь
Основная проблема здесь, по-видимому, заключается в windows.storage.dll .В частности, объект CInvokeCreateProcessVerb
никогда не уничтожается, поскольку связанный счетчик ссылок никогда не достигает 0. Это приводит к утечке всех объектов, связанных с CInvokeCreateProcessVerb
, включая 4 дескриптора и некоторыепамять.
Причина, по которой счетчик ссылок никогда не достигает 0, по-видимому, связана с изменением аргумента для ShellDDEExec::InitializeByShellInternal
с Windows 10 1709 на 1803, выполненного CInvokeCreateProcessVerb::Launch()
.
более конкретная здесь - циклическая ссылка объекта (CInvokeCreateProcessVerb
) на самого себя.
более конкретная ошибка внутри метода CInvokeCreateProcessVerb::Launch()
, вызывающего из себя
HRESULT ShellDDEExec::InitializeByShellInternal(
IAssociationElement*,
CreateProcessMethod,
PCWSTR,
STARTUPINFOEXW*,
IShellItem2*,
IUnknown*, // !!!
PCWSTR,
PCWSTR,
PCWSTR);
с неправильным 6 аргументом.класс CInvokeCreateProcessVerb
, содержащий внутренний подобъект ShellDDEExec
.в окнах 1709 CInvokeCreateProcessVerb::Launch()
передайте указатель на static_cast<IServiceProvider*>(pObj)
вместо аргумента вместо 6 на ShellDDEExec::InitializeByShellInternal
, где pObj
указывает на экземпляр класса CBindAndInvokeStaticVerb
.но в версии 1803 здесь передается указатель на static_cast<IServiceProvider*>(this)
- поэтому указатель на self .InitializeByShellInternal
хранит этот указатель внутри себя и добавляет ссылку на него.обратите внимание, что ShellDDEExec
является подобъектом из CInvokeCreateProcessVerb
.поэтому деструктор ShellDDEExec
не будет вызван, пока деструктор CInvokeCreateProcessVerb
не будет вызван.но деструктор CInvokeCreateProcessVerb
не будет вызываться до тех пор, пока счетчик ссылок не достигнет 0. Но этого не произойдет, пока ShellDDEExec
не освободит собственный указатель на CInvokeCreateProcessVerb
, который будет только внутри него, деструктор.
может бытьэто более заметно в псевдокоде
class ShellDDEExec
{
CComPtr<IUnknown*> _pUnk;
HRESULT InitializeByShellInternal(..IUnknown* pUnk..)
{
_pUnk = pUnk;
}
};
class CInvokeCreateProcessVerb : CExecuteCommandBase, IServiceProvider /**/
{
IServiceProvider* _pVerb;//point to static_cast<IServiceProvider*>(CBindAndInvokeStaticVerb*)
ShellDDEExec _exec;
TRYRESULT CInvokeCreateProcessVerb::Launch()
{
// in 1709
// _exec.InitializeByShellInternal(_pVerb);
// in 1803
_exec.InitializeByShellInternal(..static_cast<IServiceProvider*>(this)..); // !! error !!
}
};
ShellDDEExec::_pUnk
удерживать указатель на содержащий объект CInvokeCreateProcessVerb
этот указатель будет освобожден только внутри CComPtr
деструктора, вызываемого из ShellDDEExec
деструктора.вызывается из деструктора CInvokeCreateProcessVerb
, вызывается, когда счетчик ссылок становится равным 0, но этого никогда не происходит, потому что дополнительная ссылка удерживает ShellDDEExec::_pUnk
, поэтому объект хранилища ссылается на указатель на себя.после этого счетчик ссылок на CInvokeCreateProcessVerb
никогда не достигает 0