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就老老實實地呆在左下角了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM