如何将文件资源管理器中的目录内容显示为缩略图?

问题描述

在 Windows 10 中的 Delphi 10.4.2 win-32 VCL 应用程序中,我使用此代码在 Windows 文件资源管理器中显示目录内容并传递路径,例如C:\MyDirectory\

procedure ShellOpen(const Url: string; const Params: string = '');
begin
  Winapi.ShellAPI.ShellExecute(0,'Open',PChar(Url),PChar(Params),nil,SW_SHOWnorMAL);
end;

这有效。但是如何强制资源管理器使用 THUMBNAILS 显示此目录中的文件?我可以在此过程中使用任何参数吗?

我为此搜索了很多,但没有找到任何东西。

解决方法

您想使用 IFolderView::SetCurrentViewMode 方法。

这是一个 C++(使用 Visual Studio 的 ATL)示例:

int main()
{
    CoInitialize(NULL);
    {
        // get a shell item
        CComPtr<IShellItem> folder;
        ATLASSERT(SUCCEEDED(SHCreateItemFromParsingName(L"c:\\myPath1\myPath2",nullptr,IID_PPV_ARGS(&folder))));

        // get its PIDL
        CComHeapPtr<ITEMIDLIST> pidl;
        ATLASSERT(SUCCEEDED(CComQIPtr<IPersistIDList>(folder)->GetIDList(&pidl)));

        // open the item
        SHELLEXECUTEINFO info = { };
        info.cbSize = sizeof(info);
        info.fMask = SEE_MASK_IDLIST;
        info.nShow = SW_SHOW;
        info.lpIDList = pidl;
        ATLASSERT(ShellExecuteEx(&info));

        // build a variant from the PIDL
        UINT size = ILGetSize(pidl);
        SAFEARRAY* psa = SafeArrayCreateVector(VT_UI1,size);
        CopyMemory(psa->pvData,pidl,size);
        CComVariant v;
        v.parray = psa;
        v.vt = VT_ARRAY | VT_UI1;

        // find the opened window
        CComPtr<IShellWindows> windows;
        ATLASSERT(SUCCEEDED(windows.CoCreateInstance(CLSID_ShellWindows)));

        CComVariant empty;
        long hwnd;
        CComPtr<IDispatch> disp;
        do
        {
            windows->FindWindowSW(&v,&empty,SWC_BROWSER,&hwnd,SWFO_NEEDDISPATCH,&disp);
            if (disp)
                break;

            // we sleep for a while but using events would be better
            // see https://stackoverflow.com/a/59974072/403671
            Sleep(500);
        } while (true);

        // get IFolderView
        CComPtr<IFolderView> view;
        ATLASSERT(SUCCEEDED(IUnknown_QueryService(disp,IID_IFolderView,IID_PPV_ARGS(&view))));

        // change view mode
        ATLASSERT(SUCCEEDED(view->SetCurrentViewMode(FOLDERVIEWMODE::FVM_THUMBNAIL)));
    }

    CoUninitialize();
    return 0;
}
,

不,EXPLORER.EXE 没有这方面的参数 - 它 neither had one in Windows 7,nor does it have one in Windows 10。无论如何,可用的参数很少。

您最好的选择是通过 CreateProcessW()obtain the handle of its main thread and finally find the new window 启动 Explorer。然后您可以操作单个控件,例如文件列表。请参阅基于 this answerAutoIt: Automating Windows Explorer - 它基本上使用 IShellBrowser 和(除了 Windows XPIFolderView2.SetViewModeAndIconSize() 然后应用 FVM_THUMBNAIL

,

这是 approach 给出的 Simon Mourier 的 Delphi 版本:

uses
  ComObj,ShellAPI,ShlObj,ActiveX,SHDocVw,ShLwApi;

function IUnknown_QueryService(punk: IUnknown; const guidService: TGUID;
  const IID: TGUID; out Obj): HRESULT; stdcall; external 'ShLwApi'
  name 'IUnknown_QueryService';

type
  TFolderViewMode = (fvmAuto,fvmIcon,fvmSmallIcon,fvmList,fvmDetails,fvmThumbnail,fvmTile,fvmThumbstrip,fvmContent);

procedure OpenFolder(AHandle: HWND; const AFolder: string; AViewMode: TFolderViewMode);
const
  FolderViewModes: array[TFolderViewMode] of Cardinal =
    (Cardinal(FVM_AUTO),FVM_ICON,FVM_SMALLICON,FVM_LIST,FVM_DETAILS,FVM_THUMBNAIL,FVM_TILE,FVM_THUMBSTRIP,FVM_CONTENT);
var
  ShellItem: IShellItem;
  PIDL: PItemIDList;
  SEInfo: TShellExecuteInfo;
  ILSize: Cardinal;
  SafeArray: PSafeArray;
  v: OleVariant;
  ShellWindows: IShellWindows;
  ExplorerHWND: Integer;
  disp: IDispatch;
  view: IFolderView;
  dummy: OleVariant;
begin

  OleCheck(CoInitialize(nil));
  try

    OleCheck(SHCreateItemFromParsingName(PChar(AFolder),nil,IShellItem,ShellItem));
    try

      OleCheck((ShellItem as IPersistIDList).GetIDList(PIDL));
      try

        ZeroMemory(@SEInfo,SizeOf(SEInfo));
        SEInfo.cbSize := SizeOf(SEInfo);
        SEInfo.Wnd := AHandle;
        SEInfo.fMask := SEE_MASK_IDLIST;
        SEInfo.nShow := SW_SHOW;
        SEInfo.lpIDList := PIDL;
        Win32Check(ShellExecuteEx(@SEInfo));

        ILSize := ILGetSize(PIDL);
        SafeArray := SafeArrayCreateVector(VT_UI1,ILSize);

        CopyMemory(SafeArray.pvData,PIDL,ILSize);
        PVariantArg(@v).vt := VT_ARRAY or VT_UI1;
        PVariantArg(@v).parray := SafeArray;

        OleCheck(CoCreateInstance(CLASS_ShellWindows,CLSCTX_LOCAL_SERVER,IShellWindows,ShellWindows));
        try
          dummy := Unassigned;
          var c: Integer := 0;
          repeat
            if c > 0 then
              Sleep(200);
            disp := ShellWindows.FindWindowSW(v,dummy,ExplorerHWND,SWFO_NEEDDISPATCH);
            Inc(c);
          until Assigned(disp) or (c > 15);
          if disp = nil then
            Exit;
          OleCheck(IUnknown_QueryService(disp,IFolderView,view));
          try
            OleCheck(view.SetCurrentViewMode(FolderViewModes[AViewMode]));
          finally
            view := nil;
          end;
        finally
          ShellWindows := nil;
        end;

      finally
        CoTaskMemFree(PIDL);
      end;

    finally
      ShellItem := nil;
    end;

  finally
    CoUninitialize;
  end;

end;

我没有无限期地对窗口进行睡眠轮询(并可能杀死应用程序!),而是在 3 秒后放弃。

示例用法:

procedure TForm1.Button1Click(Sender: TObject);
begin
  OpenFolder(Handle,'C:\Users\Andreas Rejbrand\Skrivbord\Test',fvmThumbnail);
end;

视图模式,

type
  TFolderViewMode = (fvmAuto,fvmContent);

直接映射到 Windows 的 FOLDERVIEWMODEs。请注意,您的 Windows 版本可能不支持所有这些。