I'm making a explorer like app to browse files stored on my computer. My app will be in "Icon" view. The question is: How to display the icon of an exe file?
There are several ways to do that. The easiest is probably to add a reference to System.Drawing and take advantage of the Icon.ExtractAssociatedIcon method:
public static ImageSource GetIcon(string fileName)
{
Icon icon = Icon.ExtractAssociatedIcon(fileName);
return System.Windows.Interop.Imaging.CreateBitmapSourceFromHIcon(
icon.Handle,
new Int32Rect(icon.Width, icon.Height),
BitmapSizeOptions.FromEmptyOptions());
}
Another option, if you don't want to use System.Drawing, is to obtain the icon from the SHGetFileInfo API. It's more difficult, but also much more flexible; for instance you can get the icon of a file that doesn't exist, based on the extension (of course for an executable it's not very useful, since it would return the default icon for executable files).
public static ImageSource GetIcon(string path, bool smallIcon, bool isDirectory)
{
// SHGFI_USEFILEATTRIBUTES takes the file name and attributes into account if it doesn't exist
uint flags = SHGFI_ICON | SHGFI_USEFILEATTRIBUTES;
if (smallIcon)
flags |= SHGFI_SMALLICON;
uint attributes = FILE_ATTRIBUTE_NORMAL;
if (isDirectory)
attributes |= FILE_ATTRIBUTE_DIRECTORY;
SHFILEINFO shfi;
if (0 != SHGetFileInfo(
path,
attributes,
out shfi,
(uint)Marshal.SizeOf(typeof(SHFILEINFO)),
flags))
{
return System.Windows.Interop.Imaging.CreateBitmapSourceFromHIcon(
shfi.hIcon,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
return null;
}
[StructLayout(LayoutKind.Sequential)]
private 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")]
private static extern int SHGetFileInfo(string pszPath, uint dwFileAttributes, out SHFILEINFO psfi, uint cbFileInfo, uint flags);
private const uint FILE_ATTRIBUTE_READONLY = 0x00000001;
private const uint FILE_ATTRIBUTE_HIDDEN = 0x00000002;
private const uint FILE_ATTRIBUTE_SYSTEM = 0x00000004;
private const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
private const uint FILE_ATTRIBUTE_ARCHIVE = 0x00000020;
private const uint FILE_ATTRIBUTE_DEVICE = 0x00000040;
private const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;
private const uint FILE_ATTRIBUTE_TEMPORARY = 0x00000100;
private const uint FILE_ATTRIBUTE_SPARSE_FILE = 0x00000200;
private const uint FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400;
private const uint FILE_ATTRIBUTE_COMPRESSED = 0x00000800;
private const uint FILE_ATTRIBUTE_OFFLINE = 0x00001000;
private const uint FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000;
private const uint FILE_ATTRIBUTE_ENCRYPTED = 0x00004000;
private const uint FILE_ATTRIBUTE_VIRTUAL = 0x00010000;
private const uint SHGFI_ICON = 0x000000100; // get icon
private const uint SHGFI_DISPLAYNAME = 0x000000200; // get display name
private const uint SHGFI_TYPENAME = 0x000000400; // get type name
private const uint SHGFI_ATTRIBUTES = 0x000000800; // get attributes
private const uint SHGFI_ICONLOCATION = 0x000001000; // get icon location
private const uint SHGFI_EXETYPE = 0x000002000; // return exe type
private const uint SHGFI_SYSICONINDEX = 0x000004000; // get system icon index
private const uint SHGFI_LINKOVERLAY = 0x000008000; // put a link overlay on icon
private const uint SHGFI_SELECTED = 0x000010000; // show icon in selected state
private const uint SHGFI_ATTR_SPECIFIED = 0x000020000; // get only specified attributes
private const uint SHGFI_LARGEICON = 0x000000000; // get large icon
private const uint SHGFI_SMALLICON = 0x000000001; // get small icon
private const uint SHGFI_OPENICON = 0x000000002; // get open icon
private const uint SHGFI_SHELLICONSIZE = 0x000000004; // get shell size icon
private const uint SHGFI_PIDL = 0x000000008; // pszPath is a pidl
private const uint SHGFI_USEFILEATTRIBUTES = 0x000000010; // use passed dwFileAttribute
Another advantage of this approach is that it can also get the icon for a directory (see last edit in the code), which isn't possible with Icon.ExtractAssociatedIcon
Related
I have a small problem. I have method which register global hotkey, when I press hotkey, program call Action. In Action method I would like Show window, but don't work it.
This is my code:
MainWindow.cs:
_hotKeyRegistrator.Add(Modifier.Ctrl, Keys.A, () => {Show();}
HotkeyRegistrator.cs:
public class HotkeyRegistrator
{
private HwndSource _source;
private readonly WindowInteropHelper _windowInteropHelper;
private const int HotkeyId = 9000;
private const int WmHotkey = 0x0312;
private List<HotKey> _hotKeys;
public HotkeyRegistrator(Window window)
{
_windowInteropHelper = new WindowInteropHelper(window);
_source = HwndSource.FromHwnd(_windowInteropHelper.Handle);
_source?.AddHook(HwndHook);
_hotKeys = new List<HotKey>();
}
[DllImport("User32.dll")]
private static extern bool RegisterHotKey([In] IntPtr hWnd, [In] int id, [In] uint fsModifiers, [In] uint vk);
[DllImport("User32.dll")]
private static extern bool UnregisterHotKey([In] IntPtr hWnd, [In] int id);
public void Add(Modifiers modifier, Keys key, Action action)
=> _hotKeys.Add(new HotKey(HotkeyId + _hotKeys.Count, modifier, key, action));
public void Register()
{
foreach (var hotKey in _hotKeys)
{
if (!RegisterHotKey(_windowInteropHelper.Handle, hotKey.Id, hotKey.Modifier, hotKey.Key))
{
throw new Exception("Cannot register hotkey");
}
}
}
public void UnRegisterAll()
{
_source.RemoveHook(HwndHook);
_source = null;
foreach (var hotKey in _hotKeys)
{
UnregisterHotKey(_windowInteropHelper.Handle, hotKey.Id);
}
_hotKeys = null;
}
private IntPtr HwndHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WmHotkey)
_hotKeys.FirstOrDefault(x => x.Id == wParam.ToInt32())?.Action.Invoke();
return IntPtr.Zero;
}
}
Hotkey.cs:
public class HotKey
{
public int Id { get; set; }
public uint Modifier { get; }
public uint Key { get; }
public Action Action { get; }
public HotKey(int id, Modifiers modifier, Keys key, Action action)
{
Id = id;
Modifier = (uint)modifier;
Key = (uint)key;
Action = action;
}
}
Action method is called using Invoke method. When I press CTRL + A, call action and Show method, but window dont open.
Thanks for advices.
Problem solved. My window has set WindowState="Minimized" attribute on startup. When I would like call Show() after press global shortcut, code must be this:
_hotKeyRegistrator.Add(Modifier.Ctrl, Keys.A, () => {
WindowState = WindowState.Normal;
Show();
});
Disclaimer: This is not a duplicated post. I googled about the issue. Also read this, this and this SO questions. I tried some of those things but nothing seemed to help.
Consider the following simple example code. It's just an empty ElementHost inside a WinForm (no WPF control inside):
using System.Windows.Forms;
using System.Windows.Forms.Integration;
namespace WindowsFormsApplication15
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
ElementHost host = new ElementHost();
host.Dock = DockStyle.Fill;
this.Controls.Add(host);
}
}
}
When you resize the form, you can see two black edges at the form border:
Please, ¿someone could give a working solution over my example to fix this issue?
Try this (same idea as the first link you provided, but better performance):
public class ElementHost2 : ElementHost {
public ElementHost2() {
this.AutoSize = true;
}
public override Size GetPreferredSize(Size proposedSize) {
Form f = this.FindForm();
Size s = f.ClientSize;
return s;
}
private const uint WM_SETREDRAW = 0xB;
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
private const uint NOSIZE = 0x0001;
private const uint NOMOVE = 0x0002;
private const uint NOZORDER = 0x0004;
private const uint NOREDRAW = 0x0008;
private const uint NOACTIVATE = 0x0010;
private const uint DRAWFRAME = 0x0020;
private const uint FRAMECHANGED = 0x0020;
private const uint SHOWWINDOW = 0x0040;
private const uint HIDEWINDOW = 0x0080;
private const uint NOCOPYBITS = 0x0100;
private const uint NOOWNERZORDER = 0x0200;
private const uint NOREPOSITION = 0x0200;
private const uint NOSENDCHANGING = 0x0400;
private const uint DEFERERASE = 0x2000;
private const uint ASYNCWINDOWPOS = 0x4000;
protected override void OnResize(EventArgs e) {
base.OnResize(e);
SendMessage(this.Handle, WM_SETREDRAW, 0, 0);
SendMessage(this.Handle, WM_SETREDRAW, 1, 0);
// forces window to redraw:
SetWindowPos(this.Handle, IntPtr.Zero, 0, 0, 0, 0, NOSIZE | NOMOVE| NOZORDER | NOACTIVATE | SHOWWINDOW);
}
// better performance?
protected override void OnPaintBackground(PaintEventArgs pevent) {
//base.OnPaintBackground(pevent);
}
protected override void OnPaint(PaintEventArgs e) {
//base.OnPaint(e);
}
}
class Form2 : Form {
ElementHost host = new ElementHost2();
public Form2() {
Controls.Add(host);
this.BackColor = Color.Red;
var p = new System.Windows.Controls.DockPanel();
p.Background = System.Windows.Media.Brushes.Red;
host.Child = p;
p.Children.Add(new System.Windows.Controls.TextBox { Width = 100, Height = 20 });
}
}
The issue is not related to ElementHost and Winforms. It's just a WPF issue, and I found the answer in the following SO question:
How to fix the WPF form resize - controls lagging behind and black background?
How can I control the initial WindowState (Normal, Minimized, Maximized) of a WPF main window from a desktop shortcut?
The "Run:" combobox of the shortcut's properties dialog let's me choose between "Normal window", "Minimized" and "Maximized". But this option seems to be completely ignored by WPF apps. With WinForms this was automatically supported with no additional code.
Is there a way to access this option from the launched WPF process? I know I can specify the ProcessStartInfo.WindowStyle property when launching new processes. But how can I access this option from the process being launched?
System.Diagnostics.Process.GetCurrentProcess().StartInfo.WindowStyle
Use NativeMethods.StartupInfo.GetInitialWindowStyle() to get just the initial window state or NativeMethods.StartupInfo.FromCurrentProcess to access the entire information.
static partial class NativeMethods
{
public static class StartupInfo
{
[StructLayout(LayoutKind.Sequential)]
public class STARTUPINFO
{
public readonly UInt32 cb;
private IntPtr lpReserved;
[MarshalAs(UnmanagedType.LPWStr)] public readonly string lpDesktop;
[MarshalAs(UnmanagedType.LPWStr)] public readonly string lpTitle;
public readonly UInt32 dwX;
public readonly UInt32 dwY;
public readonly UInt32 dwXSize;
public readonly UInt32 dwYSize;
public readonly UInt32 dwXCountChars;
public readonly UInt32 dwYCountChars;
public readonly UInt32 dwFillAttribute;
public readonly UInt32 dwFlags;
[MarshalAs(UnmanagedType.U2)] public readonly UInt16 wShowWindow;
[MarshalAs(UnmanagedType.U2)] public readonly UInt16 cbReserved2;
private IntPtr lpReserved2;
public readonly IntPtr hStdInput;
public readonly IntPtr hStdOutput;
public readonly IntPtr hStdError;
}
public readonly static STARTUPINFO FromCurrentProcess = null;
const uint STARTF_USESHOWWINDOW = 0x00000001;
const ushort SW_HIDE = 0;
const ushort SW_SHOWNORMAL = 1;
const ushort SW_SHOWMINIMIZED = 2;
const ushort SW_SHOWMAXIMIZED = 3;
const ushort SW_MINIMIZE = 6;
const ushort SW_SHOWMINNOACTIVE = 7;
const ushort SW_FORCEMINIMIZE = 11;
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern void GetStartupInfoW(IntPtr startupInfoPtr);
static StartupInfo() //Static constructor
{
FromCurrentProcess = new STARTUPINFO();
int length = Marshal.SizeOf(typeof(STARTUPINFO));
IntPtr ptr = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(FromCurrentProcess, ptr, false);
GetStartupInfoW(ptr);
Marshal.PtrToStructure(ptr, FromCurrentProcess);
Marshal.FreeHGlobal(ptr);
}
public static ProcessWindowStyle GetInitialWindowStyle()
{
if ((FromCurrentProcess.dwFlags & STARTF_USESHOWWINDOW) == 0) return ProcessWindowStyle.Normal;
switch (FromCurrentProcess.wShowWindow)
{
case SW_HIDE: return ProcessWindowStyle.Hidden;
case SW_SHOWNORMAL: return ProcessWindowStyle.Normal;
case SW_MINIMIZE:
case SW_FORCEMINIMIZE:
case SW_SHOWMINNOACTIVE:
case SW_SHOWMINIMIZED: return ProcessWindowStyle.Minimized;
case SW_SHOWMAXIMIZED: return ProcessWindowStyle.Maximized;
default: return ProcessWindowStyle.Normal;
}
}
}
}
I have the following code, which adds an 'Always on Top' item to the system context menu as displayed on the window chrome. It works correctly, but I'd like it to display a check mark or similar to indicate if it's been toggled on/off.
Any idea how I can do this?
public RibbonShell()
{
InitializeComponent();
Loaded += (s,e) =>
{
// Get the Handle for the Forms System Menu
var systemMenuHandle = GetSystemMenu(Handle, false);
// Create our new System Menu items just before the Close menu item
InsertMenu(systemMenuHandle, 5, MfByposition | MfSeparator, 0, string.Empty); // <-- Add a menu seperator
InsertMenu(systemMenuHandle, 6, MfByposition, SettingsSysMenuId, "Always on Top");
// Attach our WindowCommandHandler handler to this Window
var source = HwndSource.FromHwnd(Handle);
source.AddHook(WindowCommandHandler);
};
}
#region Win32 API Stuff
// Define the Win32 API methods we are going to use
[DllImport("user32.dll")]
private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[DllImport("user32.dll")]
private static extern bool InsertMenu(IntPtr hMenu, Int32 wPosition, Int32 wFlags, Int32 wIDNewItem, string lpNewItem);
/// Define our Constants we will use
private const int WmSyscommand = 0x112;
private const int MfSeparator = 0x800;
private const int MfByposition = 0x400;
#endregion
// The constants we'll use to identify our custom system menu items
private const int SettingsSysMenuId = 1000;
/// <summary>
/// This is the Win32 Interop Handle for this Window
/// </summary>
public IntPtr Handle
{
get { return new WindowInteropHelper(this).Handle; }
}
private IntPtr WindowCommandHandler(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
// Check if a System Command has been executed
if (msg == WmSyscommand && wParam.ToInt32() == SettingsSysMenuId)
{
Topmost = !Topmost;
handled = true;
}
return IntPtr.Zero;
}
You need to call CheckMenuItem whenever you change Topmost. See the CheckMenuItem documentaton for details. Here's the P/Invoke signature and constants you'll need:
[DllImport("user32.dll")]
private static extern bool CheckMenuItem(IntPtr hMenu, Int32 uIDCheckItem, Int32 uCheck);
private const int MfChecked = 8;
private const int MfUnchecked = 0;
Now to check the item, just:
CheckMenuItem(systemMenuHandle, SettingsSysMenuId, MfChecked);
and to uncheck:
CheckMenuItem(systemMenuHandle, SettingsSysMenuId, MfUnchecked);
how to open an file's Properties dialog by a button
private void button_Click(object sender, EventArgs e)
{
string path = #"C:\Users\test\Documents\tes.text";
// how to open this propertie
}
Thank you.
For example if want the System properties
Process.Start("sysdm.cpl");
But how do i get the Properties dialog for a file path?
Solution is:
using System.Runtime.InteropServices;
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
static extern bool ShellExecuteEx(ref SHELLEXECUTEINFO lpExecInfo);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct SHELLEXECUTEINFO
{
public int cbSize;
public uint fMask;
public IntPtr hwnd;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpVerb;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpFile;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpParameters;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpDirectory;
public int nShow;
public IntPtr hInstApp;
public IntPtr lpIDList;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpClass;
public IntPtr hkeyClass;
public uint dwHotKey;
public IntPtr hIcon;
public IntPtr hProcess;
}
private const int SW_SHOW = 5;
private const uint SEE_MASK_INVOKEIDLIST = 12;
public static bool ShowFileProperties(string Filename)
{
SHELLEXECUTEINFO info = new SHELLEXECUTEINFO();
info.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(info);
info.lpVerb = "properties";
info.lpFile = Filename;
info.nShow = SW_SHOW;
info.fMask = SEE_MASK_INVOKEIDLIST;
return ShellExecuteEx(ref info);
}
// button click
private void button1_Click(object sender, EventArgs e)
{
string path = #"C:\Users\test\Documents\test.text";
ShowFileProperties(path);
}
Call Process.Start, passing a ProcessStartInfo containing the name of the file, and with the ProcessStartInfo.Verb set to properties. (For more info, see the description of the unmanaged SHELLEXECUTEINFO structure, which is what ProcessStartInfo wraps, and in particular the lpVerb member.)
Various file properties are available from the FileInfo class:
FileInfo info = new FileInfo(path);
Console.WriteLine(info.CreationTime);
Console.WriteLine(info.Attributes);
...
Solution is to use ShellExecute () api.
How to invoke this api using C# :
http://weblogs.asp.net/rchartier/442339
This works fine for me without CharSet attribute both in Debug and Release mode.
To simplify handling Shell32 stuff and such, you could also use Vanara like:
using Vanara.PInvoke;
using System.Runtime.InteropServices;
// ...
void ShowProperties(string filepath)
{
var info = new Shell32.SHELLEXECUTEINFO();
info.cbSize = Marshal.SizeOf(info);
info.lpVerb = "properties";
info.lpFile = filepath;
info.nShellExecuteShow = ShowWindowCommand.SW_SHOW;
info.fMask = Shell32.ShellExecuteMaskFlags.SEE_MASK_INVOKEDLIST;
Shell32.ShellExecuteEx(ref i);
}
and call it like:
ShowProperties(#"C:\The\Path\To\The\File.txt");