如何关闭打开了特定驱动器文件夹的Windows资源管理器窗口

3
我正在编写一个小应用程序,允许用户弹出(或安全移除)USB驱动器。我的应用程序工作得很好,除了一个情况:当Windows资源管理器中打开了USB驱动器上的文件夹时,弹出功能会失败,因为驱动器似乎被锁定了。
所以我很好奇,既然用户通过我的应用程序发出命令弹出USB驱动器,是否有办法让资源管理器关闭那些打开了USB驱动器上文件夹的窗口?
PS. 注意,我不想关闭属于Windows资源管理器的所有进程,只想关闭打开了特定驱动器上文件夹的进程。

这有点棘手。Windows资源管理器窗口通常具有相同的进程ID,您不希望停止该进程。但是,当您使用默认的Windows通知区域图标安全地弹出USB密钥时,它确实会关闭任何相关的资源管理器窗口 - 或许您可以找出正在调用哪些API函数? - Cameron
@Cameron:嗯,那就是我在这里试图要做的。 - c00000fd
我认为唯一可以编程销毁属于另一个进程的窗口的方法是要求该进程自行销毁它。通知区域图标可能在 explorer.exe 的控制下,因此它能够关闭这些窗口。您需要一种直接与 explorer.exe 交流并要求其关闭窗口的方法,或者您可能会合成单击窗口本身的关闭按钮。 - ooga
2
你可以使用ShellWindows对象并枚举打开的窗口。对于你不喜欢的打开到文件夹的窗口,你可以将它们导航到“我的电脑”。 - Raymond Chen
这个问题能否提供一些帮助,关于你可以使用的一些WinAPI:https://dev59.com/questions/2XVD5IYBdhLWcg3wHnoX? - txtechhelp
@RaymondChen:嘿,雷蒙德,怎么样换个方式链接到你自己的文章 :) http://blogs.msdn.com/b/oldnewthing/archive/2004/07/20/188696.aspx - c00000fd
2个回答

3
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;

谢谢。但是,老兄,那个Delphi让我崩溃了... :) 我会用C++重写它。 - c00000fd

2

这是@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;
}

1
你实际上不需要SFGAO_FILESYSTEM检查。你可以使用带有SIGDN_DESKTOPABSOLUTEPARSIN的SHGetNameFromIDList,它比STRRET古老的东西更容易使用。否则,我建议您对所有具有IID和void **的调用使用酷炫的IID_PPV_ARGS宏,例如:pIShView->QueryInterface(IID_PPV_ARGS(&pIFV))。最后一件事,有时窗口很忙(用户可能正在操作它或者他们刚打开 - 可能存在竞争条件)。您可以使用IWebBrowser2->get_Busy进行检查。我通常会添加一个循环,在返回硬错误之前重试X次。 - Simon Mourier
这个只在我的桌面上运行。有没有办法让CoCreateInstance()函数也在Windows CE上工作?如果我复制这段代码,函数会返回:REGDB_E_CLASSNOTREG,如果我做一些更改,错误会是:CO_E_NOTINITIALIZED,E_NOINTERFACE。 - Racky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接