三. 如何得到Preview Handler的GUID
首先说明一下,IPreviewHandler接口的GUID是8895b1c6-b41f-4c1c-a562-0d564250836f,要找支持的Preview Handler的GUID就要用到它。如何去找呢,在注册表中有四个地方,我这个程序里是四个,不知道还没有别的方式。
第一种方式以.docx后缀为例,注册表路径是:

其中{8895b1c6-b41f-4c1c-a562-0d564250836f}是IPreviewHandler接口的GUID(下同)。
第二种方式以.html后缀为例,注册表路径是:先从HKEY_CLASSES_ROOT\.html得到其默认值htmlfile.再到HKEY_CLASSES_ROOT\htmlfile\ShellEx\{8895b1c6-b41f-4c1c-a562-0d564250836f}
第三种方式还是以.html后缀为例,注册表路径是HKEY_CLASSES_ROOT\SystemFileAssociations\.html\ShellEx\{8895b1c6-b41f-4c1c-a562-0d564250836f}。
第四种方式以.txt后缀为例:先从HKEY_CLASSES_ROOT\.txt 得到PerceivedType名字的值text,再到这个路径得到GUID。HKEY_CLASSES_ROOT\SystemFileAssociations\text\ShellEx\{8895b1c6-b41f-4c1c-a562-0d564250836f}。
以下代码都在PreviewsManager.cs里面
C#实现读注册表
// The GUID of IPreviewHandler
string CLSID = "8895b1c6-b41f-4c1c-a562-0d564250836f";
Guid g = new Guid(CLSID);
// This method returns the GUID of supported preview handler associated strExtension
public string GetCLSIDFromExtensionShellEx(Guid previewHandlerGuid, string strExtension)
{
String strCLSID = String.Empty;
using (RegistryKey hkPreviewHandler = Registry.ClassesRoot.OpenSubKey(
String.Format(@"{0} \ShellEx\{0:B}", strExtension, previewHandlerGuid)))
{
if (hkPreviewHandler != null)
{
strCLSID = hkPreviewHandler.GetValue(String.Empty).ToString();
}
}
return strCLSID;
}
}
用法如下:
String strGUID =
注意:返回的字符串可能是null或String.Empty,所以要对其进行判断
根据GUID创建COM实例
如果找到了GUID,那么就要用这个GUID来创建一个COM实例,相当于C++中的CoCreateInstance函数,如何实现呢?
// Create a COM instance, it is like CoCreateInstance in C++
Guid newCLSID = new Guid(strCLSID);
Get the type of the COM interface specified by CLSID(Guid)
Type typeofPreveiwHandler = Type.GetTypeFromCLSID(newCLSID, true);
Object obj = Activator.CreateInstance(typeofPreveiwHandler);
COM实例我也创建好了,我应该怎么用这个实例来实现文件预览功能,不要着急,让我们分析一下。前面说过,IPreviewHandler是MS提供的一个接口,这个接口里面提供了一些方法,能实现文件预览,那么这些COM实例肯定是实现了IPreviewHandler接口,所以,就应该把刚才创建的COM实例转换成IPreviewHandler对象,用IPreviewHandler对象去调用它的函数,从而实现文件预览功能。
没错,就是这样的,但有一个问题,这些COM实例并没有打开过文件,它是怎么知道文件内容是什么? 问得好,在把COM实例转换成IPreviewHandler接口之前,要对Preview Handler进行初始化,这里就要用到两另外的两个接口IInitializeWithFile和IInitializeWithStream,这两个接口的相关信息请参见MSDN,这里不介绍。其实绝大部分Preview
Handler都用IInitializeWithFile这个接口进行初始化。部分代码如下:
// Convert COM instance to IInitializeWithFile or IInitializeWithStream to initialize.
IInitializeWithFile fileInit = obj as IInitializeWithFile;
IInitializeWithStream streamInit = obj as IInitializeWithStream;
bool bSuccess = false;
bool isInitalized = false;
if (fileInit != null)
{
fileInit.Initialize(fileName, 0);
isInitalized = true;
}
else if(streamInit != null)
{
// Open file and read data to stream
FileStream stream = new FileStream(fileName, FileMode.Open,
FileAccess.Read, FileShare.ReadWrite);
COMStream stream = new COMStream(stream);
streamInit.Initialize(stream, 0);
isInitalized = true;
}
if(isInitalized)
{
// Convert obj to IPreviewHandler object
IPreviewHandler pHandler = obj as IPreviewHandler;
if(pHandler != null)
{
// TODO: Begin to get preview
RECT rect = new RECT(viewRect);
pHandler.SetWindow(handler, ref rect);
pHandler.SetRect(ref rect);
pHandler.DoPreview();
pHandler.Unload();
Marshal.ReleaseComObject(pHandler);
}
} 以上就是得到Preview的部分代码,需要进行说明的,我已经将其标记为红色。下面对其进行一些解释(顺序从上到下):
1,obj:这个对象就是COM实例,它会在后面用到。
2,COMStream:这是一个自己写的类,它继承了IStream,IDispose接口,它实现了IStream,IDispose接口的方法。具体的我会在后面文章中说明。
3,RECT rect = new RECT(viewRect); pHandler.SetRect(ref rect);这两句话就告诉IPreviewHander,你想把取得的Preview放在多大的位置上面。
4,pHandler.SetWindow(handler, ref rect); 这句话就告诉IPreviewHander,你想把取得的Preview放在哪个窗体上面,窗体由handler指定,在C#中是IntPtr类型,C++中是HANDLE。这句话一定要有,不然Preview就是取到了,也显示不出来。
5,pHandler.DoPreview(); 只要程序调用这一句,那么它就开始取Preivew了。
6,pHandler.Unload():取完Preview后,不要忘记释放IPreviewHandler对象,不然会有内存泄漏。直接调用IPreviewHandler接口的Unload()函数。
7,Marshal.ReleaseComObject(pHandler); 释放COM对象
小结:
未完待续,敬看下文
本文详细介绍了如何通过四种不同的注册表路径找到支持的文件预览GUID,并使用C#代码实现创建COM实例,初始化并调用IPreviewHandler接口完成文件预览。
&spm=1001.2101.3001.5002&articleId=8607560&d=1&t=3&u=b259b4049d3842dfb51a7a4ee88a24db)
3080

被折叠的 条评论
为什么被折叠?



