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;
}
}
}
}
Related
I'm writing a windows service, which will manage some agent program capturing screenshots. Agent program works fine, no problems at all - it just takes screenshot and saves it into bmp file.
But when i try to execute this agent program from my service - it doesnt't work, all i get is black pictures (as if i tried to capture screenshot directly from my service)
My code inside service is:
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus (hStatus, &ServiceStatus);
SHELLEXECUTEINFO seInfo;
seInfo.cbSize = sizeof(SHELLEXECUTEINFO);
seInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
seInfo.hwnd = NULL;
seInfo.lpVerb = TEXT("open");
seInfo.lpFile = TEXT("D:\\dev\\work\\agent.exe");
seInfo.lpDirectory = TEXT("D:\\dev\\work\\");
seInfo.nShow = SW_SHOWNORMAL;
seInfo.hInstApp = NULL;
ShellExecuteEx(&seInfo);
I tried to change service properties - to allow it interacting with desktop, change service user from SYSTEM to my local account - nothing helpes.
What can i do to make it work properely?
UPD. Well, i suppose, this happenes because my service executes program in session0. I tried CreateProcess() and CreateProcessAsUser() as well - no result. So how can i create procees NOT in session0?
Aliostad did all the heavy lifting for this. Take a look at his code, it's an excellent start. Keep in mind, you'll need a separate app to take the screenshot since a new process has to be kicked off while impersonating the logged in user.
https://stackoverflow.com/a/4147868/125406
Here is my version of it (fully based on Aliostad's code). It adds command line params, a wait for the process to finish and return of the exit code.
public static class ProcessAsCurrentUser
{
/// <summary>
/// Connection state of a session.
/// </summary>
public enum ConnectionState
{
/// <summary>
/// A user is logged on to the session.
/// </summary>
Active,
/// <summary>
/// A client is connected to the session.
/// </summary>
Connected,
/// <summary>
/// The session is in the process of connecting to a client.
/// </summary>
ConnectQuery,
/// <summary>
/// This session is shadowing another session.
/// </summary>
Shadowing,
/// <summary>
/// The session is active, but the client has disconnected from it.
/// </summary>
Disconnected,
/// <summary>
/// The session is waiting for a client to connect.
/// </summary>
Idle,
/// <summary>
/// The session is listening for connections.
/// </summary>
Listening,
/// <summary>
/// The session is being reset.
/// </summary>
Reset,
/// <summary>
/// The session is down due to an error.
/// </summary>
Down,
/// <summary>
/// The session is initializing.
/// </summary>
Initializing
}
[StructLayout(LayoutKind.Sequential)]
class SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct STARTUPINFO
{
public Int32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
enum LOGON_TYPE
{
LOGON32_LOGON_INTERACTIVE = 2,
LOGON32_LOGON_NETWORK,
LOGON32_LOGON_BATCH,
LOGON32_LOGON_SERVICE,
LOGON32_LOGON_UNLOCK = 7,
LOGON32_LOGON_NETWORK_CLEARTEXT,
LOGON32_LOGON_NEW_CREDENTIALS
}
enum LOGON_PROVIDER
{
LOGON32_PROVIDER_DEFAULT,
LOGON32_PROVIDER_WINNT35,
LOGON32_PROVIDER_WINNT40,
LOGON32_PROVIDER_WINNT50
}
[Flags]
enum CreateProcessFlags : uint
{
CREATE_BREAKAWAY_FROM_JOB = 0x01000000,
CREATE_DEFAULT_ERROR_MODE = 0x04000000,
CREATE_NEW_CONSOLE = 0x00000010,
CREATE_NEW_PROCESS_GROUP = 0x00000200,
CREATE_NO_WINDOW = 0x08000000,
CREATE_PROTECTED_PROCESS = 0x00040000,
CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000,
CREATE_SEPARATE_WOW_VDM = 0x00000800,
CREATE_SHARED_WOW_VDM = 0x00001000,
CREATE_SUSPENDED = 0x00000004,
CREATE_UNICODE_ENVIRONMENT = 0x00000400,
DEBUG_ONLY_THIS_PROCESS = 0x00000002,
DEBUG_PROCESS = 0x00000001,
DETACHED_PROCESS = 0x00000008,
EXTENDED_STARTUPINFO_PRESENT = 0x00080000,
INHERIT_PARENT_AFFINITY = 0x00010000
}
[StructLayout(LayoutKind.Sequential)]
public struct WTS_SESSION_INFO
{
public int SessionID;
[MarshalAs(UnmanagedType.LPTStr)] public string WinStationName;
public ConnectionState State;
}
[DllImport("wtsapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Int32 WTSEnumerateSessions(IntPtr hServer, int reserved, int version,
ref IntPtr sessionInfo, ref int count);
[DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUserW", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool CreateProcessAsUser(
IntPtr hToken,
string lpApplicationName,
string lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
bool bInheritHandles,
UInt32 dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern Int32 WaitForSingleObject(IntPtr handle, Int32 wait);
public const Int32 INFINITE = -1;
public const Int32 WAIT_ABANDONED = 0x80;
public const Int32 WAIT_OBJECT_0 = 0x00;
public const Int32 WAIT_TIMEOUT = 0x102;
public const Int32 WAIT_FAILED = -1;
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool GetExitCodeProcess(IntPtr hProcess, out uint exitCode);
[DllImport("wtsapi32.dll")]
public static extern void WTSFreeMemory(IntPtr memory);
[DllImport("kernel32.dll")]
private static extern UInt32 WTSGetActiveConsoleSessionId();
[DllImport("wtsapi32.dll", SetLastError = true)]
static extern int WTSQueryUserToken(UInt32 sessionId, out IntPtr Token);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public extern static bool DuplicateTokenEx(
IntPtr hExistingToken,
uint dwDesiredAccess,
IntPtr lpTokenAttributes,
int ImpersonationLevel,
int TokenType,
out IntPtr phNewToken);
private const int TokenImpersonation = 2;
private const int SecurityIdentification = 1;
private const int MAXIMUM_ALLOWED = 0x2000000;
private const int TOKEN_DUPLICATE = 0x2;
private const int TOKEN_QUERY = 0x00000008;
/// <summary>
/// Launches a process for the current logged on user if there are any.
/// If none, return false as well as in case of
///
/// ##### !!! BEWARE !!! #### ------------------------------------------
/// This code will only work when running in a windows service (where it is really needed)
/// so in case you need to test it, it needs to run in the service. Reason
/// is a security privileg which only services have (SE_??? something, cant remember)!
/// </summary>
/// <param name="processExe"></param>
/// <returns></returns>
public static uint CreateProcessAsCurrentUser(string processExe, string commandLine)
{
IntPtr duplicate = new IntPtr();
STARTUPINFO info = new STARTUPINFO();
PROCESS_INFORMATION procInfo = new PROCESS_INFORMATION();
Debug.WriteLine(string.Format("CreateProcessAsCurrentUser. processExe: " + processExe));
IntPtr p = GetCurrentUserToken();
bool result = DuplicateTokenEx(p, MAXIMUM_ALLOWED | TOKEN_QUERY | TOKEN_DUPLICATE, IntPtr.Zero,
SecurityIdentification, SecurityIdentification, out duplicate);
Debug.WriteLine(string.Format("DuplicateTokenEx result: {0}", result));
Debug.WriteLine(string.Format("duplicate: {0}", duplicate));
if (result)
{
//NOTE: CREATE_NO_WINDOW will hide the console
//If there are commandline options, pass them AND the exe in
//commandLine and leave processExe empty.
result = CreateProcessAsUser(duplicate, processExe, commandLine,
// IntPtr.Zero, IntPtr.Zero, false, (UInt32) CreateProcessFlags.CREATE_NEW_CONSOLE, IntPtr.Zero, null,
IntPtr.Zero, IntPtr.Zero, false, (UInt32)CreateProcessFlags.CREATE_NO_WINDOW, IntPtr.Zero, null,
ref info, out procInfo);
Debug.WriteLine(string.Format("CreateProcessAsUser result: {0}", result));
}
if (p.ToInt32() != 0)
{
Marshal.Release(p);
Debug.WriteLine(string.Format("Released handle p: {0}", p));
}
if (duplicate.ToInt32() != 0)
{
Marshal.Release(duplicate);
Debug.WriteLine(string.Format("Released handle duplicate: {0}", duplicate));
}
//Wait for the process to complete
WaitForSingleObject(procInfo.hProcess, (int)INFINITE);
//Get and return the exit code
uint exitcode;
GetExitCodeProcess(procInfo.hProcess, out exitcode);
return exitcode;
}
public static int GetCurrentSessionId()
{
uint sessionId = WTSGetActiveConsoleSessionId();
Debug.WriteLine(string.Format("sessionId: {0}", sessionId));
if (sessionId == 0xFFFFFFFF)
return -1;
else
return (int)sessionId;
}
public static bool IsUserLoggedOn()
{
List<WTS_SESSION_INFO> wtsSessionInfos = ListSessions();
Debug.WriteLine(string.Format("Number of sessions: {0}", wtsSessionInfos.Count));
int activeSessionCount = 0;
foreach (var session in wtsSessionInfos)
{
if (session.State == ConnectionState.Active)
activeSessionCount++;
}
//return wtsSessionInfos.Where(x => x.State == ConnectionState.Active).Count() > 0;
return activeSessionCount > 0;
}
private static IntPtr GetCurrentUserToken()
{
List<WTS_SESSION_INFO> wtsSessionInfos = ListSessions();
int sessionId = 0;
foreach (var session in wtsSessionInfos)
{
if (session.State == ConnectionState.Active)
{
sessionId = session.SessionID;
break;
}
}
//Old Linq method
//int sessionId = wtsSessionInfos.Where(x => x.State == ConnectionState.Active).FirstOrDefault().SessionID;
//int sessionId = GetCurrentSessionId();
Debug.WriteLine(string.Format("sessionId: {0}", sessionId));
if (sessionId == int.MaxValue)
{
return IntPtr.Zero;
}
else
{
IntPtr p = new IntPtr();
int result = WTSQueryUserToken((UInt32)sessionId, out p);
Debug.WriteLine(string.Format("WTSQueryUserToken result: {0}", result));
Debug.WriteLine(string.Format("WTSQueryUserToken p: {0}", p));
return p;
}
}
public static List<WTS_SESSION_INFO> ListSessions()
{
IntPtr server = IntPtr.Zero;
List<WTS_SESSION_INFO> ret = new List<WTS_SESSION_INFO>();
try
{
IntPtr ppSessionInfo = IntPtr.Zero;
Int32 count = 0;
Int32 retval = WTSEnumerateSessions(IntPtr.Zero, 0, 1, ref ppSessionInfo, ref count);
Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
Int64 current = (int)ppSessionInfo;
if (retval != 0)
{
for (int i = 0; i < count; i++)
{
WTS_SESSION_INFO si =
(WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)current,
typeof(WTS_SESSION_INFO));
current += dataSize;
ret.Add(si);
}
WTSFreeMemory(ppSessionInfo);
}
}
catch (Exception exception)
{
Debug.WriteLine(exception.ToString());
}
return ret;
}
}
We pass a filename as a parameter to the screenshot app and then wait for it to complete. The service then picks up the file and processes and deletes it.
We also found another way around this using named pipes. I'm less familiar with that solution.
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?
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
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");