毕业设计需要实现类似Dropbox样式的文件夹及文件的效果,即如果文件已经同步,则需要在相应的文件图标上添加一个标志。

经过一番百度google发现这个效果的实现是通过windows shell extension实现的,其中涉及的技术主要是COM与VC++。

由于各种原因,不愿意用VC++来实现,于是选择了用较新的C#来实现。

IconOverlay效果的实现原理比较简单,如果有COM的基础的话,用visual studio 中vc++ 的 ATL是很容易实现的。

下面的两个连接可以提供很多参考

http://msdn.microsoft.com/en-us/library/windows/desktop/bb761265(v=vs.85).aspx

http://www.codeproject.com/Articles/7484/How-to-overlay-an-icon-over-existing-shell-objects

MSDN中简单讲述了如何实现,codeproject中的文章则基本上是手把手地讲了实现的细节。

不过想要移植到C#中,不是那么简单。

最大的问题是COM与C#托管代码的互操作性,COM接口是基于C/C++的,其中的数据类型和C#有所不同,要解决两种语言之间数据类型的Marshal。

COM与C#互操作原理的讲述可以从MSDN上找到,下面是两个或许有用的链接,话说我找了挺久的~~

http://msdn.microsoft.com/zh-cn/library/aa686045.aspx

http://msdn.microsoft.com/zh-cn/magazine/cc164193.aspx

--------------------------------------------------------我是分割线------------------------------------------------------------------------------

以上是一些资料,以下是晒一晒我的实现过程

实现过程分为三部曲:

  1. 声明C#版本的 IShellIconOverlayIdentifier 
    1.         [ComVisible(false)]
              [ComImport]
              [Guid("0C6C4200-C589-11D0-999A-00C04FD655E1")]
              [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
              public interface IShellIconOverlayIdentifier
              {
                  [PreserveSig]
                  int IsMemberOf([MarshalAs(UnmanagedType.LPWStr)] string path, uint attributes);
      
                  [PreserveSig]
                  int GetOverlayInfo(IntPtr iconFileBuffer, int iconFileBufferSize, out int iconIndex, out uint flags);
      
                  [PreserveSig]
                  int GetPriority(out int priority);
              }    

      其中需要注意的是这里的Guid一定要和Com中的IShellIconOverlayIdentifier的GUID保持一致,因为这里只是声明了IShellIconOverlayIdentifier的C#版本,并非定义一个新的接口

        
  2. 实现上述接口
    1.         [ComVisible(true)]
              [Guid("95B6DB50-D997-4E2C-9E57-17447992F8B1")]
              publicclass HHIconOverlayA : IShellIconOverlayIdentifier
              {
                  privateconststring GUID = "{95B6DB50-D997-4E2C-9E57-17447992F8B1}";
      
                  #region IShellIconOverlayIdentifier Members
      
                  publicint IsMemberOf(string path, uint attributes)
                  {
                      if(true) //test condition, here show everything with icon overlay
                      {
                          return 0; //S_OK                
                      }
                      return 1; // S_FALSE            
                  }
      
                  publicint GetOverlayInfo(IntPtr iconFileBuffer, int
                  iconFileBufferSize, outint iconIndex, outuint flags)
                  {
                      string icnFile = @"C:\overlay.ico";
                      byte[] bytes = Encoding.Unicode.GetBytes(icnFile);
                      if (bytes.Length + 2 < iconFileBufferSize)
                      {
                          for (int i = 0; i < bytes.Length; i++)
                          {
                              Marshal.WriteByte(iconFileBuffer, i, bytes[i]);
                          }
                          //write the "\0\0"
                          Marshal.WriteByte(iconFileBuffer, bytes.Length, 0);
                          Marshal.WriteByte(iconFileBuffer, bytes.Length + 1, 0);
                      }
                      iconIndex = 0;
                      flags = 1; // ISIOI_ICONINDEX 2 | ISIOI_ICONFILE 1
                      return0; // S_OK                 
                  }
      
                  publicint GetPriority(outint priority)
                  {
                      priority = 0; // 0-100 (0 is highest priority)return0; // S_OK            
                  }
      
                  #endregion

      我在GetOverlayInfo这个函数这里遇到了巨大的困难,第一个参数在Com接口里的类型是PWSTR,也就是wchar_t *类型,根据前述资料里面的讲解,它既是输入参数也是输出参数,我刚开始的时候用的是[In, Out, MarshalAs(UnmanagedType.LPWSTR)]String,然后发现效果不对,我又换成StringBuilder,最后的效果还是不对。

  3. 实现之后需要注册我们的服务,以使得Explorer能够识别并使用我们的服务。
    1.   
                 #region Registry
      
                  [System.Runtime.InteropServices.ComRegisterFunctionAttribute()]
                  static void RegisterServer(String str1)
                  {
                      RegistryKey rk =
                      Registry.LocalMachine.CreateSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\_HHIconOverlayA");
                      rk.SetValue(string.Empty, GUID);
                      rk.Close();
                  }
                  [System.Runtime.InteropServices.ComUnregisterFunctionAttribute()]
                  static void UnregisterServer(String str1)
                  {
                      Registry.LocalMachine.DeleteSubKeyTree(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\_HHIconOverlayA");
      
                  }
      
                  #endregion    

      以上是类中的用来注册的代码

    2. 在项目属性中,选择签名,以产生程序集的强名;然后生成解决方案,会在bin目录的debug或release目录下产生xxx.dll文件
    3. 注册服务: regasm xxx.dll /codebase
    4. 重启Explorer,即可看到效果

 GetOverlayInfo只能够设置那个图标,不能通过参数控制其大小,网上某些资料表明windows默认使用32x32大小的icon,所以,要注意自己的图标的设计。

比如要想自己的Overlay图标显示在左下角,可以做一个大小为32x32的图标,将自己要显示的部分放在左下角的16x16的地方,背景设置为透明,这样Overlay就老老实实地呆在左下角了。

posted on 2012-12-08 17:02  shosciation  阅读(1220)  评论(10编辑  收藏  举报