Я пишу небольшое приложение, которое позволит пользователю извлечь (или безопасно удалить) USB-накопитель. Мое приложение работает нормально, за исключением ситуации, когда папка на USB-накопителе (или несколько папок) открываются в проводнике Windows. В этом случае функция извлечения не выполняется, так как кажется, что привод заблокирован.
Поэтому мне любопытно, так как пользователь запускает через мое приложение команду для извлечения USB-накопителя, есть ли способ заставить Проводник закрыть эти открытые окна с USB-накопителя?
PS. Обратите внимание, что я не хочу закрывать все процессы, относящиеся к проводнику Windows, а только те, которые открывали папки на определенном диске.
procedure ProcessExplorerWindows(ADriveLetter: WideChar; AClose: Boolean);
var
ShellWindows: IShellWindows;
i: Integer;
Dispatch: IDispatch;
WebBrowser2: IWebBrowser2;
ServiceProvider: IServiceProvider;
ShellBrowser: IShellBrowser;
ShellView: IShellView;
FolderView: IFolderView;
PersistFolder2: IPersistFolder2;
ItemIDList: PItemIDList;
ShellFolder: IShellFolder;
ChildItem: PItemIDList;
Attr: DWORD;
StrRet: TStrRet;
FileName: UnicodeString;
DesktopItemIDList: PItemIDList;
begin
OleCheck(CoCreateInstance(CLASS_ShellWindows, nil, CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER, IShellWindows, ShellWindows));
try
for i := ShellWindows.Count - 1 downto 0 do
begin
Dispatch := ShellWindows.Item(i);
try
OleCheck(Dispatch.QueryInterface(IWebBrowser2, WebBrowser2));
try
OleCheck(Dispatch.QueryInterface(IServiceProvider, ServiceProvider));
try
OleCheck(ServiceProvider.QueryService(SID_STopLevelBrowser, IShellBrowser, ShellBrowser));
try
OleCheck(ShellBrowser.QueryActiveShellView(ShellView));
try
OleCheck(ShellView.QueryInterface(IFolderView, FolderView));
try
OleCheck(FolderView.GetFolder(IPersistFolder2, PersistFolder2));
try
OleCheck(PersistFolder2.GetCurFolder(ItemIDList));
try
OleCheck(SHBindToParent(ItemIDList, IShellFolder, Pointer(ShellFolder), ChildItem));
try
Attr := SFGAO_FILESYSTEM;
OleCheck(ShellFolder.GetAttributesOf(1, ChildItem, Attr));
if Attr and SFGAO_FILESYSTEM = SFGAO_FILESYSTEM then
begin
OleCheck(ShellFolder.GetDisplayNameOf(ChildItem, SHGDN_FORPARSING, StrRet));
FileName := '';
case StrRet.uType of
STRRET_WSTR:
begin
FileName := StrRet.pOleStr;
CoTaskMemFree(StrRet.pOleStr);
end;
STRRET_OFFSET:
if Assigned(ChildItem) then
begin
Inc(PByte(ChildItem), StrRet.uOffset);
FileName := UnicodeString(PAnsiChar(ChildItem));
end;
STRRET_CSTR:
FileName := UnicodeString(AnsiString(StrRet.cStr));
end;
if ExtractFileDrive(FileName) = ADriveLetter + ':' then
if AClose then
WebBrowser2.Quit
else
begin
SHGetFolderLocation(0, CSIDL_DESKTOP, 0, 0, DesktopItemIDList);
try
OleCheck(ShellBrowser.BrowseObject(DesktopItemIDList, SBSP_SAMEBROWSER or SBSP_DEFMODE or SBSP_ABSOLUTE));
finally
CoTaskMemFree(DesktopItemIDList);
end;
end;
end;
finally
ShellFolder := nil;
end;
finally
CoTaskMemFree(ItemIDList);
end;
finally
PersistFolder2 := nil
end;
finally
FolderView := nil;
end;
finally
ShellView := nil;
end;
finally
ShellBrowser := nil;
end;
finally
ServiceProvider := nil;
end;
finally
WebBrowser2 := nil;
end;
finally
Dispatch := nil;
end;
end;
finally
ShellWindows := nil;
end;
end;
Вот метод @ DenisAnisimov, переписанный для C ++:
HRESULT CloseWindowsExplorerWindowsForDrive(LPCTSTR pStrPath)
{
//Closes all Windows Explorer windows for a specific drive
//'pStrPath' = path somewhere on the drive
//RETURN:
// = S_OK if done
CoInitialize(NULL);
HRESULT hr;
TCHAR buffVolPath[MAX_PATH];
buffVolPath[0] = 0;
if(GetVolumePathName(pStrPath, buffVolPath, MAX_PATH))
{
int nLnVolPath = lstrlen(buffVolPath);
IShellWindows* pISW = NULL;
hr = CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
IID_IShellWindows, (void**) &pISW);
if (SUCCEEDED(hr))
{
long nCount;
if(SUCCEEDED(hr = pISW->get_Count(&nCount)))
{
for(int i = nCount - 1; i >= 0; i--)
{
CComPtr<IDispatch> pIDisp;
CComVariant v_i(i);
if(SUCCEEDED(hr = pISW->Item(v_i, &pIDisp)))
{
CComPtr<IWebBrowser2> pIWB2;
if(SUCCEEDED(pIDisp->QueryInterface(IID_IWebBrowser2, (void**)&pIWB2)))
{
CComPtr<IServiceProvider> pISP;
if(SUCCEEDED(pIDisp->QueryInterface(IID_IServiceProvider, (void**)&pISP)))
{
CComPtr<IShellBrowser> pIShBrswr;
if(SUCCEEDED(hr= pISP->QueryService(SID_STopLevelBrowser, IID_IShellBrowser, (void**)&pIShBrswr)))
{
CComPtr<IShellView> pIShView;
if(SUCCEEDED(hr = pIShBrswr->QueryActiveShellView(&pIShView)))
{
CComPtr<IFolderView> pIFV;
if(SUCCEEDED(hr = pIShView->QueryInterface(IID_IFolderView, (void**)&pIFV)))
{
CComPtr<IPersistFolder2> pIPF2;
if(SUCCEEDED(hr = pIFV->GetFolder(IID_IPersistFolder2, (void**)&pIPF2)))
{
LPITEMIDLIST pidlFolder = NULL;
if(SUCCEEDED(hr = pIPF2->GetCurFolder(&pidlFolder)))
{
LPCITEMIDLIST pidlChild = NULL;
CComPtr<IShellFolder> pIShFldr;
if(SUCCEEDED(::SHBindToParent(pidlFolder, IID_IShellFolder, (void**)&pIShFldr, &pidlChild)))
{
ULONG attrs = SFGAO_FILESYSTEM;
if(SUCCEEDED(hr = pIShFldr->GetAttributesOf(1, &pidlChild, &attrs)))
{
if(attrs & SFGAO_FILESYSTEM)
{
STRRET srt;
if(SUCCEEDED(hr = pIShFldr->GetDisplayNameOf(pidlChild, SHGDN_FORPARSING, &srt)))
{
LPWSTR pStrFileName = NULL;
if(SUCCEEDED(hr = StrRetToStr(&srt, pidlChild, &pStrFileName)))
{
//Compare to our path
if(lstrlen(pStrFileName) >= nLnVolPath &&
::CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE,
buffVolPath, nLnVolPath,
pStrFileName, nLnVolPath) == CSTR_EQUAL)
{
//Close it
hr = pIWB2->Quit();
}
}
if(pStrFileName)
{
CoTaskMemFree(pStrFileName);
pStrFileName = NULL;
}
//Free mem (if StrRetToStr() hasn't done it)
if(srt.pOleStr)
{
CoTaskMemFree(srt.pOleStr);
}
}
}
}
}
//No need to free pidlChild!
}
if(pidlFolder)
{
CoTaskMemFree(pidlFolder);
pidlFolder = NULL;
}
}
}
}
}
}
}
}
}
}
pISW->Release();
}
}
else
hr = E_INVALIDARG;
CoUninitialize();
return hr;
}