diff --git a/App.config b/App.config index 0da5351..cf3eea2 100644 --- a/App.config +++ b/App.config @@ -1,23 +1,35 @@ - + - + - - + + + + + + + + + + + + + + - + - - + + - - + + diff --git a/Constant/MainWindowEnum.cs b/Constant/MainWindowEnum.cs index fb22692..8c79e11 100644 --- a/Constant/MainWindowEnum.cs +++ b/Constant/MainWindowEnum.cs @@ -8,7 +8,9 @@ namespace GeekDesk.Constant WINDOW_WIDTH = 666, //默认窗体宽度 WINDOW_HEIGHT = 500, //默认窗体高度 MENU_CARD_WIDHT = 165, //默认菜单栏宽度 - IMAGE_WIDTH = 60, //默认图标宽度 - IMAGE_HEIGHT = 60, //默认图标高度 + IMAGE_WIDTH = 50, //默认图标宽度 + IMAGE_HEIGHT = 50, //默认图标高度 + IMAGE_WIDTH_AM = 60, //动画变换宽度 + IMAGE_HEIGHT_AM = 60 //动画变换高度 } } diff --git a/Control/UserControls/Config/OtherControl.xaml b/Control/UserControls/Config/OtherControl.xaml index 55c80b1..2fd767d 100644 --- a/Control/UserControls/Config/OtherControl.xaml +++ b/Control/UserControls/Config/OtherControl.xaml @@ -17,7 +17,7 @@ - + @@ -26,8 +26,21 @@ - - + + + + + + + + + + + - + @@ -84,7 +84,7 @@ SelectionChanged="menus_SelectionChanged" > - + @@ -94,7 +94,7 @@ @@ -110,9 +110,6 @@ - - - - + @@ -47,7 +35,6 @@ @@ -58,55 +45,62 @@ - - + - + - + - - + + Margin="0,5,0,0" + MaxHeight="40" + TextWrapping="Wrap" + TextTrimming="WordEllipsis" + TextAlignment="Center" + VerticalAlignment="Center" + Text="{Binding Name}"/> diff --git a/Control/UserControls/PannelCard/RightCardControl.xaml.cs b/Control/UserControls/PannelCard/RightCardControl.xaml.cs index 3513d31..a0ffde2 100644 --- a/Control/UserControls/PannelCard/RightCardControl.xaml.cs +++ b/Control/UserControls/PannelCard/RightCardControl.xaml.cs @@ -18,6 +18,7 @@ using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; +using System.Windows.Media.Animation; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; @@ -154,15 +155,6 @@ namespace GeekDesk.Control.UserControls.PannelCard } - /// - /// data选中事件 设置不可选中 - /// - /// - /// - private void IconSelectionChanged(object sender, SelectionChangedEventArgs e) - { - if (icons.SelectedIndex != -1) icons.SelectedIndex = -1; - } private void Wrap_Drop(object sender, DragEventArgs e) @@ -175,6 +167,17 @@ namespace GeekDesk.Control.UserControls.PannelCard //string base64 = ImageUtil.FileImageToBase64(path, ImageFormat.Jpeg); + string ext = System.IO.Path.GetExtension(path).ToLower(); + + if (".lnk".Equals(ext)) + { + string targetPath = FileUtil.GetTargetPathByLnk(path); + if (targetPath!=null) + { + path = targetPath; + } + } + IconInfo iconInfo = new IconInfo { Path = path, @@ -182,6 +185,10 @@ namespace GeekDesk.Control.UserControls.PannelCard }; iconInfo.DefaultImage = iconInfo.ImageByteArr; iconInfo.Name = System.IO.Path.GetFileNameWithoutExtension(path); + if (StringUtil.IsEmpty(iconInfo.Name)) + { + iconInfo.Name = path; + } MainWindow.appData.MenuList[appData.AppConfig.SelectedMenuIndex].IconList.Add(iconInfo); } } @@ -196,13 +203,16 @@ namespace GeekDesk.Control.UserControls.PannelCard appData.MenuList[appData.AppConfig.SelectedMenuIndex].IconList.Remove((IconInfo)((MenuItem)sender).Tag); } - private void MenuItem_Click(object sender, RoutedEventArgs e) + private void SystemContextMenu(object sender, RoutedEventArgs e) { - IconInfo icon = (IconInfo)((MenuItem)sender).Tag; - System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo("Explorer.exe"); - psi.Arguments = "/e,/select," + icon.Path; - System.Diagnostics.Process.Start(psi); + DirectoryInfo[] folders = new DirectoryInfo[1]; + folders[0] = new DirectoryInfo(icon.Path); + ShellContextMenu scm = new ShellContextMenu(); + System.Drawing.Point p = System.Windows.Forms.Cursor.Position; + p.X -= 80; + p.Y -= 80; + scm.ShowContextMenu(folders, p); } /// @@ -214,5 +224,41 @@ namespace GeekDesk.Control.UserControls.PannelCard { HandyControl.Controls.Dialog.Show(new IconInfoDialog((IconInfo)((MenuItem)sender).Tag)); } + + private void StackPanel_MouseEnter(object sender, MouseEventArgs e) + { + ImgStroyBoard(sender, (int)MainWindowEnum.IMAGE_HEIGHT_AM, (int)MainWindowEnum.IMAGE_WIDTH_AM, 1); + } + + private void StackPanel_MouseLeave(object sender, MouseEventArgs e) + { + ImgStroyBoard(sender, (int)MainWindowEnum.IMAGE_HEIGHT, (int)MainWindowEnum.IMAGE_WIDTH, 500); + } + + + private void ImgStroyBoard(object sender, int height, int width, int milliseconds) + { + + if (appData.AppConfig.PMModel) return; + + StackPanel sp = sender as StackPanel; + + Image img = sp.Children[0] as Image; + + DoubleAnimation heightAnimation = new DoubleAnimation(); + DoubleAnimation widthAnimation = new DoubleAnimation(); + + heightAnimation.From = img.Height; + widthAnimation.From = img.Width; + + heightAnimation.To = height; + widthAnimation.To = width; + + heightAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(milliseconds)); + widthAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(milliseconds)); + + img.BeginAnimation(HeightProperty, heightAnimation); + img.BeginAnimation(WidthProperty, widthAnimation); + } } } diff --git a/Control/Windows/ToDoInfoWindow.xaml.cs b/Control/Windows/ToDoInfoWindow.xaml.cs index a9fb9f3..e43a70e 100644 --- a/Control/Windows/ToDoInfoWindow.xaml.cs +++ b/Control/Windows/ToDoInfoWindow.xaml.cs @@ -80,7 +80,7 @@ namespace GeekDesk.Control.Windows private void Save_Button_Click(object sender, RoutedEventArgs e) { - + DateTime dt; if (Title.Text.Trim() == "" || ExeTime.Text.Trim() == "") { Growl.Warning("任务标题 和 待办时间不能为空!"); @@ -89,7 +89,7 @@ namespace GeekDesk.Control.Windows { try { - Convert.ToDateTime(ExeTime.Text); + dt = Convert.ToDateTime(ExeTime.Text); } catch (Exception) { Growl.Warning("请输入正确的时间!"); @@ -107,14 +107,32 @@ namespace GeekDesk.Control.Windows appData.ToDoList.Add(info); } else { - int index =appData.ToDoList.IndexOf(info); - appData.ToDoList.Remove(info); + appData.HiToDoList.Remove(info); info.Title = Title.Text; info.Msg = Msg.Text; info.ExeTime = ExeTime.Text; - info.DoneTime = DoneTime.Text; - appData.ToDoList.Insert(index, info); + info.DoneTime = null; + appData.ToDoList.Add(info); } + + DateTime dtNow = DateTime.Now; + TimeSpan ts = dt.Subtract(dtNow); + int minutes = (int)Math.Ceiling(ts.TotalMinutes); + if (minutes < 0) + { + minutes = 0; + } + if (minutes > 60) + { + int m = minutes % 60; + int h = minutes / 60; + Growl.SuccessGlobal("设置待办任务成功, 系统将在 " + h + " 小时零 " + m + " 分钟后提醒您!"); + + } else + { + Growl.SuccessGlobal("设置待办任务成功, 系统将在 " + minutes + " 分钟后提醒您!"); + } + CommonCode.SaveAppData(MainWindow.appData); this.Close(); } diff --git a/GeekDesk.csproj b/GeekDesk.csproj index 5c40dfc..f9835a2 100644 --- a/GeekDesk.csproj +++ b/GeekDesk.csproj @@ -66,8 +66,8 @@ - - packages\System.Drawing.Common.6.0.0-preview.3.21201.4\lib\net461\System.Drawing.Common.dll + + packages\System.Drawing.Common.6.0.0-preview.6.21352.12\lib\net461\System.Drawing.Common.dll @@ -161,6 +161,7 @@ + @@ -169,9 +170,9 @@ + - @@ -312,5 +313,16 @@ + + + {F935DC20-1CF0-11D0-ADB9-00C04FD58A0B} + 1 + 0 + 0 + tlbimp + False + True + + \ No newline at end of file diff --git a/Thread/UpdateThread.cs b/Thread/UpdateThread.cs index dc6f972..c0da24b 100644 --- a/Thread/UpdateThread.cs +++ b/Thread/UpdateThread.cs @@ -57,7 +57,8 @@ namespace GeekDesk.Thread } } catch (Exception e) { - MessageBox.Show(e.Message); + //不做处理 + //MessageBox.Show(e.Message); } } } diff --git a/Util/DefaultIcons.cs b/Util/DefaultIcons.cs new file mode 100644 index 0000000..8cb00dd --- /dev/null +++ b/Util/DefaultIcons.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace GeekDesk.Util +{ + public static class DefaultIcons + { + private static Icon folderIcon; + + public static Icon FolderLarge => folderIcon ?? (folderIcon = GetStockIcon(SHGSI_ICON, SHGSI_LARGEICON)); + + public static Icon GetStockIcon(uint type, uint size) + { + var info = new SHSTOCKICONINFO(); + info.cbSize = (uint)Marshal.SizeOf(info); + + SHGetStockIconInfo(type, SHGSI_ICON | size, ref info); + + var icon = (Icon)Icon.FromHandle(info.hIcon).Clone(); // Get a copy that doesn't use the original handle + DestroyIcon(info.hIcon); // Clean up native icon to prevent resource leak + + return icon; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct SHSTOCKICONINFO + { + public uint cbSize; + public IntPtr hIcon; + public int iSysIconIndex; + public int iIcon; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + public string szPath; + } + + [DllImport("shell32.dll")] + public static extern int SHGetStockIconInfo(uint siid, uint uFlags, ref SHSTOCKICONINFO psii); + + [DllImport("user32.dll")] + public static extern bool DestroyIcon(IntPtr handle); + + public static uint SHSIID_FOLDER = 0x3; + public static uint SHGSI_ICON = 0x100; + public static uint SHGSI_LARGEICON = 0x0; + public static uint SHGSI_SMALLICON = 0x1; + } +} diff --git a/Util/FileIcon.cs b/Util/FileIcon.cs index c85d515..ec0abd0 100644 --- a/Util/FileIcon.cs +++ b/Util/FileIcon.cs @@ -1,4 +1,6 @@ -using System; +using IWshRuntimeLibrary; +using System; +using System.Collections.Generic; using System.Drawing; using System.IO; using System.Runtime.InteropServices; @@ -6,20 +8,61 @@ using System.Windows.Media.Imaging; namespace GeekDesk.Util { - class FileIcon + public class FileIcon { - - public static Icon GetIcon(string filePath) + private static List GetBlurExts() { - IntPtr hIcon = GetJumboIcon(GetIconIndex(filePath)); - Icon ico = Icon.FromHandle(hIcon); - return ico; + List list = new List(); + list.Add(".exe"); + list.Add(".cer"); + list.Add(".lnk"); + return list; } + [DllImport("User32.dll")] + public static extern int PrivateExtractIcons( + string lpszFile, //文件名可以是exe,dll,ico,cur,ani,bmp + int nIconIndex, //从第几个图标开始获取 + int cxIcon, //获取图标的尺寸x + int cyIcon, //获取图标的尺寸y + IntPtr[] phicon, //获取到的图标指针数组 + int[] piconid, //图标对应的资源编号 + int nIcons, //指定获取的图标数量,仅当文件类型为.exe 和 .dll时候可用 + int flags //标志,默认0就可以,具体可以看LoadImage函数 + ); + + public static BitmapImage GetBitmapImage(string filePath) { - Icon ico = GetIcon(filePath); + Icon ico; + //选中文件中的图标总数 + var iconTotalCount = PrivateExtractIcons(filePath, 0, 0, 0, null, null, 0, 0); + //用于接收获取到的图标指针 + IntPtr[] hIcons = new IntPtr[iconTotalCount]; + //对应的图标id + int[] ids = new int[iconTotalCount]; + //成功获取到的图标个数 + var successCount = PrivateExtractIcons(filePath, 0, 256, 256, hIcons, ids, iconTotalCount, 0); + + string ext = Path.GetExtension(filePath).ToLower(); + + IntPtr ip = IntPtr.Zero; + if (successCount > 0) + { + ip = hIcons[0]; + ico = Icon.FromHandle(ip); + } + else if (GetBlurExts().Contains(ext)) + { + ico = Icon.ExtractAssociatedIcon(filePath); + } + else + { + ip = GetJumboIcon(GetIconIndex(filePath)); + ico = Icon.FromHandle(ip); + } + Bitmap bmp = ico.ToBitmap(); MemoryStream strm = new MemoryStream(); bmp.Save(strm, System.Drawing.Imaging.ImageFormat.Png); @@ -28,10 +71,15 @@ namespace GeekDesk.Util strm.Seek(0, SeekOrigin.Begin); bmpImage.StreamSource = strm; bmpImage.EndInit(); - + if (ip != IntPtr.Zero) + { + Shell32.DestroyIcon(ip); + } return bmpImage.Clone(); } + + public static int GetIconIndex(string pszFile) { SHFILEINFO sfi = new SHFILEINFO(); diff --git a/Util/FileUtil.cs b/Util/FileUtil.cs new file mode 100644 index 0000000..1c53714 --- /dev/null +++ b/Util/FileUtil.cs @@ -0,0 +1,32 @@ +using IWshRuntimeLibrary; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GeekDesk.Util +{ + public class FileUtil + { + + public static string GetTargetPathByLnk(string filePath) + { + try + { + WshShell shell = new WshShell(); + IWshRuntimeLibrary.IWshShortcut shortcut = (IWshRuntimeLibrary.IWshShortcut)shell.CreateShortcut(filePath); + + if (StringUtil.IsEmpty(shortcut.TargetPath)) + { + return null; + } + return shortcut.TargetPath; + } + catch (Exception e) + { + return null; + } + } + } +} diff --git a/Util/IconHelper.cs b/Util/IconHelper.cs new file mode 100644 index 0000000..4803ba4 --- /dev/null +++ b/Util/IconHelper.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace GeekDesk.Util +{ + public class IconHelper + { + [DllImport("Shell32.dll")] + private static extern IntPtr SHGetFileInfo + ( + string pszPath, //һҪȡϢļԻ·Ļ塣ԴļҲָļ·ע[1] + uint dwFileAttributes,//˵uFlagsаSHGFI_USEFILEATTRIBUTES־(һ㲻ʹ)ˣӦļԵϣ浵ֻĿ¼ϵͳȡ + out SHFILEINFO psfi, + uint cbfileInfo,//򵥵ظṹijߴ硣 + SHGFI uFlags//ĺıͨпܵı־ܼԦΪʵʵصõϢ + ); + + + [StructLayout(LayoutKind.Sequential)] + private struct SHFILEINFO + { + public SHFILEINFO(bool b) + { + hIcon = IntPtr.Zero; iIcon = 0; dwAttributes = 0; szDisplayName = ""; szTypeName = ""; + } + public IntPtr hIcon;//ͼ + public int iIcon;//ϵͳͼб + public uint dwAttributes; //ļ + [MarshalAs(UnmanagedType.LPStr, SizeConst = 260)] + public string szDisplayName;//ļ· ļ256ANSI̷X:\3ֽڣ259ֽڣټϽ1ֽڣ260 + [MarshalAs(UnmanagedType.LPStr, SizeConst = 80)] + public string szTypeName;//ļ ̶80ֽ + }; + + + + private enum SHGFI + { + SmallIcon = 0x00000001, + LargeIcon = 0x00000000, + Icon = 0x00000100, + DisplayName = 0x00000200,//Retrieve the display name for the file, which is the name as it appears in Windows Explorer. The name is copied to the szDisplayName member of the structure specified in psfi. The returned display name uses the long file name, if there is one, rather than the 8.3 form of the file name. Note that the display name can be affected by settings such as whether extensions are shown. + Typename = 0x00000400, //Retrieve the string that describes the file's type. The string is copied to the szTypeName member of the structure specified in psfi. + SysIconIndex = 0x00004000, //Retrieve the index of a system image list icon. If successful, the index is copied to the iIcon member of psfi. The return value is a handle to the system image list. Only those images whose indices are successfully copied to iIcon are valid. Attempting to access other images in the system image list will result in undefined behavior. + UseFileAttributes = 0x00000010 //Indicates that the function should not attempt to access the file specified by pszPath. Rather, it should act as if the file specified by pszPath exists with the file attributes passed in dwFileAttributes. This flag cannot be combined with the SHGFI_ATTRIBUTES, SHGFI_EXETYPE, or SHGFI_PIDL flags. + } + + /// + /// ļչõϵͳչͼ + /// + /// ļ(磺win.rar;setup.exe;temp.txt) + /// ͼĴС + /// + public static Icon GetFileIcon(string fileName, bool largeIcon) + { + SHFILEINFO info = new SHFILEINFO(true); + int cbFileInfo = Marshal.SizeOf(info); + SHGFI flags; + if (largeIcon) + flags = SHGFI.Icon | SHGFI.LargeIcon | SHGFI.UseFileAttributes; + else + flags = SHGFI.Icon | SHGFI.SmallIcon | SHGFI.UseFileAttributes; + IntPtr IconIntPtr = SHGetFileInfo(fileName, 256, out info, (uint)cbFileInfo, flags); + if (IconIntPtr.Equals(IntPtr.Zero)) + return null; + return Icon.FromHandle(info.hIcon); + } + + /// + /// ȡļͼ + /// + /// ͼ + public static Icon GetDirectoryIcon(string path, bool largeIcon) + { + SHFILEINFO _SHFILEINFO = new SHFILEINFO(); + int cbFileInfo = Marshal.SizeOf(_SHFILEINFO); + SHGFI flags; + if (largeIcon) + flags = SHGFI.Icon | SHGFI.LargeIcon; + else + flags = SHGFI.Icon | SHGFI.SmallIcon; + + IntPtr IconIntPtr = SHGetFileInfo(path, 256, out _SHFILEINFO, (uint)cbFileInfo, flags); + if (IconIntPtr.Equals(IntPtr.Zero)) + return null; + Icon _Icon = Icon.FromHandle(_SHFILEINFO.hIcon); + return _Icon; + } + + } +} \ No newline at end of file diff --git a/Util/IconUtil.cs b/Util/IconUtil.cs new file mode 100644 index 0000000..d945ae8 --- /dev/null +++ b/Util/IconUtil.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace GeekDesk.Util +{ + public class IconUtil + { + const string IID_IImageList = "46EB5926-582E-4017-9FDF-E8998DAA0950"; + const string IID_IImageList2 = "192B9D83-50FC-457B-90A0-2B82A8B5DAE1"; + + } +} diff --git a/Util/ImageUtil.cs b/Util/ImageUtil.cs index ac4b799..33bcfca 100644 --- a/Util/ImageUtil.cs +++ b/Util/ImageUtil.cs @@ -268,21 +268,13 @@ namespace GeekDesk.Util public static BitmapImage Bitmap2BitmapImage(Bitmap bitmap) { - IntPtr hBitmap = bitmap.GetHbitmap(); - BitmapImage retval; - try - { - retval = (BitmapImage)Imaging.CreateBitmapSourceFromHBitmap( - hBitmap, - IntPtr.Zero, - Int32Rect.Empty, - BitmapSizeOptions.FromEmptyOptions()); - } - finally - { - DeleteObject(hBitmap); - } - return retval; + MemoryStream ms = new MemoryStream(); + bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Png); + BitmapImage bit3 = new BitmapImage(); + bit3.BeginInit(); + bit3.StreamSource = ms; + bit3.EndInit(); + return bit3; } diff --git a/Util/NativeMethods.cs b/Util/NativeMethods.cs new file mode 100644 index 0000000..14fd579 --- /dev/null +++ b/Util/NativeMethods.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace GeekDesk.Util +{ + /// + /// 保存文件信息的结构体 + /// + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + struct SHFILEINFO + { + public IntPtr hIcon; + public int iIcon; + public uint dwAttributes; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + public string szDisplayName; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)] + public string szTypeName; + } + + class NativeMethods + { + + [DllImport("Shell32.dll", EntryPoint = "SHGetFileInfo", SetLastError = true, CharSet = CharSet.Auto)] + public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbFileInfo, uint uFlags); + + [DllImport("User32.dll", EntryPoint = "DestroyIcon")] + public static extern int DestroyIcon(IntPtr hIcon); + + #region API 参数的常量定义 + + public const uint SHGFI_ICON = 0x100; + public const uint SHGFI_LARGEICON = 0x0; //大图标 32×32 + public const uint SHGFI_SMALLICON = 0x1; //小图标 16×16 + public const uint SHGFI_USEFILEATTRIBUTES = 0x10; + + #endregion + + /// + /// 获取文件类型的关联图标 + /// + /// 文件类型的扩展名或文件的绝对路径 + /// 是否返回大图标 + /// 获取到的图标 + static Icon GetIcon(string fileName, bool isLargeIcon) + { + SHFILEINFO shfi = new SHFILEINFO(); + IntPtr hI; + + if (isLargeIcon) + hI = NativeMethods.SHGetFileInfo(fileName, 0, ref shfi, (uint)Marshal.SizeOf(shfi), NativeMethods.SHGFI_ICON | NativeMethods.SHGFI_USEFILEATTRIBUTES | NativeMethods.SHGFI_LARGEICON); + else + hI = NativeMethods.SHGetFileInfo(fileName, 0, ref shfi, (uint)Marshal.SizeOf(shfi), NativeMethods.SHGFI_ICON | NativeMethods.SHGFI_USEFILEATTRIBUTES | NativeMethods.SHGFI_SMALLICON); + + Icon icon = Icon.FromHandle(shfi.hIcon).Clone() as Icon; + + NativeMethods.DestroyIcon(shfi.hIcon); //释放资源 + return icon; + } + + } + + + +} diff --git a/Util/ShellContextMenu.cs b/Util/ShellContextMenu.cs new file mode 100644 index 0000000..6d121b1 --- /dev/null +++ b/Util/ShellContextMenu.cs @@ -0,0 +1,1584 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.InteropServices; +using System.Drawing; +using System.Windows.Forms; +using System.IO; +using System.Security.Permissions; + +namespace GeekDesk.Util +{ + /// + /// "Stand-alone" shell context menu + /// + /// It isn't really debugged but is mostly working. + /// Create an instance and call ShowContextMenu with a list of FileInfo for the files. + /// Limitation is that it only handles files in the same directory but it can be fixed + /// by changing the way files are translated into PIDLs. + /// + /// Based on FileBrowser in C# from CodeProject + /// http://www.codeproject.com/useritems/FileBrowser.asp + /// + /// Hooking class taken from MSDN Magazine Cutting Edge column + /// http://msdn.microsoft.com/msdnmag/issues/02/10/CuttingEdge/ + /// + /// Andreas Johansson + /// afjohansson@hotmail.com + /// http://afjohansson.spaces.live.com + /// + /// + /// ShellContextMenu scm = new ShellContextMenu(); + /// FileInfo[] files = new FileInfo[1]; + /// files[0] = new FileInfo(@"c:\windows\notepad.exe"); + /// scm.ShowContextMenu(this.Handle, files, Cursor.Position); + /// + public class ShellContextMenu : NativeWindow + { + #region Constructor + /// Default constructor + public ShellContextMenu() + { + this.CreateHandle(new CreateParams()); + } + #endregion + + #region Destructor + /// Ensure all resources get released + ~ShellContextMenu() + { + ReleaseAll(); + } + #endregion + + #region GetContextMenuInterfaces() + /// Gets the interfaces to the context menu + /// Parent folder + /// PIDLs + /// true if it got the interfaces, otherwise false + private bool GetContextMenuInterfaces(IShellFolder oParentFolder, IntPtr[] arrPIDLs, out IntPtr ctxMenuPtr) + { + int nResult = oParentFolder.GetUIObjectOf( + IntPtr.Zero, + (uint)arrPIDLs.Length, + arrPIDLs, + ref IID_IContextMenu, + IntPtr.Zero, + out ctxMenuPtr); + + if (S_OK == nResult) + { + _oContextMenu = (IContextMenu)Marshal.GetTypedObjectForIUnknown(ctxMenuPtr, typeof(IContextMenu)); + + return true; + } + else + { + ctxMenuPtr = IntPtr.Zero; + _oContextMenu = null; + return false; + } + } + #endregion + + #region Override + + /// + /// This method receives WindowMessages. It will make the "Open With" and "Send To" work + /// by calling HandleMenuMsg and HandleMenuMsg2. It will also call the OnContextMenuMouseHover + /// method of Browser when hovering over a ContextMenu item. + /// + /// the Message of the Browser's WndProc + /// true if the message has been handled, false otherwise + protected override void WndProc(ref Message m) + { + #region IContextMenu + + if (_oContextMenu != null && + m.Msg == (int)WM.MENUSELECT && + ((int)ShellHelper.HiWord(m.WParam) & (int)MFT.SEPARATOR) == 0 && + ((int)ShellHelper.HiWord(m.WParam) & (int)MFT.POPUP) == 0) + { + string info = string.Empty; + + if (ShellHelper.LoWord(m.WParam) == (int)CMD_CUSTOM.ExpandCollapse) + info = "Expands or collapses the current selected item"; + else + { + info = ""; + } + } + + #endregion + + #region IContextMenu2 + + if (_oContextMenu2 != null && + (m.Msg == (int)WM.INITMENUPOPUP || + m.Msg == (int)WM.MEASUREITEM || + m.Msg == (int)WM.DRAWITEM)) + { + if (_oContextMenu2.HandleMenuMsg( + (uint)m.Msg, m.WParam, m.LParam) == S_OK) + return; + } + + #endregion + + #region IContextMenu3 + + if (_oContextMenu3 != null && + m.Msg == (int)WM.MENUCHAR) + { + if (_oContextMenu3.HandleMenuMsg2( + (uint)m.Msg, m.WParam, m.LParam, IntPtr.Zero) == S_OK) + return; + } + + #endregion + + base.WndProc(ref m); + } + + #endregion + + #region InvokeCommand + private void InvokeCommand(IContextMenu oContextMenu, uint nCmd, string strFolder, Point pointInvoke) + { + CMINVOKECOMMANDINFOEX invoke = new CMINVOKECOMMANDINFOEX(); + invoke.cbSize = cbInvokeCommand; + invoke.lpVerb = (IntPtr)(nCmd - CMD_FIRST); + invoke.lpDirectory = strFolder; + invoke.lpVerbW = (IntPtr)(nCmd - CMD_FIRST); + invoke.lpDirectoryW = strFolder; + invoke.fMask = CMIC.UNICODE | CMIC.PTINVOKE | + ((System.Windows.Forms.Control.ModifierKeys & Keys.Control) != 0 ? CMIC.CONTROL_DOWN : 0) | + ((System.Windows.Forms.Control.ModifierKeys & Keys.Shift) != 0 ? CMIC.SHIFT_DOWN : 0); + invoke.ptInvoke = new POINT(pointInvoke.X, pointInvoke.Y); + invoke.nShow = SW.SHOWNORMAL; + + oContextMenu.InvokeCommand(ref invoke); + } + #endregion + + #region ReleaseAll() + /// + /// Release all allocated interfaces, PIDLs + /// + private void ReleaseAll() + { + if (null != _oContextMenu) + { + Marshal.ReleaseComObject(_oContextMenu); + _oContextMenu = null; + } + if (null != _oContextMenu2) + { + Marshal.ReleaseComObject(_oContextMenu2); + _oContextMenu2 = null; + } + if (null != _oContextMenu3) + { + Marshal.ReleaseComObject(_oContextMenu3); + _oContextMenu3 = null; + } + if (null != _oDesktopFolder) + { + Marshal.ReleaseComObject(_oDesktopFolder); + _oDesktopFolder = null; + } + if (null != _oParentFolder) + { + Marshal.ReleaseComObject(_oParentFolder); + _oParentFolder = null; + } + if (null != _arrPIDLs) + { + FreePIDLs(_arrPIDLs); + _arrPIDLs = null; + } + } + #endregion + + #region GetDesktopFolder() + /// + /// Gets the desktop folder + /// + /// IShellFolder for desktop folder + private IShellFolder GetDesktopFolder() + { + IntPtr pUnkownDesktopFolder = IntPtr.Zero; + + if (null == _oDesktopFolder) + { + // Get desktop IShellFolder + int nResult = SHGetDesktopFolder(out pUnkownDesktopFolder); + if (S_OK != nResult) + { + throw new ShellContextMenuException("Failed to get the desktop shell folder"); + } + _oDesktopFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pUnkownDesktopFolder, typeof(IShellFolder)); + } + + return _oDesktopFolder; + } + #endregion + + #region GetParentFolder() + /// + /// Gets the parent folder + /// + /// Folder path + /// IShellFolder for the folder (relative from the desktop) + private IShellFolder GetParentFolder(string folderName) + { + if (null == _oParentFolder) + { + IShellFolder oDesktopFolder = GetDesktopFolder(); + if (null == oDesktopFolder) + { + return null; + } + + // Get the PIDL for the folder file is in + IntPtr pPIDL = IntPtr.Zero; + uint pchEaten = 0; + SFGAO pdwAttributes = 0; + int nResult = oDesktopFolder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, folderName, ref pchEaten, out pPIDL, ref pdwAttributes); + if (S_OK != nResult) + { + return null; + } + + IntPtr pStrRet = Marshal.AllocCoTaskMem(MAX_PATH * 2 + 4); + Marshal.WriteInt32(pStrRet, 0, 0); + nResult = _oDesktopFolder.GetDisplayNameOf(pPIDL, SHGNO.FORPARSING, pStrRet); + StringBuilder strFolder = new StringBuilder(MAX_PATH); + StrRetToBuf(pStrRet, pPIDL, strFolder, MAX_PATH); + Marshal.FreeCoTaskMem(pStrRet); + pStrRet = IntPtr.Zero; + _strParentFolder = strFolder.ToString(); + + // Get the IShellFolder for folder + IntPtr pUnknownParentFolder = IntPtr.Zero; + nResult = oDesktopFolder.BindToObject(pPIDL, IntPtr.Zero, ref IID_IShellFolder, out pUnknownParentFolder); + // Free the PIDL first + Marshal.FreeCoTaskMem(pPIDL); + if (S_OK != nResult) + { + return null; + } + _oParentFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pUnknownParentFolder, typeof(IShellFolder)); + } + + return _oParentFolder; + } + #endregion + + #region GetPIDLs() + /// + /// Get the PIDLs + /// + /// Array of FileInfo + /// Array of PIDLs + protected IntPtr[] GetPIDLs(FileInfo[] arrFI) + { + if (null == arrFI || 0 == arrFI.Length) + { + return null; + } + + IShellFolder oParentFolder = GetParentFolder(arrFI[0].DirectoryName); + if (null == oParentFolder) + { + return null; + } + + IntPtr[] arrPIDLs = new IntPtr[arrFI.Length]; + int n = 0; + foreach (FileInfo fi in arrFI) + { + // Get the file relative to folder + uint pchEaten = 0; + SFGAO pdwAttributes = 0; + IntPtr pPIDL = IntPtr.Zero; + int nResult = oParentFolder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, fi.Name, ref pchEaten, out pPIDL, ref pdwAttributes); + if (S_OK != nResult) + { + FreePIDLs(arrPIDLs); + return null; + } + arrPIDLs[n] = pPIDL; + n++; + } + + return arrPIDLs; + } + + /// + /// Get the PIDLs + /// + /// Array of DirectoryInfo + /// Array of PIDLs + protected IntPtr[] GetPIDLs(DirectoryInfo[] arrFI) + { + if (null == arrFI || 0 == arrFI.Length) + { + return null; + } + + IShellFolder oParentFolder = GetParentFolder(arrFI[0].Parent.FullName); + if (null == oParentFolder) + { + return null; + } + + IntPtr[] arrPIDLs = new IntPtr[arrFI.Length]; + int n = 0; + foreach (DirectoryInfo fi in arrFI) + { + // Get the file relative to folder + uint pchEaten = 0; + SFGAO pdwAttributes = 0; + IntPtr pPIDL = IntPtr.Zero; + int nResult = oParentFolder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, fi.Name, ref pchEaten, out pPIDL, ref pdwAttributes); + if (S_OK != nResult) + { + FreePIDLs(arrPIDLs); + return null; + } + arrPIDLs[n] = pPIDL; + n++; + } + + return arrPIDLs; + } + #endregion + + #region FreePIDLs() + /// + /// Free the PIDLs + /// + /// Array of PIDLs (IntPtr) + protected void FreePIDLs(IntPtr[] arrPIDLs) + { + if (null != arrPIDLs) + { + for (int n = 0; n < arrPIDLs.Length; n++) + { + if (arrPIDLs[n] != IntPtr.Zero) + { + Marshal.FreeCoTaskMem(arrPIDLs[n]); + arrPIDLs[n] = IntPtr.Zero; + } + } + } + } + #endregion + + #region InvokeContextMenuDefault + private void InvokeContextMenuDefault(FileInfo[] arrFI) + { + // Release all resources first. + ReleaseAll(); + + IntPtr pMenu = IntPtr.Zero, + iContextMenuPtr = IntPtr.Zero; + + try + { + _arrPIDLs = GetPIDLs(arrFI); + if (null == _arrPIDLs) + { + ReleaseAll(); + return; + } + + if (false == GetContextMenuInterfaces(_oParentFolder, _arrPIDLs, out iContextMenuPtr)) + { + ReleaseAll(); + return; + } + + pMenu = CreatePopupMenu(); + + int nResult = _oContextMenu.QueryContextMenu( + pMenu, + 0, + CMD_FIRST, + CMD_LAST, + CMF.DEFAULTONLY | + ((System.Windows.Forms.Control.ModifierKeys & Keys.Shift) != 0 ? CMF.EXTENDEDVERBS : 0)); + + uint nDefaultCmd = (uint)GetMenuDefaultItem(pMenu, false, 0); + if (nDefaultCmd >= CMD_FIRST) + { + InvokeCommand(_oContextMenu, nDefaultCmd, arrFI[0].DirectoryName, System.Windows.Forms.Control.MousePosition); + } + + DestroyMenu(pMenu); + pMenu = IntPtr.Zero; + } + catch + { + throw; + } + finally + { + if (pMenu != IntPtr.Zero) + { + DestroyMenu(pMenu); + } + ReleaseAll(); + } + } + #endregion + + #region ShowContextMenu() + + /// + /// Shows the context menu + /// + /// FileInfos (should all be in same directory) + /// Where to show the menu + public void ShowContextMenu(FileInfo[] files, Point pointScreen) + { + // Release all resources first. + ReleaseAll(); + _arrPIDLs = GetPIDLs(files); + this.ShowContextMenu(pointScreen); + } + + /// + /// Shows the context menu + /// + /// DirectoryInfos (should all be in same directory) + /// Where to show the menu + public void ShowContextMenu(DirectoryInfo[] dirs, Point pointScreen) + { + // Release all resources first. + ReleaseAll(); + _arrPIDLs = GetPIDLs(dirs); + this.ShowContextMenu(pointScreen); + } + + /// + /// Shows the context menu + /// + /// FileInfos (should all be in same directory) + /// Where to show the menu + private void ShowContextMenu(Point pointScreen) + { + IntPtr pMenu = IntPtr.Zero, + iContextMenuPtr = IntPtr.Zero, + iContextMenuPtr2 = IntPtr.Zero, + iContextMenuPtr3 = IntPtr.Zero; + + try + { + if (null == _arrPIDLs) + { + ReleaseAll(); + return; + } + + if (false == GetContextMenuInterfaces(_oParentFolder, _arrPIDLs, out iContextMenuPtr)) + { + ReleaseAll(); + return; + } + + pMenu = CreatePopupMenu(); + + int nResult = _oContextMenu.QueryContextMenu( + pMenu, + 0, + CMD_FIRST, + CMD_LAST, + CMF.EXPLORE | + CMF.NORMAL | + ((System.Windows.Forms.Control.ModifierKeys & Keys.Shift) != 0 ? CMF.EXTENDEDVERBS : 0)); + + Marshal.QueryInterface(iContextMenuPtr, ref IID_IContextMenu2, out iContextMenuPtr2); + Marshal.QueryInterface(iContextMenuPtr, ref IID_IContextMenu3, out iContextMenuPtr3); + + _oContextMenu2 = (IContextMenu2)Marshal.GetTypedObjectForIUnknown(iContextMenuPtr2, typeof(IContextMenu2)); + _oContextMenu3 = (IContextMenu3)Marshal.GetTypedObjectForIUnknown(iContextMenuPtr3, typeof(IContextMenu3)); + + uint nSelected = TrackPopupMenuEx( + pMenu, + TPM.RETURNCMD, + pointScreen.X, + pointScreen.Y, + this.Handle, + IntPtr.Zero); + + DestroyMenu(pMenu); + pMenu = IntPtr.Zero; + + if (nSelected != 0) + { + InvokeCommand(_oContextMenu, nSelected, _strParentFolder, pointScreen); + } + } + catch + { + throw; + } + finally + { + //hook.Uninstall(); + if (pMenu != IntPtr.Zero) + { + DestroyMenu(pMenu); + } + + if (iContextMenuPtr != IntPtr.Zero) + Marshal.Release(iContextMenuPtr); + + if (iContextMenuPtr2 != IntPtr.Zero) + Marshal.Release(iContextMenuPtr2); + + if (iContextMenuPtr3 != IntPtr.Zero) + Marshal.Release(iContextMenuPtr3); + + ReleaseAll(); + } + } + #endregion + + #region Local variabled + private IContextMenu _oContextMenu; + private IContextMenu2 _oContextMenu2; + private IContextMenu3 _oContextMenu3; + private IShellFolder _oDesktopFolder; + private IShellFolder _oParentFolder; + private IntPtr[] _arrPIDLs; + private string _strParentFolder; + #endregion + + #region Variables and Constants + + private const int MAX_PATH = 260; + private const uint CMD_FIRST = 1; + private const uint CMD_LAST = 30000; + + private const int S_OK = 0; + private const int S_FALSE = 1; + + private static int cbMenuItemInfo = Marshal.SizeOf(typeof(MENUITEMINFO)); + private static int cbInvokeCommand = Marshal.SizeOf(typeof(CMINVOKECOMMANDINFOEX)); + + #endregion + + #region DLL Import + + // Retrieves the IShellFolder interface for the desktop folder, which is the root of the Shell's namespace. + [DllImport("shell32.dll")] + private static extern Int32 SHGetDesktopFolder(out IntPtr ppshf); + + // Takes a STRRET structure returned by IShellFolder::GetDisplayNameOf, converts it to a string, and places the result in a buffer. + [DllImport("shlwapi.dll", EntryPoint = "StrRetToBuf", ExactSpelling = false, CharSet = CharSet.Auto, SetLastError = true)] + private static extern Int32 StrRetToBuf(IntPtr pstr, IntPtr pidl, StringBuilder pszBuf, int cchBuf); + + // The TrackPopupMenuEx function displays a shortcut menu at the specified location and tracks the selection of items on the shortcut menu. The shortcut menu can appear anywhere on the screen. + [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)] + private static extern uint TrackPopupMenuEx(IntPtr hmenu, TPM flags, int x, int y, IntPtr hwnd, IntPtr lptpm); + + // The CreatePopupMenu function creates a drop-down menu, submenu, or shortcut menu. The menu is initially empty. You can insert or append menu items by using the InsertMenuItem function. You can also use the InsertMenu function to insert menu items and the AppendMenu function to append menu items. + [DllImport("user32", SetLastError = true, CharSet = CharSet.Auto)] + private static extern IntPtr CreatePopupMenu(); + + // The DestroyMenu function destroys the specified menu and frees any memory that the menu occupies. + [DllImport("user32", SetLastError = true, CharSet = CharSet.Auto)] + private static extern bool DestroyMenu(IntPtr hMenu); + + // Determines the default menu item on the specified menu + [DllImport("user32", SetLastError = true, CharSet = CharSet.Auto)] + private static extern int GetMenuDefaultItem(IntPtr hMenu, bool fByPos, uint gmdiFlags); + + #endregion + + #region Shell GUIDs + + private static Guid IID_IShellFolder = new Guid("{000214E6-0000-0000-C000-000000000046}"); + private static Guid IID_IContextMenu = new Guid("{000214e4-0000-0000-c000-000000000046}"); + private static Guid IID_IContextMenu2 = new Guid("{000214f4-0000-0000-c000-000000000046}"); + private static Guid IID_IContextMenu3 = new Guid("{bcfce0a0-ec17-11d0-8d10-00a0c90f2719}"); + + #endregion + + #region Structs + + [StructLayout(LayoutKind.Sequential)] + private struct CWPSTRUCT + { + public IntPtr lparam; + public IntPtr wparam; + public int message; + public IntPtr hwnd; + } + + // Contains extended information about a shortcut menu command + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + private struct CMINVOKECOMMANDINFOEX + { + public int cbSize; + public CMIC fMask; + public IntPtr hwnd; + public IntPtr lpVerb; + [MarshalAs(UnmanagedType.LPStr)] + public string lpParameters; + [MarshalAs(UnmanagedType.LPStr)] + public string lpDirectory; + public SW nShow; + public int dwHotKey; + public IntPtr hIcon; + [MarshalAs(UnmanagedType.LPStr)] + public string lpTitle; + public IntPtr lpVerbW; + [MarshalAs(UnmanagedType.LPWStr)] + public string lpParametersW; + [MarshalAs(UnmanagedType.LPWStr)] + public string lpDirectoryW; + [MarshalAs(UnmanagedType.LPWStr)] + public string lpTitleW; + public POINT ptInvoke; + } + + // Contains information about a menu item + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + private struct MENUITEMINFO + { + public MENUITEMINFO(string text) + { + cbSize = cbMenuItemInfo; + dwTypeData = text; + cch = text.Length; + fMask = 0; + fType = 0; + fState = 0; + wID = 0; + hSubMenu = IntPtr.Zero; + hbmpChecked = IntPtr.Zero; + hbmpUnchecked = IntPtr.Zero; + dwItemData = IntPtr.Zero; + hbmpItem = IntPtr.Zero; + } + + public int cbSize; + public MIIM fMask; + public MFT fType; + public MFS fState; + public uint wID; + public IntPtr hSubMenu; + public IntPtr hbmpChecked; + public IntPtr hbmpUnchecked; + public IntPtr dwItemData; + [MarshalAs(UnmanagedType.LPTStr)] + public string dwTypeData; + public int cch; + public IntPtr hbmpItem; + } + + // A generalized global memory handle used for data transfer operations by the + // IAdviseSink, IDataObject, and IOleCache interfaces + [StructLayout(LayoutKind.Sequential)] + private struct STGMEDIUM + { + public TYMED tymed; + public IntPtr hBitmap; + public IntPtr hMetaFilePict; + public IntPtr hEnhMetaFile; + public IntPtr hGlobal; + public IntPtr lpszFileName; + public IntPtr pstm; + public IntPtr pstg; + public IntPtr pUnkForRelease; + } + + // Defines the x- and y-coordinates of a point + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + private struct POINT + { + public POINT(int x, int y) + { + this.x = x; + this.y = y; + } + + public int x; + public int y; + } + + #endregion + + #region Enums + + // Defines the values used with the IShellFolder::GetDisplayNameOf and IShellFolder::SetNameOf + // methods to specify the type of file or folder names used by those methods + [Flags] + private enum SHGNO + { + NORMAL = 0x0000, + INFOLDER = 0x0001, + FOREDITING = 0x1000, + FORADDRESSBAR = 0x4000, + FORPARSING = 0x8000 + } + + // The attributes that the caller is requesting, when calling IShellFolder::GetAttributesOf + [Flags] + private enum SFGAO : uint + { + BROWSABLE = 0x8000000, + CANCOPY = 1, + CANDELETE = 0x20, + CANLINK = 4, + CANMONIKER = 0x400000, + CANMOVE = 2, + CANRENAME = 0x10, + CAPABILITYMASK = 0x177, + COMPRESSED = 0x4000000, + CONTENTSMASK = 0x80000000, + DISPLAYATTRMASK = 0xfc000, + DROPTARGET = 0x100, + ENCRYPTED = 0x2000, + FILESYSANCESTOR = 0x10000000, + FILESYSTEM = 0x40000000, + FOLDER = 0x20000000, + GHOSTED = 0x8000, + HASPROPSHEET = 0x40, + HASSTORAGE = 0x400000, + HASSUBFOLDER = 0x80000000, + HIDDEN = 0x80000, + ISSLOW = 0x4000, + LINK = 0x10000, + NEWCONTENT = 0x200000, + NONENUMERATED = 0x100000, + READONLY = 0x40000, + REMOVABLE = 0x2000000, + SHARE = 0x20000, + STORAGE = 8, + STORAGEANCESTOR = 0x800000, + STORAGECAPMASK = 0x70c50008, + STREAM = 0x400000, + VALIDATE = 0x1000000 + } + + // Determines the type of items included in an enumeration. + // These values are used with the IShellFolder::EnumObjects method + [Flags] + private enum SHCONTF + { + FOLDERS = 0x0020, + NONFOLDERS = 0x0040, + INCLUDEHIDDEN = 0x0080, + INIT_ON_FIRST_NEXT = 0x0100, + NETPRINTERSRCH = 0x0200, + SHAREABLE = 0x0400, + STORAGE = 0x0800, + } + + // Specifies how the shortcut menu can be changed when calling IContextMenu::QueryContextMenu + [Flags] + private enum CMF : uint + { + NORMAL = 0x00000000, + DEFAULTONLY = 0x00000001, + VERBSONLY = 0x00000002, + EXPLORE = 0x00000004, + NOVERBS = 0x00000008, + CANRENAME = 0x00000010, + NODEFAULT = 0x00000020, + INCLUDESTATIC = 0x00000040, + EXTENDEDVERBS = 0x00000100, + RESERVED = 0xffff0000 + } + + // Flags specifying the information to return when calling IContextMenu::GetCommandString + [Flags] + private enum GCS : uint + { + VERBA = 0, + HELPTEXTA = 1, + VALIDATEA = 2, + VERBW = 4, + HELPTEXTW = 5, + VALIDATEW = 6 + } + + // Specifies how TrackPopupMenuEx positions the shortcut menu horizontally + [Flags] + private enum TPM : uint + { + LEFTBUTTON = 0x0000, + RIGHTBUTTON = 0x0002, + LEFTALIGN = 0x0000, + CENTERALIGN = 0x0004, + RIGHTALIGN = 0x0008, + TOPALIGN = 0x0000, + VCENTERALIGN = 0x0010, + BOTTOMALIGN = 0x0020, + HORIZONTAL = 0x0000, + VERTICAL = 0x0040, + NONOTIFY = 0x0080, + RETURNCMD = 0x0100, + RECURSE = 0x0001, + HORPOSANIMATION = 0x0400, + HORNEGANIMATION = 0x0800, + VERPOSANIMATION = 0x1000, + VERNEGANIMATION = 0x2000, + NOANIMATION = 0x4000, + LAYOUTRTL = 0x8000 + } + + // The cmd for a custom added menu item + private enum CMD_CUSTOM + { + ExpandCollapse = (int)CMD_LAST + 1 + } + + // Flags used with the CMINVOKECOMMANDINFOEX structure + [Flags] + private enum CMIC : uint + { + HOTKEY = 0x00000020, + ICON = 0x00000010, + FLAG_NO_UI = 0x00000400, + UNICODE = 0x00004000, + NO_CONSOLE = 0x00008000, + ASYNCOK = 0x00100000, + NOZONECHECKS = 0x00800000, + SHIFT_DOWN = 0x10000000, + CONTROL_DOWN = 0x40000000, + FLAG_LOG_USAGE = 0x04000000, + PTINVOKE = 0x20000000 + } + + // Specifies how the window is to be shown + [Flags] + private enum SW + { + HIDE = 0, + SHOWNORMAL = 1, + NORMAL = 1, + SHOWMINIMIZED = 2, + SHOWMAXIMIZED = 3, + MAXIMIZE = 3, + SHOWNOACTIVATE = 4, + SHOW = 5, + MINIMIZE = 6, + SHOWMINNOACTIVE = 7, + SHOWNA = 8, + RESTORE = 9, + SHOWDEFAULT = 10, + } + + // Window message flags + [Flags] + private enum WM : uint + { + ACTIVATE = 0x6, + ACTIVATEAPP = 0x1C, + AFXFIRST = 0x360, + AFXLAST = 0x37F, + APP = 0x8000, + ASKCBFORMATNAME = 0x30C, + CANCELJOURNAL = 0x4B, + CANCELMODE = 0x1F, + CAPTURECHANGED = 0x215, + CHANGECBCHAIN = 0x30D, + CHAR = 0x102, + CHARTOITEM = 0x2F, + CHILDACTIVATE = 0x22, + CLEAR = 0x303, + CLOSE = 0x10, + COMMAND = 0x111, + COMPACTING = 0x41, + COMPAREITEM = 0x39, + CONTEXTMENU = 0x7B, + COPY = 0x301, + COPYDATA = 0x4A, + CREATE = 0x1, + CTLCOLORBTN = 0x135, + CTLCOLORDLG = 0x136, + CTLCOLOREDIT = 0x133, + CTLCOLORLISTBOX = 0x134, + CTLCOLORMSGBOX = 0x132, + CTLCOLORSCROLLBAR = 0x137, + CTLCOLORSTATIC = 0x138, + CUT = 0x300, + DEADCHAR = 0x103, + DELETEITEM = 0x2D, + DESTROY = 0x2, + DESTROYCLIPBOARD = 0x307, + DEVICECHANGE = 0x219, + DEVMODECHANGE = 0x1B, + DISPLAYCHANGE = 0x7E, + DRAWCLIPBOARD = 0x308, + DRAWITEM = 0x2B, + DROPFILES = 0x233, + ENABLE = 0xA, + ENDSESSION = 0x16, + ENTERIDLE = 0x121, + ENTERMENULOOP = 0x211, + ENTERSIZEMOVE = 0x231, + ERASEBKGND = 0x14, + EXITMENULOOP = 0x212, + EXITSIZEMOVE = 0x232, + FONTCHANGE = 0x1D, + GETDLGCODE = 0x87, + GETFONT = 0x31, + GETHOTKEY = 0x33, + GETICON = 0x7F, + GETMINMAXINFO = 0x24, + GETOBJECT = 0x3D, + GETSYSMENU = 0x313, + GETTEXT = 0xD, + GETTEXTLENGTH = 0xE, + HANDHELDFIRST = 0x358, + HANDHELDLAST = 0x35F, + HELP = 0x53, + HOTKEY = 0x312, + HSCROLL = 0x114, + HSCROLLCLIPBOARD = 0x30E, + ICONERASEBKGND = 0x27, + IME_CHAR = 0x286, + IME_COMPOSITION = 0x10F, + IME_COMPOSITIONFULL = 0x284, + IME_CONTROL = 0x283, + IME_ENDCOMPOSITION = 0x10E, + IME_KEYDOWN = 0x290, + IME_KEYLAST = 0x10F, + IME_KEYUP = 0x291, + IME_NOTIFY = 0x282, + IME_REQUEST = 0x288, + IME_SELECT = 0x285, + IME_SETCONTEXT = 0x281, + IME_STARTCOMPOSITION = 0x10D, + INITDIALOG = 0x110, + INITMENU = 0x116, + INITMENUPOPUP = 0x117, + INPUTLANGCHANGE = 0x51, + INPUTLANGCHANGEREQUEST = 0x50, + KEYDOWN = 0x100, + KEYFIRST = 0x100, + KEYLAST = 0x108, + KEYUP = 0x101, + KILLFOCUS = 0x8, + LBUTTONDBLCLK = 0x203, + LBUTTONDOWN = 0x201, + LBUTTONUP = 0x202, + LVM_GETEDITCONTROL = 0x1018, + LVM_SETIMAGELIST = 0x1003, + MBUTTONDBLCLK = 0x209, + MBUTTONDOWN = 0x207, + MBUTTONUP = 0x208, + MDIACTIVATE = 0x222, + MDICASCADE = 0x227, + MDICREATE = 0x220, + MDIDESTROY = 0x221, + MDIGETACTIVE = 0x229, + MDIICONARRANGE = 0x228, + MDIMAXIMIZE = 0x225, + MDINEXT = 0x224, + MDIREFRESHMENU = 0x234, + MDIRESTORE = 0x223, + MDISETMENU = 0x230, + MDITILE = 0x226, + MEASUREITEM = 0x2C, + MENUCHAR = 0x120, + MENUCOMMAND = 0x126, + MENUDRAG = 0x123, + MENUGETOBJECT = 0x124, + MENURBUTTONUP = 0x122, + MENUSELECT = 0x11F, + MOUSEACTIVATE = 0x21, + MOUSEFIRST = 0x200, + MOUSEHOVER = 0x2A1, + MOUSELAST = 0x20A, + MOUSELEAVE = 0x2A3, + MOUSEMOVE = 0x200, + MOUSEWHEEL = 0x20A, + MOVE = 0x3, + MOVING = 0x216, + NCACTIVATE = 0x86, + NCCALCSIZE = 0x83, + NCCREATE = 0x81, + NCDESTROY = 0x82, + NCHITTEST = 0x84, + NCLBUTTONDBLCLK = 0xA3, + NCLBUTTONDOWN = 0xA1, + NCLBUTTONUP = 0xA2, + NCMBUTTONDBLCLK = 0xA9, + NCMBUTTONDOWN = 0xA7, + NCMBUTTONUP = 0xA8, + NCMOUSEHOVER = 0x2A0, + NCMOUSELEAVE = 0x2A2, + NCMOUSEMOVE = 0xA0, + NCPAINT = 0x85, + NCRBUTTONDBLCLK = 0xA6, + NCRBUTTONDOWN = 0xA4, + NCRBUTTONUP = 0xA5, + NEXTDLGCTL = 0x28, + NEXTMENU = 0x213, + NOTIFY = 0x4E, + NOTIFYFORMAT = 0x55, + NULL = 0x0, + PAINT = 0xF, + PAINTCLIPBOARD = 0x309, + PAINTICON = 0x26, + PALETTECHANGED = 0x311, + PALETTEISCHANGING = 0x310, + PARENTNOTIFY = 0x210, + PASTE = 0x302, + PENWINFIRST = 0x380, + PENWINLAST = 0x38F, + POWER = 0x48, + PRINT = 0x317, + PRINTCLIENT = 0x318, + QUERYDRAGICON = 0x37, + QUERYENDSESSION = 0x11, + QUERYNEWPALETTE = 0x30F, + QUERYOPEN = 0x13, + QUEUESYNC = 0x23, + QUIT = 0x12, + RBUTTONDBLCLK = 0x206, + RBUTTONDOWN = 0x204, + RBUTTONUP = 0x205, + RENDERALLFORMATS = 0x306, + RENDERFORMAT = 0x305, + SETCURSOR = 0x20, + SETFOCUS = 0x7, + SETFONT = 0x30, + SETHOTKEY = 0x32, + SETICON = 0x80, + SETMARGINS = 0xD3, + SETREDRAW = 0xB, + SETTEXT = 0xC, + SETTINGCHANGE = 0x1A, + SHOWWINDOW = 0x18, + SIZE = 0x5, + SIZECLIPBOARD = 0x30B, + SIZING = 0x214, + SPOOLERSTATUS = 0x2A, + STYLECHANGED = 0x7D, + STYLECHANGING = 0x7C, + SYNCPAINT = 0x88, + SYSCHAR = 0x106, + SYSCOLORCHANGE = 0x15, + SYSCOMMAND = 0x112, + SYSDEADCHAR = 0x107, + SYSKEYDOWN = 0x104, + SYSKEYUP = 0x105, + TCARD = 0x52, + TIMECHANGE = 0x1E, + TIMER = 0x113, + TVM_GETEDITCONTROL = 0x110F, + TVM_SETIMAGELIST = 0x1109, + UNDO = 0x304, + UNINITMENUPOPUP = 0x125, + USER = 0x400, + USERCHANGED = 0x54, + VKEYTOITEM = 0x2E, + VSCROLL = 0x115, + VSCROLLCLIPBOARD = 0x30A, + WINDOWPOSCHANGED = 0x47, + WINDOWPOSCHANGING = 0x46, + WININICHANGE = 0x1A, + SH_NOTIFY = 0x0401 + } + + // Specifies the content of the new menu item + [Flags] + private enum MFT : uint + { + GRAYED = 0x00000003, + DISABLED = 0x00000003, + CHECKED = 0x00000008, + SEPARATOR = 0x00000800, + RADIOCHECK = 0x00000200, + BITMAP = 0x00000004, + OWNERDRAW = 0x00000100, + MENUBARBREAK = 0x00000020, + MENUBREAK = 0x00000040, + RIGHTORDER = 0x00002000, + BYCOMMAND = 0x00000000, + BYPOSITION = 0x00000400, + POPUP = 0x00000010 + } + + // Specifies the state of the new menu item + [Flags] + private enum MFS : uint + { + GRAYED = 0x00000003, + DISABLED = 0x00000003, + CHECKED = 0x00000008, + HILITE = 0x00000080, + ENABLED = 0x00000000, + UNCHECKED = 0x00000000, + UNHILITE = 0x00000000, + DEFAULT = 0x00001000 + } + + // Specifies the content of the new menu item + [Flags] + private enum MIIM : uint + { + BITMAP = 0x80, + CHECKMARKS = 0x08, + DATA = 0x20, + FTYPE = 0x100, + ID = 0x02, + STATE = 0x01, + STRING = 0x40, + SUBMENU = 0x04, + TYPE = 0x10 + } + + // Indicates the type of storage medium being used in a data transfer + [Flags] + private enum TYMED + { + ENHMF = 0x40, + FILE = 2, + GDI = 0x10, + HGLOBAL = 1, + ISTORAGE = 8, + ISTREAM = 4, + MFPICT = 0x20, + NULL = 0 + } + + #endregion + + #region IShellFolder + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("000214E6-0000-0000-C000-000000000046")] + private interface IShellFolder + { + // Translates a file object's or folder's display name into an item identifier list. + // Return value: error code, if any + [PreserveSig] + Int32 ParseDisplayName( + IntPtr hwnd, + IntPtr pbc, + [MarshalAs(UnmanagedType.LPWStr)] + string pszDisplayName, + ref uint pchEaten, + out IntPtr ppidl, + ref SFGAO pdwAttributes); + + // Allows a client to determine the contents of a folder by creating an item + // identifier enumeration object and returning its IEnumIDList interface. + // Return value: error code, if any + [PreserveSig] + Int32 EnumObjects( + IntPtr hwnd, + SHCONTF grfFlags, + out IntPtr enumIDList); + + // Retrieves an IShellFolder object for a subfolder. + // Return value: error code, if any + [PreserveSig] + Int32 BindToObject( + IntPtr pidl, + IntPtr pbc, + ref Guid riid, + out IntPtr ppv); + + // Requests a pointer to an object's storage interface. + // Return value: error code, if any + [PreserveSig] + Int32 BindToStorage( + IntPtr pidl, + IntPtr pbc, + ref Guid riid, + out IntPtr ppv); + + // Determines the relative order of two file objects or folders, given their + // item identifier lists. Return value: If this method is successful, the + // CODE field of the HRESULT contains one of the following values (the code + // can be retrived using the helper function GetHResultCode): Negative A + // negative return value indicates that the first item should precede + // the second (pidl1 < pidl2). + + // Positive A positive return value indicates that the first item should + // follow the second (pidl1 > pidl2). Zero A return value of zero + // indicates that the two items are the same (pidl1 = pidl2). + [PreserveSig] + Int32 CompareIDs( + IntPtr lParam, + IntPtr pidl1, + IntPtr pidl2); + + // Requests an object that can be used to obtain information from or interact + // with a folder object. + // Return value: error code, if any + [PreserveSig] + Int32 CreateViewObject( + IntPtr hwndOwner, + Guid riid, + out IntPtr ppv); + + // Retrieves the attributes of one or more file objects or subfolders. + // Return value: error code, if any + [PreserveSig] + Int32 GetAttributesOf( + uint cidl, + [MarshalAs(UnmanagedType.LPArray)] + IntPtr[] apidl, + ref SFGAO rgfInOut); + + // Retrieves an OLE interface that can be used to carry out actions on the + // specified file objects or folders. + // Return value: error code, if any + [PreserveSig] + Int32 GetUIObjectOf( + IntPtr hwndOwner, + uint cidl, + [MarshalAs(UnmanagedType.LPArray)] + IntPtr[] apidl, + ref Guid riid, + IntPtr rgfReserved, + out IntPtr ppv); + + // Retrieves the display name for the specified file object or subfolder. + // Return value: error code, if any + [PreserveSig()] + Int32 GetDisplayNameOf( + IntPtr pidl, + SHGNO uFlags, + IntPtr lpName); + + // Sets the display name of a file object or subfolder, changing the item + // identifier in the process. + // Return value: error code, if any + [PreserveSig] + Int32 SetNameOf( + IntPtr hwnd, + IntPtr pidl, + [MarshalAs(UnmanagedType.LPWStr)] + string pszName, + SHGNO uFlags, + out IntPtr ppidlOut); + } + #endregion + + #region IContextMenu + [ComImport()] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [GuidAttribute("000214e4-0000-0000-c000-000000000046")] + private interface IContextMenu + { + // Adds commands to a shortcut menu + [PreserveSig()] + Int32 QueryContextMenu( + IntPtr hmenu, + uint iMenu, + uint idCmdFirst, + uint idCmdLast, + CMF uFlags); + + // Carries out the command associated with a shortcut menu item + [PreserveSig()] + Int32 InvokeCommand( + ref CMINVOKECOMMANDINFOEX info); + + // Retrieves information about a shortcut menu command, + // including the help string and the language-independent, + // or canonical, name for the command + [PreserveSig()] + Int32 GetCommandString( + uint idcmd, + GCS uflags, + uint reserved, + [MarshalAs(UnmanagedType.LPArray)] + byte[] commandstring, + int cch); + } + + [ComImport, Guid("000214f4-0000-0000-c000-000000000046")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + private interface IContextMenu2 + { + // Adds commands to a shortcut menu + [PreserveSig()] + Int32 QueryContextMenu( + IntPtr hmenu, + uint iMenu, + uint idCmdFirst, + uint idCmdLast, + CMF uFlags); + + // Carries out the command associated with a shortcut menu item + [PreserveSig()] + Int32 InvokeCommand( + ref CMINVOKECOMMANDINFOEX info); + + // Retrieves information about a shortcut menu command, + // including the help string and the language-independent, + // or canonical, name for the command + [PreserveSig()] + Int32 GetCommandString( + uint idcmd, + GCS uflags, + uint reserved, + [MarshalAs(UnmanagedType.LPWStr)] + StringBuilder commandstring, + int cch); + + // Allows client objects of the IContextMenu interface to + // handle messages associated with owner-drawn menu items + [PreserveSig] + Int32 HandleMenuMsg( + uint uMsg, + IntPtr wParam, + IntPtr lParam); + } + + [ComImport, Guid("bcfce0a0-ec17-11d0-8d10-00a0c90f2719")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + private interface IContextMenu3 + { + // Adds commands to a shortcut menu + [PreserveSig()] + Int32 QueryContextMenu( + IntPtr hmenu, + uint iMenu, + uint idCmdFirst, + uint idCmdLast, + CMF uFlags); + + // Carries out the command associated with a shortcut menu item + [PreserveSig()] + Int32 InvokeCommand( + ref CMINVOKECOMMANDINFOEX info); + + // Retrieves information about a shortcut menu command, + // including the help string and the language-independent, + // or canonical, name for the command + [PreserveSig()] + Int32 GetCommandString( + uint idcmd, + GCS uflags, + uint reserved, + [MarshalAs(UnmanagedType.LPWStr)] + StringBuilder commandstring, + int cch); + + // Allows client objects of the IContextMenu interface to + // handle messages associated with owner-drawn menu items + [PreserveSig] + Int32 HandleMenuMsg( + uint uMsg, + IntPtr wParam, + IntPtr lParam); + + // Allows client objects of the IContextMenu3 interface to + // handle messages associated with owner-drawn menu items + [PreserveSig] + Int32 HandleMenuMsg2( + uint uMsg, + IntPtr wParam, + IntPtr lParam, + IntPtr plResult); + } + #endregion + } + + #region ShellContextMenuException + public class ShellContextMenuException : Exception + { + /// Default contructor + public ShellContextMenuException() + { + } + + /// Constructor with message + /// Message + public ShellContextMenuException(string message) + : base(message) + { + } + } + #endregion + + #region Class HookEventArgs + public class HookEventArgs : EventArgs + { + public int HookCode; // Hook code + public IntPtr wParam; // WPARAM argument + public IntPtr lParam; // LPARAM argument + } + #endregion + + #region Enum HookType + // Hook Types + public enum HookType : int + { + WH_JOURNALRECORD = 0, + WH_JOURNALPLAYBACK = 1, + WH_KEYBOARD = 2, + WH_GETMESSAGE = 3, + WH_CALLWNDPROC = 4, + WH_CBT = 5, + WH_SYSMSGFILTER = 6, + WH_MOUSE = 7, + WH_HARDWARE = 8, + WH_DEBUG = 9, + WH_SHELL = 10, + WH_FOREGROUNDIDLE = 11, + WH_CALLWNDPROCRET = 12, + WH_KEYBOARD_LL = 13, + WH_MOUSE_LL = 14 + } + #endregion + + #region Class LocalWindowsHook + public class LocalWindowsHook + { + // ************************************************************************ + // Filter function delegate + public delegate int HookProc(int code, IntPtr wParam, IntPtr lParam); + // ************************************************************************ + + // ************************************************************************ + // Internal properties + protected IntPtr m_hhook = IntPtr.Zero; + protected HookProc m_filterFunc = null; + protected HookType m_hookType; + // ************************************************************************ + + // ************************************************************************ + // Event delegate + public delegate void HookEventHandler(object sender, HookEventArgs e); + // ************************************************************************ + + // ************************************************************************ + // Event: HookInvoked + public event HookEventHandler HookInvoked; + protected void OnHookInvoked(HookEventArgs e) + { + if (HookInvoked != null) + HookInvoked(this, e); + } + // ************************************************************************ + + // ************************************************************************ + // Class constructor(s) + public LocalWindowsHook(HookType hook) + { + m_hookType = hook; + m_filterFunc = new HookProc(this.CoreHookProc); + } + public LocalWindowsHook(HookType hook, HookProc func) + { + m_hookType = hook; + m_filterFunc = func; + } + // ************************************************************************ + + // ************************************************************************ + // Default filter function + protected int CoreHookProc(int code, IntPtr wParam, IntPtr lParam) + { + if (code < 0) + return CallNextHookEx(m_hhook, code, wParam, lParam); + + // Let clients determine what to do + HookEventArgs e = new HookEventArgs(); + e.HookCode = code; + e.wParam = wParam; + e.lParam = lParam; + OnHookInvoked(e); + + // Yield to the next hook in the chain + return CallNextHookEx(m_hhook, code, wParam, lParam); + } + // ************************************************************************ + + // ************************************************************************ + // Install the hook + public void Install() + { + m_hhook = SetWindowsHookEx( + m_hookType, + m_filterFunc, + IntPtr.Zero, + (int)AppDomain.GetCurrentThreadId()); + } + // ************************************************************************ + + // ************************************************************************ + // Uninstall the hook + public void Uninstall() + { + UnhookWindowsHookEx(m_hhook); + } + // ************************************************************************ + + + #region Win32 Imports + // ************************************************************************ + // Win32: SetWindowsHookEx() + [DllImport("user32.dll")] + protected static extern IntPtr SetWindowsHookEx(HookType code, + HookProc func, + IntPtr hInstance, + int threadID); + // ************************************************************************ + + // ************************************************************************ + // Win32: UnhookWindowsHookEx() + [DllImport("user32.dll")] + protected static extern int UnhookWindowsHookEx(IntPtr hhook); + // ************************************************************************ + + // ************************************************************************ + // Win32: CallNextHookEx() + [DllImport("user32.dll")] + protected static extern int CallNextHookEx(IntPtr hhook, + int code, IntPtr wParam, IntPtr lParam); + // ************************************************************************ + #endregion + } + #endregion + + #region ShellHelper + + internal static class ShellHelper + { + #region Low/High Word + + /// + /// Retrieves the High Word of a WParam of a WindowMessage + /// + /// The pointer to the WParam + /// The unsigned integer for the High Word + public static uint HiWord(IntPtr ptr) + { + if (((uint)ptr & 0x80000000) == 0x80000000) + return ((uint)ptr >> 16); + else + return ((uint)ptr >> 16) & 0xffff; + } + + /// + /// Retrieves the Low Word of a WParam of a WindowMessage + /// + /// The pointer to the WParam + /// The unsigned integer for the Low Word + public static uint LoWord(IntPtr ptr) + { + return (uint)ptr & 0xffff; + } + + #endregion + } + + #endregion +} diff --git a/Util/SystemIcon.cs b/Util/SystemIcon.cs deleted file mode 100644 index 0e9f79c..0000000 --- a/Util/SystemIcon.cs +++ /dev/null @@ -1,145 +0,0 @@ -using System; -using System.Drawing; -using System.Runtime.InteropServices; - -namespace GeekDesk.Util -{ - class SystemIcon - { - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] - public struct SHFILEINFO - { - public IntPtr hIcon; - public int iIcon; - public uint dwAttributes; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] - public string szDisplayName; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)] - public string szTypeName; - } - [DllImport("Shell32.dll", EntryPoint = "SHGetFileInfo", SetLastError = true, CharSet = CharSet.Auto)] - public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbFileInfo, uint uFlags); - [DllImport("User32.dll", EntryPoint = "DestroyIcon")] - public static extern int DestroyIcon(IntPtr hIcon); - #region API 参数的常量定义 - public enum FileInfoFlags : uint - { - SHGFI_ICON = 0x000000100, // get icon - SHGFI_DISPLAYNAME = 0x000000200, // get display name - SHGFI_TYPENAME = 0x000000400, // get type name - SHGFI_ATTRIBUTES = 0x000000800, // get attributes - SHGFI_ICONLOCATION = 0x000001000, // get icon location - SHGFI_EXETYPE = 0x000002000, // return exe type - SHGFI_SYSICONINDEX = 0x000004000, // get system icon index - SHGFI_LINKOVERLAY = 0x000008000, // put a link overlay on icon - SHGFI_SELECTED = 0x000010000, // show icon in selected state - SHGFI_ATTR_SPECIFIED = 0x000020000, // get only specified attributes - SHGFI_LARGEICON = 0x000000000, // get large icon - SHGFI_SMALLICON = 0x000000001, // get small icon - SHGFI_OPENICON = 0x000000002, // get open icon - SHGFI_SHELLICONSIZE = 0x000000004, // get shell size icon - SHGFI_PIDL = 0x000000008, // pszPath is a pidl - SHGFI_USEFILEATTRIBUTES = 0x000000010, // use passed dwFileAttribute - SHGFI_ADDOVERLAYS = 0x000000020, // apply the appropriate overlays - SHGFI_OVERLAYINDEX = 0x000000040 // Get the index of the overlay - } - public enum FileAttributeFlags : uint - { - FILE_ATTRIBUTE_READONLY = 0x00000001, - FILE_ATTRIBUTE_HIDDEN = 0x00000002, - FILE_ATTRIBUTE_SYSTEM = 0x00000004, - FILE_ATTRIBUTE_DIRECTORY = 0x00000010, - FILE_ATTRIBUTE_ARCHIVE = 0x00000020, - FILE_ATTRIBUTE_DEVICE = 0x00000040, - FILE_ATTRIBUTE_NORMAL = 0x00000080, - FILE_ATTRIBUTE_TEMPORARY = 0x00000100, - FILE_ATTRIBUTE_SPARSE_FILE = 0x00000200, - FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400, - FILE_ATTRIBUTE_COMPRESSED = 0x00000800, - FILE_ATTRIBUTE_OFFLINE = 0x00001000, - FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000, - FILE_ATTRIBUTE_ENCRYPTED = 0x00004000 - } - #endregion - /// - /// 获取文件类型的关联图标 - /// - /// 文件类型的扩展名或文件的绝对路径 - /// 是否返回大图标 - /// 获取到的图标 - public static Icon GetIcon(string fileName, bool isLargeIcon) - { - //SHFILEINFO shfi = new SHFILEINFO(); - //IntPtr hI; - //if (isLargeIcon) - // hI = SHGetFileInfo(fileName, 0, ref shfi, (uint)Marshal.SizeOf(shfi), (uint)FileInfoFlags.SHGFI_ICON | (uint)FileInfoFlags.SHGFI_USEFILEATTRIBUTES | (uint)FileInfoFlags.SHGFI_LARGEICON); - //else - // hI = SHGetFileInfo(fileName, 0, ref shfi, (uint)Marshal.SizeOf(shfi), (uint)FileInfoFlags.SHGFI_ICON | (uint)FileInfoFlags.SHGFI_USEFILEATTRIBUTES | (uint)FileInfoFlags.SHGFI_SMALLICON); - //Icon icon = Icon.FromHandle(shfi.hIcon).Clone() as Icon; - //DestroyIcon(shfi.hIcon); //释放资源 - - //选中文件中的图标总数 - //var iconTotalCount = PrivateExtractIcons(fileName, 0, 0, 0, null, null, 1, 0); - //用于接收获取到的图标指针 - //IntPtr[] hIcons = new IntPtr[1]; - ////对应的图标id - //int[] ids = new int[1]; - ////成功获取到的图标个数 - //int successCount = PrivateExtractIcons(fileName, 0, 0, 0, hIcons, ids, 1, 0); - //Icon ico = Icon.FromHandle(hIcons[0]); - //var myIcon = ico.ToBitmap(); - //myIcon.Save("D:\\" + ids[0].ToString("000") + ".png", ImageFormat.Png); - IntPtr hIcon = FileIcon.GetJumboIcon(FileIcon.GetIconIndex(fileName)); - //IntPtr hIcon = GetJumboIcon(GetIconIndex("*." + ext)); - - // from native to managed - Icon ico = Icon.FromHandle(hIcon); - string path = "D:\\test\\" + System.Guid.NewGuid().ToString() + ".png"; - //using ( ico = (Icon)Icon.FromHandle(hIcon).Clone()) - //{ - // // save to file (or show in a picture box) - // ico.ToBitmap().Save(path, ImageFormat.Png); - //} - //FileIcon.Shell32.DestroyIcon(hIcon); // don't forget to cleanup - - return ico; - } - /// - /// 获取文件夹图标 - /// - /// 图标 - public static Icon GetDirectoryIcon(bool isLargeIcon) - { - SHFILEINFO _SHFILEINFO = new SHFILEINFO(); - IntPtr _IconIntPtr; - if (isLargeIcon) - { - _IconIntPtr = SHGetFileInfo(@"", 0, ref _SHFILEINFO, (uint)Marshal.SizeOf(_SHFILEINFO), ((uint)FileInfoFlags.SHGFI_ICON | (uint)FileInfoFlags.SHGFI_LARGEICON)); - } - else - { - _IconIntPtr = SHGetFileInfo(@"", 0, ref _SHFILEINFO, (uint)Marshal.SizeOf(_SHFILEINFO), ((uint)FileInfoFlags.SHGFI_ICON | (uint)FileInfoFlags.SHGFI_SMALLICON)); - } - if (_IconIntPtr.Equals(IntPtr.Zero)) return null; - Icon _Icon = System.Drawing.Icon.FromHandle(_SHFILEINFO.hIcon); - return _Icon; - } - - [DllImport("User32.dll")] - public static extern int PrivateExtractIcons( - string lpszFile, //file name - int nIconIndex, //The zero-based index of the first icon to extract. - int cxIcon, //The horizontal icon size wanted. - int cyIcon, //The vertical icon size wanted. - IntPtr[] phicon, //(out) A pointer to the returned array of icon handles. - int[] piconid, //(out) A pointer to a returned resource identifier. - int nIcons, //The number of icons to extract from the file. Only valid when *.exe and *.dll - int flags //Specifies flags that control this function. - ); - - //[DllImport("User32.dll")] - //public static extern bool DestroyIcon( - // IntPtr hIcon //A handle to the icon to be destroyed. The icon must not be in use. - // ); - } -} diff --git a/Util/WindowsThumbnailProvider.cs b/Util/WindowsThumbnailProvider.cs new file mode 100644 index 0000000..a59f5cb --- /dev/null +++ b/Util/WindowsThumbnailProvider.cs @@ -0,0 +1,442 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Runtime.InteropServices; +using System.Windows; +using System.Windows.Media.Imaging; +using System.Windows.Media; +using System.Diagnostics; +using System.Drawing; + +namespace GeekDesk.Util +{ + public class WindowsThumbnailProvider + { + private struct RECT + { + public int Left; + public int Top; + public int Right; + public int Bottom; + } + private struct POINT + { + public int x; + public int y; + } + // Constants that we need in the function call + + private const int SHGFI_ICON = 0x100; + + private const int SHGFI_SMALLICON = 0x1; + + private const int SHGFI_LARGEICON = 0x0; + + private const int SHIL_JUMBO = 0x4; + private const int SHIL_EXTRALARGE = 0x2; + + // This structure will contain information about the file + + public struct SHFILEINFO + { + + // Handle to the icon representing the file + + public IntPtr hIcon; + + // Index of the icon within the image list + + public int iIcon; + + // Various attributes of the file + + public uint dwAttributes; + + // Path to the file + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + + public string szDisplayName; + + // File type + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)] + + public string szTypeName; + + }; + + [System.Runtime.InteropServices.DllImport("Kernel32.dll")] + public static extern Boolean CloseHandle(IntPtr handle); + + private struct IMAGELISTDRAWPARAMS + { + public int cbSize; + public IntPtr himl; + public int i; + public IntPtr hdcDst; + public int x; + public int y; + public int cx; + public int cy; + public int xBitmap; // x offest from the upperleft of bitmap + public int yBitmap; // y offset from the upperleft of bitmap + public int rgbBk; + public int rgbFg; + public int fStyle; + public int dwRop; + public int fState; + public int Frame; + public int crEffect; + } + + [StructLayout(LayoutKind.Sequential)] + private struct IMAGEINFO + { + public IntPtr hbmImage; + public IntPtr hbmMask; + public int Unused1; + public int Unused2; + public RECT rcImage; + } + + #region Private ImageList COM Interop (XP) + [ComImportAttribute()] + [GuidAttribute("46EB5926-582E-4017-9FDF-E8998DAA0950")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + //helpstring("Image List"), + interface IImageList + { + [PreserveSig] + int Add( + IntPtr hbmImage, + IntPtr hbmMask, + ref int pi); + + [PreserveSig] + int ReplaceIcon( + int i, + IntPtr hicon, + ref int pi); + + [PreserveSig] + int SetOverlayImage( + int iImage, + int iOverlay); + + [PreserveSig] + int Replace( + int i, + IntPtr hbmImage, + IntPtr hbmMask); + + [PreserveSig] + int AddMasked( + IntPtr hbmImage, + int crMask, + ref int pi); + + [PreserveSig] + int Draw( + ref IMAGELISTDRAWPARAMS pimldp); + + [PreserveSig] + int Remove( + int i); + + [PreserveSig] + int GetIcon( + int i, + int flags, + ref IntPtr picon); + + [PreserveSig] + int GetImageInfo( + int i, + ref IMAGEINFO pImageInfo); + + [PreserveSig] + int Copy( + int iDst, + IImageList punkSrc, + int iSrc, + int uFlags); + + [PreserveSig] + int Merge( + int i1, + IImageList punk2, + int i2, + int dx, + int dy, + ref Guid riid, + ref IntPtr ppv); + + [PreserveSig] + int Clone( + ref Guid riid, + ref IntPtr ppv); + + [PreserveSig] + int GetImageRect( + int i, + ref RECT prc); + + [PreserveSig] + int GetIconSize( + ref int cx, + ref int cy); + + [PreserveSig] + int SetIconSize( + int cx, + int cy); + + [PreserveSig] + int GetImageCount( + ref int pi); + + [PreserveSig] + int SetImageCount( + int uNewCount); + + [PreserveSig] + int SetBkColor( + int clrBk, + ref int pclr); + + [PreserveSig] + int GetBkColor( + ref int pclr); + + [PreserveSig] + int BeginDrag( + int iTrack, + int dxHotspot, + int dyHotspot); + + [PreserveSig] + int EndDrag(); + + [PreserveSig] + int DragEnter( + IntPtr hwndLock, + int x, + int y); + + [PreserveSig] + int DragLeave( + IntPtr hwndLock); + + [PreserveSig] + int DragMove( + int x, + int y); + + [PreserveSig] + int SetDragCursorImage( + ref IImageList punk, + int iDrag, + int dxHotspot, + int dyHotspot); + + [PreserveSig] + int DragShowNolock( + int fShow); + + [PreserveSig] + int GetDragImage( + ref POINT ppt, + ref POINT pptHotspot, + ref Guid riid, + ref IntPtr ppv); + + [PreserveSig] + int GetItemFlags( + int i, + ref int dwFlags); + + [PreserveSig] + int GetOverlayImage( + int iOverlay, + ref int piIndex); + }; + #endregion + + /// + /// SHGetImageList is not exported correctly in XP. See KB316931 + /// http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q316931 + /// Apparently (and hopefully) ordinal 727 isn't going to change. + /// + [DllImport("shell32.dll", EntryPoint = "#727")] + private extern static int SHGetImageList( + int iImageList, + ref Guid riid, + out IImageList ppv + ); + + // The signature of SHGetFileInfo (located in Shell32.dll) + [DllImport("Shell32.dll")] + public static extern int SHGetFileInfo(string pszPath, int dwFileAttributes, ref SHFILEINFO psfi, int cbFileInfo, uint uFlags); + + [DllImport("Shell32.dll")] + public static extern int SHGetFileInfo(IntPtr pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, int cbFileInfo, uint uFlags); + + [DllImport("shell32.dll", SetLastError = true)] + static extern int SHGetSpecialFolderLocation(IntPtr hwndOwner, Int32 nFolder, + ref IntPtr ppidl); + + [DllImport("user32")] + public static extern int DestroyIcon(IntPtr hIcon); + + public struct pair + { + public System.Drawing.Icon icon { get; set; } + public IntPtr iconHandleToDestroy { set; get; } + + } + + public static int DestroyIcon2(IntPtr hIcon) + { + return DestroyIcon(hIcon); + } + + private static BitmapSource bitmap_source_of_icon(System.Drawing.Icon ic) + { + var ic2 = System.Windows.Interop.Imaging.CreateBitmapSourceFromHIcon(ic.Handle, + System.Windows.Int32Rect.Empty, + System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions()); + ic2.Freeze(); + return ((BitmapSource)ic2); + } + + //public static BitmapSource SystemIcon(bool small, ShellLib.ShellApi.CSIDL csidl) + //{ + + // IntPtr pidlTrash = IntPtr.Zero; + // int hr = SHGetSpecialFolderLocation(IntPtr.Zero, (int)csidl, ref pidlTrash); + // Debug.Assert(hr == 0); + + // SHFILEINFO shinfo = new SHFILEINFO(); + + // uint SHGFI_USEFILEATTRIBUTES = 0x000000010; + + // // Get a handle to the large icon + // uint flags; + // uint SHGFI_PIDL = 0x000000008; + // if (!small) + // { + // flags = SHGFI_PIDL | SHGFI_ICON | SHGFI_LARGEICON | SHGFI_USEFILEATTRIBUTES; + // } + // else + // { + // flags = SHGFI_PIDL | SHGFI_ICON | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES; + // } + + // var res = SHGetFileInfo(pidlTrash, 0, ref shinfo, Marshal.SizeOf(shinfo), flags); + // Debug.Assert(res != 0); + + // var myIcon = System.Drawing.Icon.FromHandle(shinfo.hIcon); + // Marshal.FreeCoTaskMem(pidlTrash); + // var bs = bitmap_source_of_icon(myIcon); + // myIcon.Dispose(); + // bs.Freeze(); // importantissimo se no fa memory leak + // DestroyIcon(shinfo.hIcon); + // CloseHandle(shinfo.hIcon); + // return bs; + + //} + + public static BitmapSource icon_of_path(string FileName, bool small, bool checkDisk, bool addOverlay) + { + SHFILEINFO shinfo = new SHFILEINFO(); + + uint SHGFI_USEFILEATTRIBUTES = 0x000000010; + uint SHGFI_LINKOVERLAY = 0x000008000; + + uint flags; + if (small) + { + flags = SHGFI_ICON | SHGFI_SMALLICON; + } + else + { + flags = SHGFI_ICON | SHGFI_LARGEICON; + } + if (!checkDisk) + flags |= SHGFI_USEFILEATTRIBUTES; + if (addOverlay) + flags |= SHGFI_LINKOVERLAY; + + var res = SHGetFileInfo(FileName, 0, ref shinfo, Marshal.SizeOf(shinfo), flags); + if (res == 0) + { + throw (new System.IO.FileNotFoundException()); + } + + var myIcon = System.Drawing.Icon.FromHandle(shinfo.hIcon); + + var bs = bitmap_source_of_icon(myIcon); + myIcon.Dispose(); + bs.Freeze(); // importantissimo se no fa memory leak + DestroyIcon(shinfo.hIcon); + CloseHandle(shinfo.hIcon); + return bs; + + } + + public static Icon icon_of_path_large(string FileName, bool jumbo, bool checkDisk) + { + + SHFILEINFO shinfo = new SHFILEINFO(); + + uint SHGFI_USEFILEATTRIBUTES = 0x000000010; + uint SHGFI_SYSICONINDEX = 0x4000; + + int FILE_ATTRIBUTE_NORMAL = 0x80; + + uint flags; + flags = SHGFI_SYSICONINDEX; + + if (!checkDisk) // This does not seem to work. If I try it, a folder icon is always returned. + flags |= SHGFI_USEFILEATTRIBUTES; + + var res = SHGetFileInfo(FileName, FILE_ATTRIBUTE_NORMAL, ref shinfo, Marshal.SizeOf(shinfo), flags); + if (res == 0) + { + throw (new System.IO.FileNotFoundException()); + } + var iconIndex = shinfo.iIcon; + + // Get the System IImageList object from the Shell: + Guid iidImageList = new Guid("46EB5926-582E-4017-9FDF-E8998DAA0950"); + + IImageList iml; + int size = jumbo ? SHIL_JUMBO : SHIL_EXTRALARGE; + var hres = SHGetImageList(size, ref iidImageList, out iml); // writes iml + //if (hres == 0) + //{ + // throw (new System.Exception("Error SHGetImageList")); + //} + + IntPtr hIcon = IntPtr.Zero; + int ILD_TRANSPARENT = 1; + hres = iml.GetIcon(iconIndex, ILD_TRANSPARENT, ref hIcon); + //if (hres == 0) + //{ + // throw (new System.Exception("Error iml.GetIcon")); + //} + + var myIcon = System.Drawing.Icon.FromHandle(hIcon); + var bs = bitmap_source_of_icon(myIcon); + myIcon.Dispose(); + bs.Freeze(); // very important to avoid memory leak + //DestroyIcon(hIcon); + //CloseHandle(hIcon); + + return myIcon; + + } + } +} \ No newline at end of file diff --git a/ViewModel/AppConfig.cs b/ViewModel/AppConfig.cs index 0eb178d..55c86b2 100644 --- a/ViewModel/AppConfig.cs +++ b/ViewModel/AppConfig.cs @@ -57,8 +57,22 @@ namespace GeekDesk.ViewModel private bool selfStartUp = true; //开机自启动设置 private bool selfStartUped = false; //是否已设置 + private bool pmModel = false; //性能模式 + #region GetSet + public bool PMModel + { + get + { + return pmModel; + } + set + { + pmModel = value; + OnPropertyChanged("PMModel"); + } + } public bool SelfStartUped { diff --git a/ViewModel/IconInfo.cs b/ViewModel/IconInfo.cs index 44dee28..ca86dfb 100644 --- a/ViewModel/IconInfo.cs +++ b/ViewModel/IconInfo.cs @@ -141,7 +141,8 @@ namespace GeekDesk.ViewModel { get { - return imageWidth; + // 为了兼容旧版 暂时使用默认 + return (int)MainWindowEnum.IMAGE_WIDTH; } set { @@ -154,7 +155,8 @@ namespace GeekDesk.ViewModel { get { - return imageHeight; + // 为了兼容旧版 暂时使用默认 + return (int)MainWindowEnum.IMAGE_HEIGHT; } set { diff --git a/packages.config b/packages.config index f79c0b0..7099fce 100644 --- a/packages.config +++ b/packages.config @@ -3,5 +3,5 @@ - + \ No newline at end of file