【问题标题】:How to close Windows Explorer windows with opened folders from a certain drive如何使用某个驱动器中打开的文件夹关闭 Windows 资源管理器窗口
【发布时间】:2014-05-31 02:43:39
【问题描述】:

我正在编写一个允许用户弹出(或安全移除)USB 驱动器的小应用程序。我的应用程序工作正常,除了在 Windows 资源管理器中打开 USB 驱动器上的一个文件夹(或多个文件夹)的情况。在这种情况下,弹出功能将失败,因为驱动器似乎已被锁定。

所以我很好奇,由于用户正在通过我的应用程序发出命令以弹出 USB 驱动器,有没有办法让资源管理器从 USB 驱动器关闭那些打开的窗口?

PS。请注意,我不想关闭属于 Windows 资源管理器的所有进程,而只想关闭在特定驱动器上打开文件夹的进程。

【问题讨论】:

  • 棘手,这个。 Windows 资源管理器窗口都具有相同的进程 ID(通常),您不想停止它。但是,当您使用默认的 Windows 通知区域图标安全地弹出 USB 密钥时,它会关闭任何相关的资源管理器窗口——也许您可以弄清楚为此调用了哪些 API 函数?
  • @Cameron:嗯,这就是我想要做的。
  • 我认为以编程方式销毁属于另一个进程的窗口的唯一方法是要求该进程自行销毁它。通知区域图标大概在 explorer.exe 的控制下,因此它能够杀死那些窗口。您需要一种直接与 explorer.exe 对话并要求它终止窗口的方法,或者您可以合成单击窗口本身的关闭按钮。
  • 您可以使用ShellWindows object 并枚举打开的窗口。那些打开到你不喜欢的文件夹,你可以将它们导航到我的电脑。
  • 这个问题能否为您可以使用的某些 WinAPI 提供任何帮助:stackoverflow.com/questions/85649/… ??

标签: c++ windows winapi windows-shell windows-explorer


【解决方案1】:
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++ 重写它。
【解决方案2】:

这是为 C++ 重写的 @DenisAnisimov 的方法:

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

【讨论】:

  • 您实际上并不需要 SFGAO_FILESYSTEM 检查。您可以将 SHGetNameFromIDList 与 SIGDN_DESKTOPABSOLUTEPARSIN 一起使用,这比 STRRET 古老的东西更容易使用。否则,我建议您对所有带有 IID 和 void** 的调用使用酷 IID_PPV_ARGS 宏,如下所示:pIShView-&gt;QueryInterface(IID_PPV_ARGS(&amp;pIFV))。最后一件事,有时窗口很忙(用户可能正在玩它或者他们刚刚打开 - 可能存在竞争条件)。您可以使用 IWebBrowser2->get_Busy 进行检查。我通常会在返回硬错误之前添加一个类似 X 重试的循环。
  • 这仅适用于我的桌面。有没有办法让函数 CoCreateInstance() 在 Windows CE 上也能工作?如果我复制这段代码,函数返回:REGDB_E_CLASSNOTREG,如果我做一些更改,错误是:CO_E_NOTINITIALIZED, E_NOINTERFACE
猜你喜欢
  • 1970-01-01
  • 2017-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-17
  • 1970-01-01
相关资源
最近更新 更多