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