Drawing On Top Of TitleBar (With Graphics.FromHWnd()) - c#

I am attempting to overlay a status message on an external application.
Previously, I achieved this by using a TransparencyKey on a Form and the following API call to get the window location with a hook to capture the window moved event.
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(IntPtr hWnd, ref Rect lpRect);
This works on my development machine, however, on the target machine it fails, the transparent area actually has hatch markings over it. Presumably something to do with that machine being a VM and having no hardware graphics accelleration.
So, I've done away with the Form and have the following code (with DrawFrame() being called every 60ms) to draw manually to the screen:
class DirectDisplay:IDisposable
{
private Graphics window;
private SolidBrush b;
private string Status;
private NativeMethods.Rect location;
private bool running;
public DirectDisplay(IntPtr _targetHWnd, string status)
{
Status = status;
b = new SolidBrush(Color.FromArgb(255,0,149,48));
window = Graphics.FromHwnd(_targetHWnd);
}
public void DrawFrame()
{
window.DrawString(Status, new Font("Arial", 12),b, 0, -20);
}
public void Dispose()
{
window?.Dispose();
b?.Dispose();
}
}
Which works fine:
However, I need to be able to draw up here on the title bar:
It appears as though utilising Graphics.FromHwnd() limits me to using the typeable space in notepad so I can't draw directly into the button bar.
How do I get a graphics object encompassing the entire Window that I can draw over?
For reference, here is the rest of my code:
namespace OnScreenOverlay
{
class DataManager:IDisposable
{
private const string USER_CACHE = #"C:\test.txt";
private DirectDisplay directDisplay;
private volatile bool exiting;
private readonly Process _target;
private readonly IntPtr _targetHWnd;
private string _currentUser;
private int _daysUntilExpiry;
private NativeMethods.Rect _location;
public DataManager()
{
_target= Process.GetProcessesByName("notepad")[0];
if (_target== null)
{
MessageBox.Show("No target detected... Closing");
}
_targetHWnd = _target.MainWindowHandle;
//InitializeWinHook();
GetCurrentUser();
GetExpiryDate();
directDisplay = new DirectDisplay(_targetHWnd, $"Current User: {_currentUser} --- Password Expires: {_daysUntilExpiry} days");
}
private void GetCurrentUser()
{
if (File.Exists(USER_CACHE))
{
_currentUser = File.ReadAllLines(USER_CACHE)[0].Split('=')[0];
}
else
{
Application.Exit();
}
}
private void GetExpiryDate()
{
using (PrincipalContext domain = new PrincipalContext(ContextType.Domain))
{
using (UserPrincipal user = UserPrincipal.FindByIdentity(domain, IdentityType.SamAccountName, _currentUser))
{
DateTime? pwLastSet = user?.LastPasswordSet;
if (pwLastSet.HasValue)
{
_daysUntilExpiry = (int)(TimeSpan.FromDays(7) - (DateTime.Now - pwLastSet.Value)).TotalDays;
}
else
{
_daysUntilExpiry = int.MinValue;
}
}
}
}
public void Start()
{
while (!exiting)
{
directDisplay.DrawFrame();
Thread.Sleep(1000/60); //Prevent method returning
}
}
private void InitializeWinHook()
{
NativeMethods.SetWinEventHook(NativeMethods.EVENT_OBJECT_LOCATIONCHANGE, NativeMethods.EVENT_OBJECT_LOCATIONCHANGE, IntPtr.Zero, TargetMoved, (uint)_target.Id,
NativeMethods.GetWindowThreadProcessId(_target.MainWindowHandle, IntPtr.Zero), NativeMethods.WINEVENT_OUTOFCONTEXT | NativeMethods.WINEVENT_SKIPOWNPROCESS | NativeMethods.WINEVENT_SKIPOWNTHREAD);
}
private void TargetMoved(IntPtr hWinEventHook, uint eventType, IntPtr lParam, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
NativeMethods.GetWindowRect(_targetHWnd, ref _location);
}
public void Dispose()
{
directDisplay?.Dispose();
_target?.Dispose();
}
}
}
I realise that I don't need the WindowsHook stuff if I'm utilising this method to get the Graphics object, I just haven't removed it yet in-case this method doesn't work.
Further to this, I know I could get the Graphics object for the Desktop using IntPtr.Zero but I only want my overlay to be a Single Z-level above the target application.

Related

InstalledFontCollection does not actualize when adding font with AddFontResourceW followed by SendMessage

I am developping an application with wpf, .net and devexpress. I mainly use the devexpress for the report designer and the grid.
I need to load custom fonts in the session for the report designer and cannot install them in the system as user may not have administrative priviledges.
The report designer seems to rely on "InstalledFontCollection" to present available fonts to user.
As MSDN documentation indicates here, I dynamically load fonts with "AddFontResourceW" followed by "SendMessage".
However, calling "InstalledFontCollection" subsequently is not registering newly loaded fonts. However, it will list them if I restart the app.
The same phenomena appears using RemoveFontResourceW followed by SendMessage.
Here is part of the program I built to test it.
1 - The class responsible for font related stuff :
public static class FontManager
{
static public IEnumerable<string> ListInstalledFonts()
{
var installedFonts = new InstalledFontCollection();
var result = from f in installedFonts.Families select f.Name;
installedFonts.Dispose();
return result;
}
[DllImport("gdi32.dll", EntryPoint = "AddFontResourceW", SetLastError = true)]
public static extern int AddFontResource([In][MarshalAs(UnmanagedType.LPWStr)] string lpFileName);
[DllImport("gdi32.dll", EntryPoint = "RemoveFontResourceW", SetLastError = true)]
public static extern int RemoveFontResource([In][MarshalAs(UnmanagedType.LPWStr)] string lpFileName);
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, uint wMsg, IntPtr wParam, IntPtr lParam);
static IntPtr HWND_BROADCAST = new IntPtr(0xffff);
const uint WM_FONTCHANGE = 0x001D;
static public void LoadFont(string path)
{
Console.WriteLine("Loading Font : " + path);
if (AddFontResource(path) > 0)
{
Console.WriteLine(string.Format("Font {0} installed successfully.", path));
SendMessage(HWND_BROADCAST, WM_FONTCHANGE, IntPtr.Zero, IntPtr.Zero);
}
else
{
int error = Marshal.GetLastWin32Error();
if (error != 0)
Console.WriteLine(string.Format("Error Installing font : {0} : {1}", path, new Win32Exception(error).Message));
}
}
static public void UnloadFont(string path)
{
Console.WriteLine("Removing Font : " + path);
if (RemoveFontResource(path) > 0)
{
Console.WriteLine(string.Format("Font {0} removed successfully.", path));
SendMessage(HWND_BROADCAST, WM_FONTCHANGE, IntPtr.Zero, IntPtr.Zero);
}
else
{
int error = Marshal.GetLastWin32Error();
if (error != 0)
Console.WriteLine(string.Format("Error Removing Font : {0} : {1}", path, new Win32Exception(error).Message));
}
}
}
2 - the viewmodel presenting the font list to the ui :
public class MainWindowVM : INotifyPropertyChanged
{
#region ctor
public MainWindowVM()
{
RefreshAvailableFonts();
RefreshVisibleFonts();
CmdRefreshAvailableFonts = new RelayCommand(RefreshAvailableFonts);
CmdRefreshVisibleFonts = new RelayCommand(RefreshVisibleFonts);
CmdLoadCustomFonts = new RelayCommand(LoadCustomFonts);
CmdUnloadCustomFonts = new RelayCommand(UnloadCustomFonts);
}
#endregion
#region propertyNotifier
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion propertyNotifier
// property used to present font list to an ui control, for instance a grid
private IEnumerable<string> visibleFonts = null;
public IEnumerable<string> VisibleFonts
{
get => visibleFonts;
set
{
visibleFonts = value;
NotifyPropertyChanged();
}
}
// property used to cache font list available in system
private IEnumerable<string> availableFonts = null;
public IEnumerable<string> AvailableFonts
{
get => availableFonts;
set
{
availableFonts = value;
NotifyPropertyChanged();
RefreshVisibleFonts();
}
}
// property used with textbox to filter visible fonts in grid
private string filter = null;
public string Filter
{
get => filter;
set
{
filter = value;
NotifyPropertyChanged();
RefreshVisibleFonts();
}
}
#region Commands
// command used to refresh visible font list manually from ui
public ICommand CmdRefreshVisibleFonts { get; private set; }
public void RefreshVisibleFonts()
{
Console.WriteLine("=> RefreshVisibleFonts()");
if (filter == null)
VisibleFonts = from fn in AvailableFonts select fn;
else VisibleFonts = from fn in AvailableFonts where fn.ToLower().Contains(filter.ToLower()) select fn;
}
// command used to refresh available font list manually from ui
public ICommand CmdRefreshAvailableFonts { get; private set; }
public void RefreshAvailableFonts()
{
Console.WriteLine("=> RefreshAvailableFonts()");
AvailableFonts = FontManager.ListInstalledFonts();
}
// command used to load custom fonts manually in the session
public ICommand CmdLoadCustomFonts { get; private set; }
public void LoadCustomFonts()
{
Console.WriteLine("=> LoadCustomFonts()");
var docs = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string fonts = Path.Combine(docs, "fonts");
FontManager.LoadFont(Path.Combine(fonts, #"Pancho-Semibold.ttf"));
FontManager.LoadFont(Path.Combine(fonts, #"Pancho-Medium.ttf"));
FontManager.LoadFont(Path.Combine(fonts, #"Pancho-Regular.ttf"));
}
// command used to unload custom fonts manually from the session
public ICommand CmdUnloadCustomFonts { get; private set; }
public void UnloadCustomFonts()
{
Console.WriteLine("=> UnloadCustomFonts()");
var docs = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string fonts = Path.Combine(docs, "fonts");
FontManager.UnloadFont(Path.Combine(fonts, #"Pancho-Semibold.ttf"));
FontManager.UnloadFont(Path.Combine(fonts, #"Pancho-Medium.ttf"));
FontManager.UnloadFont(Path.Combine(fonts, #"Pancho-Regular.ttf"));
}
#endregion Commands
}
3 - mainwindow code behind : I have also added a winproc hook to check if the message is effectively broadcasted when adding or removing fonts using sendMessage :
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
source.AddHook(WndProc);
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == 0x1D)
{
Console.WriteLine("Fontchange message received");
if (DataContext is ViewModels.MainWindowVM vm)
vm.RefreshAvailableFonts();
}
return IntPtr.Zero;
}
}
related links :
https://social.msdn.microsoft.com/Forums/en-US/53e22f9e-40b1-4bbb-b5d2-fd3d7431c783/installedfontcollection

Cannot Populate TreeView Control with Win32 Messages in CSharp

I'm working with TreeView Control in Windows Forms in C#.
Since actual population part of TreeView Control takes a lot of time it freezes the UI. So I'm attempting to do the population using PostMessage Win32 API from a background thread but I found that the Treeview isn't getting inserted with Items.
So I moved the code from background thread to main thread. But then also the Insert Item Code is not working. I got similar code working with TreeView in C++ and trying to do the same thing with C# using interop routines.
I'm not going the usual C# way of treeView1.Nodes.Add("...") because it freezes the UI even if I follow the Delegate method and BackgroundWorker method for populating UI controls from another thread. I'm giving the code I use below. Can some one please help to find the issue with the code.
Also Please note for the TreeView Control I'm using my own simple class derived from TreeView class, where I have overriden the WndProc method to verify the flow of Windows Messages and I can see the messages(TVM_INSERTITEM) are actually getting through but still the item is not getting populated
Also I have got similar interop code working fine from Background Thread for ListView Control but my attempts with TreeView haven't succeeded so far.
Form Class Code
using System;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace UpdateTreeViewFromAnotherThread
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct TVITEM
{
public uint mask;
public IntPtr hItem;
public uint state;
public uint stateMask;
public IntPtr pszText;
public int cchTextMax;
public int iImage;
public int iSelectedImage;
public int cChildren;
public uint lParam;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct TVINSERTSTRUCT
{
public IntPtr hParent;
public IntPtr hInsertAfter;
public TVITEM item;
}
public enum TreeViewInsert
{
TVI_ROOT = -0x10000,
}
[Flags]
public enum TreeViewItemMask
{
TVIF_TEXT = 0x0001,
}
public partial class Form1 : Form
{
const int TV_FIRST = 0x1100;
IntPtr tvInsItemPtr;
TVINSERTSTRUCT tvins;
IntPtr handleTreeView;
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, IntPtr lParam);
public enum TreeViewMessage
{
TVM_INSERTITEM = TV_FIRST + 50,
}
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
handleTreeView = treeView1.Handle;
//treeView1.Nodes.Add("hello");
PopulateTree(handleTreeView);
}
public void PopulateTree(IntPtr handle)
{
tvins = new TVINSERTSTRUCT();
tvins.item.mask = (uint)TreeViewItemMask.TVIF_TEXT;
// Set the text of the item.
string productName = "Product";
string value = productName;
byte[] buffer = new byte[100];
buffer = Encoding.Unicode.GetBytes(value + "\0");
tvins.item.pszText = Marshal.AllocHGlobal(buffer.Length);
Marshal.Copy(buffer, 0, tvins.item.pszText, buffer.Length);
tvins.hParent = IntPtr.Zero;
tvins.hInsertAfter = (IntPtr)(TreeViewInsert.TVI_ROOT);
tvInsItemPtr = Marshal.AllocHGlobal(Marshal.SizeOf(tvins));
Marshal.StructureToPtr(tvins, tvInsItemPtr, true);
PostMessage(treeView1.Handle, (uint)TreeViewMessage.TVM_INSERTITEM, IntPtr.Zero, tvInsItemPtr);
//SendMessage(treeView1.Handle, (int)TreeViewMessage.TVM_INSERTITEM, 0, tvInsItemPtr);
}
}
}
MyTreeView Class Code
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace UpdateTreeViewFromAnotherThread
{
class MyTreeView:TreeView
{
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x1132)
{
TVINSERTSTRUCT anotherTVInsertStruct;
anotherTVInsertStruct = (TVINSERTSTRUCT)Marshal.PtrToStructure(m.LParam, typeof(TVINSERTSTRUCT));
string anotherNodeText = Marshal.PtrToStringAnsi(anotherTVInsertStruct.item.pszText);
}
if(m.Msg == 0x113F)
{
TVITEM anotherTVItem;
anotherTVItem = (TVITEM)Marshal.PtrToStructure(m.LParam, typeof(TVITEM));
string anotherNodeText = Marshal.PtrToStringAnsi(anotherTVItem.pszText);
}
base.WndProc(ref m);
//Trace.WriteLine(m.Msg.ToString() + ", " + m.ToString());
}
}
}
Update_1
Prevented NM_CUSTOMDRAW for Treeview using the below code. Thanks to the code atlink.
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_REFLECT + WM_NOTIFY:
{
NMHDR nmhdr = (NMHDR)m.GetLParam(typeof(NMHDR));
switch ((int)nmhdr.code)
{
case NM_CUSTOMDRAW:
NMTVCUSTOMDRAW nmTvDraw = (NMTVCUSTOMDRAW)m.GetLParam(typeof(NMTVCUSTOMDRAW));
switch (nmTvDraw.nmcd.dwDrawStage)
{
case CDDS_ITEMPREPAINT:
m.Result = (IntPtr)CDRF_DODEFAULT;
break;
}
Marshal.StructureToPtr(nmTvDraw, m.LParam, false);
return;
}
break;
}
}
base.WndProc(ref m);
}
So Now If I change my earlier PopulateTree function (note Thread.Sleep()) and its invocation to a background thread as below it will not freeze the UI during the population process
private void button1_Click(object sender, EventArgs e)
{
handleTreeView = treeView1.Handle;
Thread backgroundThread = new Thread(() => PopulateTree(handleTreeView));
backgroundThread.Start();
}
public void PopulateTree(IntPtr handle)
{
for(int i =0; i< 1000; i++)
{
tvins = new TVINSERTSTRUCT();
tvins.item.mask = (uint)TreeViewItemMask.TVIF_TEXT;
// Set the text of the item.
string productName = "Product_" + i.ToString();
string value = productName;
byte[] buffer = new byte[100];
buffer = Encoding.Unicode.GetBytes(value + "\0");
tvins.item.pszText = Marshal.AllocHGlobal(buffer.Length);
Marshal.Copy(buffer, 0, tvins.item.pszText, buffer.Length);
tvins.hParent = IntPtr.Zero;
tvins.hInsertAfter = (IntPtr)(TreeViewInsert.TVI_ROOT);
tvInsItemPtr = Marshal.AllocHGlobal(Marshal.SizeOf(tvins));
Marshal.StructureToPtr(tvins, tvInsItemPtr, true);
PostMessage(handle, (uint)TreeViewMessage.TVM_INSERTITEM, IntPtr.Zero, tvInsItemPtr);
Thread.Sleep(1000);
}
}
Thanks Jimi and MikiD I was able to produce same non-freezing UI behaviour using the BeginUpdate and BeginInvoke approach. I changed my code as below
private async void button1_Click(object sender, EventArgs e)
{
await Task.Run(() => PopulateTree());
}
private async void PopulateTree()
{
for(int i = 0;i< 1000;i++)
{
treeView1.BeginInvoke( (MethodInvoker)delegate ()
{
treeView1.BeginUpdate();
treeView1.Nodes.Add("Product_" + i.ToString());
treeView1.EndUpdate();
}
);
System.Threading.Thread.Sleep(1000);
}
}

restoring from tray does not work (windowState) [duplicate]

Is there an easy method to restore a minimized form to its previous state, either Normal or Maximized? I'm expecting the same functionality as clicking the taskbar (or right-clicking and choosing restore).
So far, I have this, but if the form was previously maximized, it still comes back as a normal window.
if (docView.WindowState == FormWindowState.Minimized)
docView.WindowState = FormWindowState.Normal;
Do I have to handle the state change in the form to remember the previous state?
I use the following extension method:
using System.Runtime.InteropServices;
namespace System.Windows.Forms
{
public static class Extensions
{
[DllImport( "user32.dll" )]
private static extern int ShowWindow( IntPtr hWnd, uint Msg );
private const uint SW_RESTORE = 0x09;
public static void Restore( this Form form )
{
if (form.WindowState == FormWindowState.Minimized)
{
ShowWindow(form.Handle, SW_RESTORE);
}
}
}
}
Then call form.Restore() in my code.
The easiest way to restore a form to normal state is:
if (MyForm.WindowState == FormWindowState.Minimized)
{
MyForm.WindowState = FormWindowState.Normal;
}
You could simulate clicking on the taskbar button like this:
SendMessage(docView.Handle, WM_SYSCOMMAND, SC_RESTORE, 0);
For me, the code above does NOT work.
But at last I found the working code. Here it is:
CxImports.ManagedWindowPlacement placement = new CxImports.ManagedWindowPlacement();
CxImports.GetWindowPlacement(Convert.ToUInt32(Handle.ToInt64()), placement);
if (placement.flags == CxImports.WPF_RESTORETOMAXIMIZED)
WindowState = FormWindowState.Maximized;
else
WindowState = FormWindowState.Normal;
I guess, you can find all the needed "imported" functions by simple googling.
If anybody wonders how to do that with other apps windows,this code works for me:
public void UnMinimize(IntPtr handle)
{
WINDOWPLACEMENT WinPlacement = new WINDOWPLACEMENT();
GetWindowPlacement(handle, out WinPlacement);
if(WinPlacement.flags.HasFlag(WINDOWPLACEMENT.Flags.WPF_RESTORETOMAXIMIZED))
{
ShowWindow(handle, (int)SW_MAXIMIZE);
}
else
{
ShowWindow(handle, (int)SW_RESTORE);
}
}
Stuff is here:
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public Int32 Left;
public Int32 Top;
public Int32 Right;
public Int32 Bottom;
}
public struct POINT
{
public int x;
public int y;
}
public struct WINDOWPLACEMENT
{
[Flags]
public enum Flags : uint
{
WPF_ASYNCWINDOWPLACEMENT = 0x0004,
WPF_RESTORETOMAXIMIZED = 0x0002,
WPF_SETMINPOSITION = 0x0001
}
/// <summary>
/// The length of the structure, in bytes. Before calling the GetWindowPlacement or SetWindowPlacement functions, set this member to sizeof(WINDOWPLACEMENT).
/// </summary>
public uint length;
/// <summary>
/// The flags that control the position of the minimized window and the method by which the window is restored. This member can be one or more of the following values.
/// </summary>
///
public Flags flags;//uint flags;
/// <summary>
/// The current show state of the window. This member can be one of the following values.
/// </summary>
public uint showCmd;
/// <summary>
/// The coordinates of the window's upper-left corner when the window is minimized.
/// </summary>
public POINT ptMinPosition;
/// <summary>
/// The coordinates of the window's upper-left corner when the window is maximized.
/// </summary>
public POINT ptMaxPosition;
/// <summary>
/// The window's coordinates when the window is in the restored position.
/// </summary>
public RECT rcNormalPosition;
}
public class UnMinimizeClass
{
[DllImport("user32.dll")]
public static extern bool GetWindowPlacement(IntPtr hWnd, out WINDOWPLACEMENT lpwndpl);
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
const int SW_MAXIMIZE = 3;
const int SW_RESTORE = 9;
public static void UnMinimize(IntPtr handle)
{
WINDOWPLACEMENT WinPlacement = new WINDOWPLACEMENT();
GetWindowPlacement(handle, out WinPlacement);
if (WinPlacement.flags.HasFlag(WINDOWPLACEMENT.Flags.WPF_RESTORETOMAXIMIZED))
{
ShowWindow(handle, SW_MAXIMIZE);
}
else
{
ShowWindow(handle, (int)SW_RESTORE);
}
}
}
Using MainWindow.WindowState = WindowState.Normal; isn't enough
Next approach works for my WPF applcation:
MainWindow.WindowState = WindowState.Normal;
MainWindow.Show();
MainWindow.Activate();
I just added one more piece to generify the solution given by #Mesmo. It will create the instance if not created or restore and focus the form if the instance is already created from anywhere in the application. My requirement was that I didn't want to open multiple forms for some of the functionality in the application.
Utilities Class:
public static class Utilities
{
[DllImport("user32.dll")]
private static extern int ShowWindow(IntPtr hWnd, uint Msg);
private const uint SW_RESTORE = 0x09;
public static void Restore(this Form form)
{
if (form.WindowState == FormWindowState.Minimized)
{
ShowWindow(form.Handle, SW_RESTORE);
}
}
public static void CreateOrRestoreForm<T>() where T: Form
{
Form form = Application.OpenForms.OfType<T>().FirstOrDefault();
if (form == null)
{
form = Activator.CreateInstance<T>();
form.Show();
}
else
{
form.Restore();
form.Focus();
}
}
}
Usage:
Utilities.CreateOrRestoreForm<AboutForm>();
This is simple and works if you don't want to use any PInvoke or API trickery. Keep track of when the form has been resized, ignoring when it is minimised.
FormWindowState _PreviousWindowState;
private void TestForm_Resize(object sender, EventArgs e)
{
if (WindowState != FormWindowState.Minimized)
_PreviousWindowState = WindowState;
}
Later when you want to restore it -- for example if a tray icon is clicked:
private void Tray_MouseClick(object sender, MouseEventArgs e)
{
Activate();
if (WindowState == FormWindowState.Minimized)
WindowState = _PreviousWindowState; // former glory
}
[DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool GetWindowRect(IntPtr hWnd, ref wndRect lpRect);
[DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll")] public static extern bool EnumWindows(WNDENUMPROC lpEnumFunc, int lParam);//用来遍历所有窗口
[DllImport("user32.dll")] public static extern int GetWindowTextW(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)]StringBuilder lpString, int nMaxCount);//获取窗口Text
[DllImport("user32.dll")] public static extern int GetClassNameW(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)]StringBuilder lpString, int nMaxCount);//获取窗口类名
public static List<wndInfo> GetAllDesktopWindows(bool? isVisitable_)
{
//用来保存窗口对象
List<wndInfo> wndList = new List<wndInfo>();
//enum all desktop windows
EnumWindows(delegate (IntPtr hWnd, int lParam)
{
wndInfo wnd = new wndInfo();
StringBuilder sb = new StringBuilder(256);
//get hwnd
wnd.hWnd = hWnd;
if (isVisitable_ == null || IsWindowVisible(wnd.hWnd) == isVisitable_)
{
//get window name
GetWindowTextW(hWnd, sb, sb.Capacity);
wnd.szWindowName = sb.ToString();
//get window class
GetClassNameW(hWnd, sb, sb.Capacity);
wnd.szClassName = sb.ToString();
wndList.Add(wnd);
}
return true;
}, 0);
return wndList;
}
private void Btn_Test5_Click(object sender, RoutedEventArgs e)
{
var ws = WSys.GetAllDesktopWindows(true);
foreach (var w in ws)
{
if (w.szWindowName == "计算器")
{
WSys.ShowWindow(w.hWnd, 5);
WSys.ShowWindow(w.hWnd, 9);
Log.WriteLine(w.szWindowName);
}
}
}
The above code did not quite work for me in all situations
After checking the flags I also have to check showcmd=3 and if so maximise else restore

How does one invoke the Windows Permissions dialog programmatically?

I'm trying to write a certificate manager, and I want to manage the permissions over the certificate file. I'd prefer not reinventing the wheel of the Windows permissions dialog, so ideally there would be some kind of shell command that I could pass the path of the item whose permissions are being managed. Then, I could just invoke it and let the shell take care of updating the permissions.
I've seen some mention here and there of a shell function, SHObjectProperties, but nothing clear on how to use it. Any help would be appreciated.
You can display the Windows file permissions dialog using ShellExecuteEx (using the "properties" verb and the "Security" parameter).
This will display a dialog like the following within your process, and the file permission viewing and editing will be fully functional just as if you had got this dialog through the Windows explorer shell:
Here is an example with Windows Forms where a file is selected and the Security properties of that file then displayed. I have used the P/Invoke code for ShellExecuteEx from this Stackoverflow answer.
using System;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace FileSecurityProperties
{
public partial class FileSelectorForm : Form
{
private static bool ShowFileSecurityProperties(string Filename, IntPtr parentHandle)
{
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;
info.hwnd = parentHandle;
info.lpParameters = "Security"; // Opens the file properties on the Security tab
return ShellExecuteEx(ref info);
}
private void fileSelectButton_Click(object sender, EventArgs e)
{
if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
ShowFileSecurityProperties(
openFileDialog.FileName,
this.Handle); // Pass parent window handle for properties dialog
}
}
#region P/Invoke code for ShellExecuteEx from https://stackoverflow.com/a/1936957/4486839
[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;
#endregion
#region Irrelevant Windows forms code
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.openFileDialog = new System.Windows.Forms.OpenFileDialog();
this.fileSelectButton = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// openFileDialog1
//
this.openFileDialog.FileName = "";
//
// fileSelectButton
//
this.fileSelectButton.Location = new System.Drawing.Point(52, 49);
this.fileSelectButton.Name = "fileSelectButton";
this.fileSelectButton.Size = new System.Drawing.Size(131, 37);
this.fileSelectButton.TabIndex = 0;
this.fileSelectButton.Text = "Select file ...";
this.fileSelectButton.UseVisualStyleBackColor = true;
this.fileSelectButton.Click += new System.EventHandler(this.fileSelectButton_Click);
//
// FileSelectorForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(248, 162);
this.Controls.Add(this.fileSelectButton);
this.Name = "FileSelectorForm";
this.Text = "File Selector";
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.OpenFileDialog openFileDialog;
private System.Windows.Forms.Button fileSelectButton;
public FileSelectorForm()
{
InitializeComponent();
}
#endregion
}
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new FileSelectorForm());
}
}
}
If you were hoping to get the file permissions dialog on its own, rather than as a tab in the general file properties dialog, that is possible using aclui.dll, e.g. using the EditSecurity function, but this will NOT then give you your other requirement of having the file permissions handling taken care of for you, because you have to provide an interface which does the getting and setting of security properties if you go down that route, and it looks like a lot of coding.
Here is a utilily class that allows you to have only the security property sheet (not all sheets the shell displays).
You can call it like this in a console app:
class Program
{
[STAThread]
static void Main(string[] args)
{
// NOTE: if the dialog looks old fashioned (for example if used in a console app),
// then add an app.manifest and uncomment the dependency section about Microsoft.Windows.Common-Controls
PermissionDialog.Show(IntPtr.Zero, #"d:\temp\killroy_was_here.png");
}
}
or like this in a winform app
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
PermissionDialog.Show(IntPtr.Zero, #"d:\temp\killroy_was_here.png");
}
}
And this is the main class. It's basically using the same thing as the shell, but in its own property sheet.
public static class PermissionDialog
{
public static bool Show(IntPtr hwndParent, string path)
{
if (path == null)
throw new ArgumentNullException("path");
SafePidlHandle folderPidl;
int hr;
hr = SHILCreateFromPath(Path.GetDirectoryName(path), out folderPidl, IntPtr.Zero);
if (hr != 0)
throw new Win32Exception(hr);
SafePidlHandle filePidl;
hr = SHILCreateFromPath(path, out filePidl, IntPtr.Zero);
if (hr != 0)
throw new Win32Exception(hr);
IntPtr file = ILFindLastID(filePidl);
System.Runtime.InteropServices.ComTypes.IDataObject ido;
hr = SHCreateDataObject(folderPidl, 1, new IntPtr[] { file }, null, typeof(System.Runtime.InteropServices.ComTypes.IDataObject).GUID, out ido);
if (hr != 0)
throw new Win32Exception(hr);
// if you get a 'no such interface' error here, make sure the running thread is STA
IShellExtInit sei = (IShellExtInit)new SecPropSheetExt();
sei.Initialize(IntPtr.Zero, ido, IntPtr.Zero);
IShellPropSheetExt spse = (IShellPropSheetExt)sei;
IntPtr securityPage = IntPtr.Zero;
spse.AddPages((p, lp) =>
{
securityPage = p;
return true;
}, IntPtr.Zero);
PROPSHEETHEADER psh = new PROPSHEETHEADER();
psh.dwSize = Marshal.SizeOf(psh);
psh.hwndParent = hwndParent;
psh.nPages = 1;
psh.phpage = Marshal.AllocHGlobal(IntPtr.Size);
Marshal.WriteIntPtr(psh.phpage, securityPage);
// TODO: adjust title & icon here, also check out the available flags
psh.pszCaption = "Permissions for '" + path + "'";
IntPtr res;
try
{
res = PropertySheet(ref psh);
}
finally
{
Marshal.FreeHGlobal(psh.phpage);
}
return res == IntPtr.Zero;
}
private class SafePidlHandle : SafeHandle
{
public SafePidlHandle()
: base(IntPtr.Zero, true)
{
}
public override bool IsInvalid
{
get { return handle == IntPtr.Zero; }
}
protected override bool ReleaseHandle()
{
if (IsInvalid)
return false;
Marshal.FreeCoTaskMem(handle);
return true;
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct PROPSHEETHEADER
{
public int dwSize;
public int dwFlags;
public IntPtr hwndParent;
public IntPtr hInstance;
public IntPtr hIcon;
public string pszCaption;
public int nPages;
public IntPtr nStartPage;
public IntPtr phpage;
public IntPtr pfnCallback;
}
[DllImport("shell32.dll")]
private static extern IntPtr ILFindLastID(SafePidlHandle pidl);
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
private static extern int SHILCreateFromPath(string pszPath, out SafePidlHandle ppidl, IntPtr rgflnOut);
[DllImport("shell32.dll")]
private static extern int SHCreateDataObject(SafePidlHandle pidlFolder, int cidl, IntPtr[] apidl, System.Runtime.InteropServices.ComTypes.IDataObject pdtInner, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out System.Runtime.InteropServices.ComTypes.IDataObject ppv);
[DllImport("comctl32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr PropertySheet(ref PROPSHEETHEADER lppsph);
private delegate bool AddPropSheetPage(IntPtr page, IntPtr lParam);
[ComImport]
[Guid("1f2e5c40-9550-11ce-99d2-00aa006e086c")] // this GUID points to the property sheet handler for permissions
private class SecPropSheetExt
{
}
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214E8-0000-0000-C000-000000000046")]
private interface IShellExtInit
{
void Initialize(IntPtr pidlFolder, System.Runtime.InteropServices.ComTypes.IDataObject pdtobj, IntPtr hkeyProgID);
}
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214E9-0000-0000-C000-000000000046")]
private interface IShellPropSheetExt
{
void AddPages([MarshalAs(UnmanagedType.FunctionPtr)] AddPropSheetPage pfnAddPage, IntPtr lParam);
void ReplacePage(); // not fully defined, we don't use it
}
}
Opps my bad 'attrib' is how it used to be.
What you are looking to do I believe is "change security descriptors on folders and files". The command line tool is cacls.
Please see: http://en.wikipedia.org/wiki/Cacls

On start-up taskbar covers full screen C# application

I written a Kiosk style C# application using visual studio that is run on startup and should expand to full-screen and cover the task-bar.
I am doing the usual setting boarder style to none, and fill extents and it works perfectly if I just launch the application manually.
When the application launches on startup (by way of a short-cut in the startup folder in the start menu), the task-bar ends up on top of the program and clicking somewhere on the form does not bring the form back to the top.
Has anyone encountered this problem before, or know of possible workarounds.
I have also done this another time:
public class Screensize
{
/// <summary>
/// Selected Win AI Function Calls
/// </summary>
public class WinApi
{
[DllImport("user32.dll", EntryPoint = "GetSystemMetrics")]
public static extern int GetSystemMetrics(int which);
[DllImport("user32.dll")]
public static extern void
SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter,
int X, int Y, int width, int height, uint flags);
private const int SM_CXSCREEN = 0;
private const int SM_CYSCREEN = 1;
private static IntPtr HWND_TOP = IntPtr.Zero;
private const int SWP_SHOWWINDOW = 64; // 0x0040
public static int ScreenX
{
get { return GetSystemMetrics(SM_CXSCREEN); }
}
public static int ScreenY
{
get { return GetSystemMetrics(SM_CYSCREEN); }
}
public static void SetWinFullScreen(IntPtr hwnd)
{
SetWindowPos(hwnd, HWND_TOP, 0, 0, ScreenX, ScreenY, SWP_SHOWWINDOW);
}
}
/// <summary>
/// Class used to preserve / restore state of the form
/// </summary>
public class FormState
{
private FormWindowState winState;
private FormBorderStyle brdStyle;
private bool topMost;
private Rectangle bounds;
private bool IsMaximized = false;
public void Maximize(Form targetForm)
{
if (!IsMaximized)
{
IsMaximized = true;
Save(targetForm);
targetForm.WindowState = FormWindowState.Maximized;
targetForm.FormBorderStyle = FormBorderStyle.None;
targetForm.TopMost = false;
WinApi.SetWinFullScreen(targetForm.Handle);
}
}
public void Save(Form targetForm)
{
winState = targetForm.WindowState;
brdStyle = targetForm.FormBorderStyle;
topMost = targetForm.TopMost;
bounds = targetForm.Bounds;
}
public void Restore(Form targetForm)
{
targetForm.WindowState = winState;
targetForm.FormBorderStyle = brdStyle;
targetForm.TopMost = topMost;
targetForm.Bounds = bounds;
IsMaximized = false;
}
}
and just call in your form:
screensize.Maximize(this)
I think you mean this
And now I see this post is from 2013...
Workaround: When starting, kill explorer.exe - you don't need it -> taskbar disappear.
When needed, start it using Task Manager

Categories

Resources