How to close Windows Explorer windows with open folders from a certain drive

advertisements

I'm writing a small app that will allow a user to eject (or safely remove) a USB drive. My app works fine, except the situation when a folder on the USB drive (or several folders) are opened in Windows Explorer. In that case the eject function fails as the drive appears to be locked.

So I'm curious, since the user is issuing a command through my app to eject the USB drive, is there any way to make Explorer close those open windows from the USB drive?

PS. Note that I don't want to close all processes belonging to the Windows Explorer, but only the ones that opened folders on a specific drive.


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;