Date: 26.08.2019 12:14:49
Например так:
using System; using System.IO; using System.Collections.Generic; using System.Text; using System.Windows; using System.Windows.Controls; using System.Runtime.InteropServices; using System.Threading.Tasks; using System.Net; namespace WpfApplication1 { public partial class MainWindow : Window { const string prefix = "MyComputer"; const int port = 8080; ShellItemsHttpListener listener; public static Guid FOLDERID_ComputerFolder = new Guid("0AC0837C-BBF8-452A-850D-79D08E667CA7"); public MainWindow() { InitializeComponent(); List<ShellItem> items = GetItems(FOLDERID_ComputerFolder); //получаем список элементов каталога listener = new ShellItemsHttpListener(items, prefix, port); listener.Start(); //запускаем HTTP-сервер webbrowser.Navigate("http://localhost:" + port.ToString() + "/" + prefix + "/"); //открываем страницу в WebBrowser webbrowser.Navigating += webbrowser_Navigating; } void webbrowser_Navigating(object sender, System.Windows.Navigation.NavigatingCancelEventArgs e) { //обработка перенаправлений URL с http:// на file:// Uri uri = listener.ProcessRedirect(e.Uri.AbsoluteUri); if (uri != null) { webbrowser.Navigate(uri); e.Cancel = true; } } private void bHome_Click(object sender, RoutedEventArgs e) { webbrowser.Navigate("http://localhost:" + port.ToString() + "/" + prefix + "/"); } public static List<ShellItem> GetItems(Guid FolderID) //Получает список элементов каталога по GUID { IntPtr p = IntPtr.Zero; IShellFolder pFolder = null; IEnumIDList pEnum = null; IntPtr pItem = IntPtr.Zero; IntPtr lpStr = IntPtr.Zero; STRRET strret; Guid guid = typeof(IShellFolder).GUID; List<ShellItem> items = new List<ShellItem>(); ShellItem si; try { int hr = SHGetKnownFolderIDList(ref FolderID, 0, IntPtr.Zero, out p); if (hr != 0) throw Marshal.GetExceptionForHR(hr); hr = SHBindToObject(null, p, null, ref guid, out pFolder); if (hr != 0) throw Marshal.GetExceptionForHR(hr); pFolder.EnumObjects(IntPtr.Zero, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, out pEnum); while (true) { pItem = IntPtr.Zero; uint res = pEnum.Next(1, out pItem, IntPtr.Zero); if (res != 0) break; si = new ShellItem(); //display name lpStr = IntPtr.Zero; strret = new STRRET(); pFolder.GetDisplayNameOf(pItem, SHGDN_NORMAL, out strret); hr = StrRetToStr(ref strret, pItem, out lpStr); if (hr != 0) throw Marshal.GetExceptionForHR(hr); string s = Marshal.PtrToStringUni(lpStr); si.DisplayName = s; CoTaskMemFree(lpStr); //path lpStr = IntPtr.Zero; strret = new STRRET(); pFolder.GetDisplayNameOf(pItem, SHGDN_FORPARSING, out strret); hr = StrRetToStr(ref strret, pItem, out lpStr); if (hr != 0) throw Marshal.GetExceptionForHR(hr); s = Marshal.PtrToStringUni(lpStr); try { si.Path = new Uri(s); } catch (UriFormatException) { si.Path = new Uri("file://localhost/"); } CoTaskMemFree(lpStr); //icon Guid iid_IIExtractIcon = typeof(IExtractIcon).GUID; IExtractIcon pExtract; pFolder.GetUIObjectOf(IntPtr.Zero, 1, new IntPtr[] { pItem }, ref iid_IIExtractIcon, 0, out pExtract); StringBuilder sbIcon = new StringBuilder(260); int index=0; uint flags; hr = pExtract.GetIconLocation(GIL_FORSHELL, sbIcon, 260, out index, out flags); if (hr == 0) { IntPtr hIconSmall = IntPtr.Zero, hIconLarge = IntPtr.Zero; hr = pExtract.Extract(sbIcon.ToString(), (uint)index, out hIconLarge, out hIconSmall, 0x00140014); if (hr == 0 && hIconSmall != IntPtr.Zero) { var icon = System.Drawing.Icon.FromHandle(hIconSmall); var bitmap = icon.ToBitmap(); using (bitmap) { MemoryStream ms = new MemoryStream(); bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Png); si.Image = ms.ToArray(); } DestroyIcon(hIconSmall); DestroyIcon(hIconLarge); } else { si.Image = new byte[0]; } } else { si.Image = new byte[0]; } items.Add(si); CoTaskMemFree(pItem); } return items; } finally { if (p != IntPtr.Zero) CoTaskMemFree(p); if (pFolder != null) Marshal.ReleaseComObject(pFolder); if (pEnum != null) Marshal.ReleaseComObject(pEnum); } } public const uint SHGFI_DISPLAYNAME = 0x000000200; public const uint SHGFI_ICON = 0x000000100; public const uint SHGFI_PIDL = 0x000000008; public const uint GIL_FORSHELL = 0x0002; public const int SHGDN_NORMAL = 0x0000; public const int SHGDN_FORPARSING = 0x8000; public const int SHCONTF_FOLDERS = 0x0020; public const int SHCONTF_NONFOLDERS = 0x0040; [DllImport("shell32.dll")] public static extern int SHGetKnownFolderIDList(ref Guid rfid, int dwFlags, IntPtr hToken, out IntPtr ppidl); [DllImport("ole32.dll")] static extern void CoTaskMemFree(IntPtr pv); [DllImport("shell32.dll")] static extern int SHBindToObject( IShellFolder psf, IntPtr pidl, [MarshalAs(UnmanagedType.IUnknown)] object pbc, ref Guid riid, out IShellFolder ppv); [DllImport("Shlwapi.dll", CharSet = CharSet.Unicode)] static extern int StrRetToStr( ref STRRET pstr, IntPtr pidl, out IntPtr ppsz); [DllImport("user32.dll")] static extern bool DestroyIcon(IntPtr hIcon); } public struct ShellItem { public string DisplayName { get; set; } public Uri Path { get; set; } public byte[] Image { get; set; } } public class ShellItemsHttpListener //возвращает содержимое каталога в виде веб-страницы { Dictionary<string, byte[]> images; //таблица изображений Dictionary<string, Uri> redirects; //таблица редиректов string html; string urlprefix; int httpport; HttpListener listener; public ShellItemsHttpListener(List<ShellItem> items, string prefix, int port) { StringBuilder sb = new StringBuilder(); int n = 1; //генерируем веб-страницу... //устанавливаем режим IE8, чтобы работали стили sb.Append("<html><head><meta charset=\"utf-8\"><meta http-equiv=\"X-UA-Compatible\" content=\"IE=8\" /><style>"); //устанавливаем стили, имитирующий внешний вид проводника (в режиме просмотра "Список") sb.Append("a { color: black; text-decoration: none; } a:hover { color: black; text-decoration: underline;} "); sb.Append("tr:hover { background-color: lightblue;} img {border:none;}"); sb.Append("</style></head><body>"); //формируем список элементов sb.Append("<table cellspacing=\"0\" border=\"0\">"); images = new Dictionary<string, byte[]>(); redirects = new Dictionary<string, Uri>(); foreach (ShellItem item in items) { sb.Append("<tr><td><a href=\"" + n.ToString() + ".html\" >"); sb.Append("<img width=\"20\" height=\"20\" src=\"" + n.ToString() + ".png\"/></a></td>"); sb.Append("<td><a href=\"" + n.ToString() + ".html\" >"); sb.Append(item.DisplayName + "</a></td></tr>"); images[n.ToString() + ".png"] = item.Image; redirects[n.ToString() + ".html"] = item.Path; n++; } sb.Append("</table></body></html>"); html = sb.ToString(); //инициализация HttpListener... listener = new HttpListener(); listener.Prefixes.Add("http://localhost:"+port.ToString()+"/" + prefix+"/"); urlprefix = prefix; httpport = port; } public void Start() //запускает HTTP-сервер { Task.Run(() => { try { listener.Start(); while (true) { HttpListenerContext context = listener.GetContext(); //ожидаем запроса HttpListenerRequest request = context.Request; HttpListenerResponse response = context.Response; //анализ URL... byte[] buffer = new byte[0]; string urlbegin = "/" + urlprefix + "/"; string urlfile = request.RawUrl.Substring(urlbegin.Length); if (urlfile == "" || urlfile.StartsWith("index") || urlfile.StartsWith("default")) { //отдаем основную страницу buffer = System.Text.Encoding.UTF8.GetBytes(html); response.ContentLength64 = buffer.Length; response.ContentEncoding = Encoding.UTF8; } else if (urlfile.EndsWith(".png")) { //отдаем изображение buffer = images[urlfile]; response.ContentLength64 = buffer.Length; response.ContentType = "image/png"; } else { buffer = System.Text.Encoding.UTF8.GetBytes("Ошибка: Страница не найдена"); response.StatusCode = 404; response.ContentType = "text/plain"; } //возвращаем ответ System.IO.Stream output = response.OutputStream; output.Write(buffer, 0, buffer.Length); output.Close(); } } catch (Exception ex) { MessageBox.Show(ex.ToString(), "HttpListener error"); } }); } public Uri ProcessRedirect(string inputurl) { //обработка перенаправлений... string urlbegin = "http://localhost:" + httpport.ToString() + "/" + urlprefix + "/"; if (inputurl.StartsWith(urlbegin)) { string urlfile = inputurl.Substring(urlbegin.Length); if (!urlfile.EndsWith(".html")) return null; if (redirects.ContainsKey(urlfile)) { return redirects[urlfile]; } else return null; } else return null; } } /* COM Interfaces */ [ComImport] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [Guid("000214E6-0000-0000-C000-000000000046")] public interface IShellFolder { void ParseDisplayName(IntPtr hwnd, IntPtr pbc, String pszDisplayName, UInt32 pchEaten, out IntPtr ppidl, UInt32 pdwAttributes); void EnumObjects(IntPtr hwnd, int grfFlags, out IEnumIDList ppenumIDList); void BindToObject(IntPtr pidl, IntPtr pbc, [In]ref Guid riid, out IntPtr ppv); void BindToStorage(IntPtr pidl, IntPtr pbc, [In]ref Guid riid, out IntPtr ppv); [PreserveSig] Int32 CompareIDs(Int32 lParam, IntPtr pidl1, IntPtr pidl2); void CreateViewObject(IntPtr hwndOwner, [In] ref Guid riid, out IntPtr ppv); void GetAttributesOf(UInt32 cidl, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]IntPtr[] apidl, ref uint rgfInOut); void GetUIObjectOf(IntPtr hwndOwner, UInt32 cidl, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]IntPtr[] apidl, [In] ref Guid riid, UInt32 rgfReserved, out IExtractIcon ppv); void GetDisplayNameOf(IntPtr pidl, int uFlags, out STRRET pName); void SetNameOf(IntPtr hwnd, IntPtr pidl, String pszName, int uFlags, out IntPtr ppidlOut); } [StructLayout(LayoutKind.Explicit, Size = 520)] public struct STRRETinternal { [FieldOffset(0)] public IntPtr pOleStr; [FieldOffset(0)] public IntPtr pStr; [FieldOffset(0)] public uint uOffset; } [StructLayout(LayoutKind.Sequential)] public struct STRRET { public uint uType; public STRRETinternal data; } [ComImport] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [Guid("000214F2-0000-0000-C000-000000000046")] public interface IEnumIDList { [PreserveSig()] uint Next(uint celt, out IntPtr rgelt, IntPtr pceltFetched); [PreserveSig()] uint Skip(uint celt); [PreserveSig()] uint Reset(); [PreserveSig()] uint Clone(out IEnumIDList ppenum); } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 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; } [ComImport()] [Guid("000214fa-0000-0000-c000-000000000046")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IExtractIcon { [PreserveSig] int GetIconLocation( uint uFlags, [Out, MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 2)] StringBuilder szIconFile, int cchMax, out int piIndex, out uint pwFlags); [PreserveSig] int Extract( [MarshalAs(UnmanagedType.LPWStr)] string pszFile, uint nIconIndex, out IntPtr phiconLarge, out IntPtr phiconSmall, uint nIconSize); } }
Источник: https://devblogs.microsoft.com/oldnewthing/?p=9773
Это, конечно, не настоящий "Мой компьютер", но подделано довольно качественно:
Картину только портит багнутый "Мои веб-узлы MSN", который ведет в никуда и для которого не работает извлечение иконки. Извращения с обработкой webbrowser_Navigating здесь нужны из-за того, что настройки безопасности не позволяют просто так переходить с http://localhost/ на file://localhost/
Автор: VadimTagil