IPreviewHandler
?
背景
Windows允许开发人员为其自定义文件类型创建预览处理程序:
Preview handlers are called when an item is selected to show a lightweight,rich,read-only preview of the file’s contents in the view’s reading pane. This is done without launching the file’s associated application.
A preview handler is a hosted application. Hosts include the Windows Explorer in Windows Vista or Microsoft Outlook 2007.
我想利用现有的IPreviewHandler基础设施来获取文件的缩略图.
在流中
问题是我的文件没有放在shell命名空间中(即它们不在硬盘上).它们坐在记忆中,可通过IStream访问.这意味着我无法使用传统的IExtractImage接口;因为它不支持从Stream加载文件.
幸运的是,这就是现代IPreviewHandler支持(推荐和更喜欢)从Stream加载数据的原因,并建议不要从文件加载预览:
This method is preferred to 07001 due to its ability to use streams that are not accessible through a Win32 path,such as the contents of a compressed file with a .zip file name extension.
那我怎么得到它?
没有关于获取与特定扩展名相关联的IPreviewHandler的正确方法的文档.但是,如果我采取如何注册IPreviewHandler的指示,and read the contract from the other side:
HKEY_CLASSES_ROOT .xyz (Default) = xyzfile HKEY_CLASSES_ROOT xyzfile shellex {8895b1c6-b41f-4c1c-a562-0d564250836f} //IPreviewHandler subkey (Default) = [clsid of the IPreviewHandler]
鉴于我知道扩展,我应该能够遵循相同的路线.让我们用一个真实世界的例子,一个.jpg文件:
请注意,该文件具有预览.请注意,我添加了第二个屏幕截图,以强化预览不是来自硬盘驱动器上的文件的想法.
让我们得到spellunking!
首先是它是一个.jpg文件:
HKEY_CLASSES_ROOT .jpg (Default) = ACDC_JPG HKEY_CLASSES_ROOT ACDC_JPG ShellEx {BB2E617C-0920-11d1-9A0B-00C04FC2D6C1} ContextMenuHandlers
等等,预览处理程序没有{8895b1c6-b41f-4c1c-a562-0d564250836f}子项.这必然意味着我们无法获取.jpg文件的缩略图.
还原荒谬
真正的问题
细心的读者会意识到我要问的实际问题是:
How do i get the preview of an image contained only in a stream?
虽然这是一个有用的问题,而且我遇到的真正问题是,如何使用IPreviewHandler获得答案也是一个有用的问题.
所以随时回答;或两者!
奖金阅读
> MSDN:Preview Handlers and Shell Preview Host
> MSDN:How to Register a Preview Handler
> MSDN:IInitializeWithStream::Initialize method
> IPreviewHandler throws uncatchable exception
> Outlook IPreviewHandler for Delphi
文件类型具有ShellEx密钥,带有{guid}子密钥.每个{guid}键代表一个特定的InterfaceID.
有许多标准shell接口可以与文件类型相关联:
> {BB2E617C-0920-11d1-9A0B-00C04FC2D6C1} IExtractImage
> {953BB1EE-93B4-11d1-98A3-00C04FB687DA} IExtractImage2
> {e357fccd-a995-4576-b01f-234630154e96} IThumbnailProvider
> {8895b1c6-b41f-4c1c-a562-0d564250836f} IPreviewHandler
如果我想找到,例如,与.jpg文件关联的IPreviewHandler的clsid,我会查看:
HKEY_CLASSES_ROOT/.jpg/ShellEx/{8895b1c6-b41f-4c1c-a562-0d564250836f} (default) = [clsid]
但那不是我唯一能看的地方.我也可以看看:
HKEY_CLASSES_ROOT/.jpg (default) = jpgfile HKEY_CLASSES_ROOT/jpgfile/ShellEx/{8895b1c6-b41f-4c1c-a562-0d564250836f} (default) = [clsid]
但那不是我唯一能看的地方.我也可以看看:
HKEY_CLASSES_ROOT/SystemFileAssociations/.jpg/ShellEx/{8895b1c6-b41f-4c1c-a562-0d564250836f} (default) = [clsid]
但那不是我唯一能看的地方.我也可以看看:
HKEY_CLASSES_ROOT/SystemFileAssociations/jpegfile/ShellEx/{8895b1c6-b41f-4c1c-a562-0d564250836f} (default) = [clsid]
但那不是我唯一能看的地方.如果我认为该文件是图像,我也可以查看:
HKEY_CLASSES_ROOT/SystemFileAssociations/image/ShellEx/{8895b1c6-b41f-4c1c-a562-0d564250836f} (default) = [clsid]
我是如何找到这些位置的?我是否只遵循记录和支持的位置?不,我在探索资源管理器时使用了Process Monitor,因为它正在寻找一个IThumbnailProvider.
不要使用无证件的拼写
所以现在我想自己使用标准的shell接口作为文件类型.这意味着我必须抓取位置.但是,为什么要以无证件,不受支持的方式抓取这些位置.为什么会在the thing以上从the guy发起愤怒?使用AssocQueryString:
Guid GetShellClsidForFileType(String fileExtension,Guid interfaceID) { //E.g.: // String fileExtension = ".jpg" // Guid interfaceID = "{8895b1c6-b41f-4c1c-a562-0d564250836f}"; //IExtractImage //The interface we're after - in string form String szInterfaceID := GuidToString(interfaceID); //Buffer to receive the clsid string DWORD bufferSize := 1024; //more than enough to hold a 38-character clsid String buffer; SetLength(buffer,bufferSize); HRESULT hr := AssocQueryString( ASSOCF_INIT_DEFAULTTOSTAR,ASSOCSTR_SHELLEXTENSION,//for finding shell extensions fileExtension,//e.g. ".txt" szInterfaceID,//e.g. "{8895b1c6-b41f-4c1c-a562-0d564250836f}" buffer,//will receive the clsid string @bufferSize); if (hr <> S_OK) return Guid.Empty; Guid clsid; HRESULT hr = CLSIDFromString(buffer,out clsid); if (hr <> NOERROR) return Guid.Empty; return clsid; }
所以要获得.xps文件的IPreviewHandler的clsid:
Guid clsid = GetShellClsidForFileType(".xps",IPreviewHandler);
如何获得IPreviewHandler文件扩展名?
综上所述,我们现在可以回答这个问题:
IPreviewHandler GetPreviewHandlerForFileType(String extension) { //Extension: the file type to return IPreviewHandler for (e.g. ".xps") Guid previewHandlerClassID = GetShellClsidForFileType(extension,IPreviewHandler); //Create the COM object IUnknown unk = CreateComObject(previewHandlerClassID); //Return the actual IPreviewHanler interface (not IUnknown) return (IPreviewhandler)unk; }