• windows IconOverlay shell extension


    毕业设计需要实现类似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就老老实实地呆在左下角了。

  • 相关阅读:
    【转】Linq Group by
    SmtpClient发邮件时为什么用MailMessage.From而不用MailMessage.Sender
    HttpClient异步调用WEB服务
    WPF MVVM框架下,VM界面写控件
    WPF 后台读取样式文件
    vs2017 乱码
    ClickOnce 和管理员权限
    EF code first Mysql 更换主键类型
    Linq Group By
    Mvc api HelpPage 与注释
  • 原文地址:https://www.cnblogs.com/un4sure/p/2809068.html
Copyright © 2020-2023  润新知