How does one invoke the Windows Permissions dialog programmatically? - c#

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

Related

how to change main window state in a single instance using .Net Core

I am trying to leave my application with just one instance.
The code works fine, but I want to manipulate the window that is already open.
How can I bring the window already open to the front when I open more than one instance.
public partial class App : Application {
private static Mutex _mutex = null;
protected override void OnStartup(StartupEventArgs e)
{
const string appName = "MyAppName";
bool createdNew;
_mutex = new Mutex(true, appName, out createdNew);
if (!createdNew)
{
Application.Current.Shutdown();
}
base.OnStartup(e);
}
}
in OnStartup I tried to call the window using MainWindow.WindowState = WindowState.Normal; but it fails in that call noting the error System.Windows.Application.MainWindow.get returned null.
I created a sample project for single instance application in WPF. Maybe helpful:
Wpf Single Instance Application
You need the following native methods:
public static class NativeMethod
{
public static IntPtr BroadcastHandle => (IntPtr)0xffff;
public static int ReactivationMessage => RegisterWindowMessage("WM_SHOWME");
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
[DllImport("user32")]
public static extern int RegisterWindowMessage(string message);
}
In app main window add hook to window messages and when you got activation message you should bring the main window in top of other windows. Like this:
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
RegisterWndProcCallback();
}
private void RegisterWndProcCallback()
{
var handle = new WindowInteropHelper(this).EnsureHandle();
var hock = new HwndSourceHook(WndProc);
var source = HwndSource.FromHwnd(handle);
source.AddHook(hock);
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == NativeMethod.ReactivationMessage)
ReactivateMainWindow();
return IntPtr.Zero;
}
private void ReactivateMainWindow()
{
if (WindowState == WindowState.Minimized)
WindowState = WindowState.Normal;
Topmost = !Topmost;
Topmost = !Topmost;
}
}
And in app startup you should check for app instances and if needed you should send a message to the old instance and activate that. Like this:
public partial class App
{
private static readonly Mutex _singleInstanceMutex =
new Mutex(true, "{40D7FB99-C91E-471C-9E34-5D4A455E35E1}");
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
if (_singleInstanceMutex.WaitOne(TimeSpan.Zero, true))
{
Current.MainWindow = new MainWindow();
Current.MainWindow.Show();
Current.MainWindow.Activate();
_singleInstanceMutex.ReleaseMutex();
}
else
{
NativeMethod.PostMessage(
NativeMethod.BroadcastHandle,
NativeMethod.ReactivationMessage,
IntPtr.Zero,
IntPtr.Zero);
Current.Shutdown();
}
}
}

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 to change the gamma ramp of a single display monitor (NVidia Config)?

I try to change my gamma of just one screen and not all my screens.
I use this code to help me
But this SetDeviceGammaRamp(GetDC(IntPtr.Zero), ref s_ramp);
Is for all devices.
[EDIT2] I saw one weird thing : SetDeviceGammaRamp is not the same gamma of the Nvidia Panel Controller (I tried to change my value of SetDeviceGammaRamp, and it's like if i changed the value of brightness and contrast in the Nvidia panel). So i think i must to use NVidia API :/
So, how can i change this code to put my gamma on my first screen, or my second, but not both
[EDIT1] This is what i made :
class Monitor
{
[DllImport("user32.dll")]
static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, MonitorEnumProc lpfnEnum, IntPtr dwData);
public delegate int MonitorEnumProc(IntPtr hMonitor, IntPtr hDCMonitor, ref Rect lprcMonitor, IntPtr dwData);
[DllImport("user32.dll")]
public static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("user32.dll")]
static extern bool GetMonitorInfo(IntPtr hmon, ref MonitorInfo mi);
[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
public int left;
public int top;
public int right;
public int bottom;
}
/// <summary>
/// The struct that contains the display information
/// </summary>
public class DisplayInfo
{
public string Availability { get; set; }
public string ScreenHeight { get; set; }
public string ScreenWidth { get; set; }
public Rect MonitorArea { get; set; }
public Rect WorkArea { get; set; }
public IntPtr DC { get; set; }
}
[StructLayout(LayoutKind.Sequential)]
struct MonitorInfo
{
public uint size;
public Rect monitor;
public Rect work;
public uint flags;
}
/// <summary>
/// Collection of display information
/// </summary>
public class DisplayInfoCollection : List<DisplayInfo>
{
}
/// <summary>
/// Returns the number of Displays using the Win32 functions
/// </summary>
/// <returns>collection of Display Info</returns>
public DisplayInfoCollection GetDisplays()
{
DisplayInfoCollection col = new DisplayInfoCollection();
EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero,
delegate (IntPtr hMonitor, IntPtr hdcMonitor, ref Rect lprcMonitor, IntPtr dwData)
{
MonitorInfo mi = new MonitorInfo();
mi.size = (uint)Marshal.SizeOf(mi);
bool success = GetMonitorInfo(hMonitor, ref mi);
if (success)
{
DisplayInfo di = new DisplayInfo();
di.ScreenWidth = (mi.monitor.right - mi.monitor.left).ToString();
di.ScreenHeight = (mi.monitor.bottom - mi.monitor.top).ToString();
di.MonitorArea = mi.monitor;
di.WorkArea = mi.work;
di.Availability = mi.flags.ToString();
di.DC = GetDC(hdcMonitor);
col.Add(di);
}
return 1;
}, IntPtr.Zero);
return col;
}
public Monitor()
{
}
}
And for SetDeviceGammaRamp, i made this :
GammaRamp gamma = new GammaRamp();
Monitor.DisplayInfoCollection monitors;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Monitor monitor = new Monitor();
monitors = monitor.GetDisplays();
}
private void trackBar1_Scroll(object sender, EventArgs e)
{
int value = trackBar1.Value;
gamma.SetValue(Convert.ToByte(value), monitors[1].DC);
}
GammaRamp class :
public void SetValue(byte value, IntPtr hdc)
{
Ramp gammaArray = new Ramp { Red = new ushort[256], Green = new ushort[256], Blue = new ushort[256] };
for (int i = 0; i < 256; i++)
{
gammaArray.Red[i] = gammaArray.Green[i] = gammaArray.Blue[i] = (ushort)Math.Min(i * (value + 128), ushort.MaxValue);
}
SetDeviceGammaRamp(hdc, ref gammaArray);
}
You can get the DC of another monitor by using EnumDisplayMonitors or GetMonitorInfo functions.
See complete explanations at HMONITOR and the Device Context.
EDIT
As explained in EnumDisplayMonitors,
pass IntPtr.Zero to hdc parameter (values encompasses all displays)
then in MONITORENUMPROC, hdcMonitor should contain the right DC for the current monitor being evaluated
then change your di.DC = GetDC(IntPtr.Zero); to di.DC = GetDC(hdcMonitor);
(passing Zero to GetDC will obviously specify all monitors, not what you want)
EDIT 2
Little confusion with the docs, in fact the 3rd type of call in the remarks of EnumDisplayMonitors should be performed:
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow
{
private readonly List<IntPtr> _dcs = new List<IntPtr>();
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
var hdc = NativeMethods.GetDC(IntPtr.Zero);
if (hdc == IntPtr.Zero)
throw new InvalidOperationException();
if (!NativeMethods.EnumDisplayMonitors(hdc, IntPtr.Zero, Monitorenumproc, IntPtr.Zero))
throw new InvalidOperationException();
if (NativeMethods.ReleaseDC(IntPtr.Zero, hdc) == 0)
throw new InvalidOperationException();
foreach (var monitorDc in _dcs)
{
// do something cool !
}
}
private int Monitorenumproc(IntPtr param0, IntPtr param1, ref tagRECT param2, IntPtr param3)
{
// optional actually ...
var info = new MonitorInfo {cbSize = (uint) Marshal.SizeOf<MonitorInfo>()};
if (!NativeMethods.GetMonitorInfoW(param0, ref info))
throw new InvalidOperationException();
_dcs.Add(param1); // grab DC for current monitor !
return 1;
}
}
public class NativeMethods
{
[DllImport("user32.dll", EntryPoint = "ReleaseDC")]
public static extern int ReleaseDC([In] IntPtr hWnd, [In] IntPtr hDC);
[DllImport("user32.dll", EntryPoint = "GetDC")]
public static extern IntPtr GetDC([In] IntPtr hWnd);
[DllImport("user32.dll", EntryPoint = "GetMonitorInfoW")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetMonitorInfoW([In] IntPtr hMonitor, ref MonitorInfo lpmi);
[DllImport("user32.dll", EntryPoint = "EnumDisplayMonitors")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumDisplayMonitors([In] IntPtr hdc, [In] IntPtr lprcClip, MONITORENUMPROC lpfnEnum,
IntPtr dwData);
}
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate int MONITORENUMPROC(IntPtr param0, IntPtr param1, ref tagRECT param2, IntPtr param3);
[StructLayout(LayoutKind.Sequential)]
public struct MonitorInfo
{
public uint cbSize;
public tagRECT rcMonitor;
public tagRECT rcWork;
public uint dwFlags;
}
[StructLayout(LayoutKind.Sequential)]
public struct tagRECT
{
public int left;
public int top;
public int right;
public int bottom;
}
}
You should be able to get per-monitor DC, (cannot 100% confirm since I only have one screen).
If all else fails then maybe that NVidia thing interferes somehow under the hood.

Acessing C# code in Powershell

I want to use a system wide hotkey in my powershell GUI application. I found this C# code for registering a hotkey and integrated it in my script.
The trouble is, because I can't fully understand the C# code, I don't know how to properly add the parameters for the registerhotkey method.
The goal is: on key press SHIFT+ALT+Z execute the code in $hotkeyExecCode.
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[System.Windows.Forms.Application]::EnableVisualStyles()
$form1 = New-Object 'System.Windows.Forms.Form'
$form1.ClientSize = '200, 150'
$cCode = #"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
/// <summary> This class allows you to manage a hotkey </summary>
public class GlobalHotkeys : IDisposable
{
[DllImport( "user32", SetLastError = true )]
[return: MarshalAs( UnmanagedType.Bool )]
public static extern bool RegisterHotKey (IntPtr hwnd, int id, uint fsModifiers, uint vk);
[DllImport( "user32", SetLastError = true )]
public static extern int UnregisterHotKey (IntPtr hwnd, int id);
[DllImport( "kernel32", SetLastError = true )]
public static extern short GlobalAddAtom (string lpString);
[DllImport( "kernel32", SetLastError = true )]
public static extern short GlobalDeleteAtom (short nAtom);
public const int MOD_ALT = 1;
public const int MOD_CONTROL = 2;
public const int MOD_SHIFT = 4;
public const int MOD_WIN = 8;
public const int WM_HOTKEY = 0x312;
public GlobalHotkeys()
{
this.Handle = Process.GetCurrentProcess().Handle;
}
/// <summary>Handle of the current process</summary>
public IntPtr Handle;
/// <summary>The ID for the hotkey</summary>
public short HotkeyID { get; private set; }
/// <summary>Register the hotkey</summary>
public void RegisterGlobalHotKey(int hotkey, int modifiers, IntPtr handle)
{
UnregisterGlobalHotKey();
this.Handle = handle;
RegisterGlobalHotKey(hotkey, modifiers);
}
/// <summary>Register the hotkey</summary>
public void RegisterGlobalHotKey(int hotkey, int modifiers)
{
UnregisterGlobalHotKey();
try
{
// use the GlobalAddAtom API to get a unique ID (as suggested by MSDN)
string atomName = Thread.CurrentThread.ManagedThreadId.ToString("X8") + this.GetType().FullName;
HotkeyID = GlobalAddAtom(atomName);
if (HotkeyID == 0)
throw new Exception("Unable to generate unique hotkey ID. Error: " + Marshal.GetLastWin32Error().ToString());
// register the hotkey, throw if any error
if (!RegisterHotKey(this.Handle, HotkeyID, (uint)modifiers, (uint)hotkey))
throw new Exception("Unable to register hotkey. Error: " + Marshal.GetLastWin32Error().ToString());
}
catch (Exception ex)
{
// clean up if hotkey registration failed
Dispose();
Console.WriteLine(ex);
}
}
/// <summary>Unregister the hotkey</summary>
public void UnregisterGlobalHotKey()
{
if ( this.HotkeyID != 0 )
{
UnregisterHotKey(this.Handle, HotkeyID);
// clean up the atom list
GlobalDeleteAtom(HotkeyID);
HotkeyID = 0;
}
}
public void Dispose()
{
UnregisterGlobalHotKey();
}
}
"#
Add-Type -TypeDefinition $cCode
$hotkeyExecCode = { $form1.WindowState = "normal"}
[GlobalHotkeys]::RegisterHotKey( ???????? ) # <--------- How do i add the parameters here?
$form1.windowstate = "minimized"
$form1.ShowDialog()

Allow custom item "Always on Top" to be available when right-clicking the title bar? [duplicate]

I want to add the age old About menu item to my application. I want to add it to the 'system menu' of the application (the one which pops up when we click the application icon in the top-left corner). So, how can I do it in .NET?
Windows makes it fairly easy to get a handle to a copy of the form's system menu for customization purposes with the GetSystemMenu function. The hard part is that you're on your own to perform the appropriate modifications to the menu it returns, using functions such as AppendMenu, InsertMenu, and DeleteMenu just as you would if you were programming directly against the Win32 API.
However, if all you want to do is add a simple menu item, it's really not all that difficult. For example, you would only need to use the AppendMenu function because all you want to do is add an item or two to the end of the menu. Doing anything more advanced (like inserting an item in the middle of the menu, displaying a bitmap on the menu item, showing menu items checked, setting a default menu item, etc.) requires a bit more work. But once you know how it's done, you can go wild. The documentation on menu-related functions tells all.
Here's the complete code for a form that adds a separator line and an "About" item to the bottom of its system menu (also called a window menu):
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
public class CustomForm : Form
{
// P/Invoke constants
private const int WM_SYSCOMMAND = 0x112;
private const int MF_STRING = 0x0;
private const int MF_SEPARATOR = 0x800;
// P/Invoke declarations
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool AppendMenu(IntPtr hMenu, int uFlags, int uIDNewItem, string lpNewItem);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool InsertMenu(IntPtr hMenu, int uPosition, int uFlags, int uIDNewItem, string lpNewItem);
// ID for the About item on the system menu
private int SYSMENU_ABOUT_ID = 0x1;
public CustomForm()
{
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
// Get a handle to a copy of this form's system (window) menu
IntPtr hSysMenu = GetSystemMenu(this.Handle, false);
// Add a separator
AppendMenu(hSysMenu, MF_SEPARATOR, 0, string.Empty);
// Add the About menu item
AppendMenu(hSysMenu, MF_STRING, SYSMENU_ABOUT_ID, "&About…");
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
// Test if the About item was selected from the system menu
if ((m.Msg == WM_SYSCOMMAND) && ((int)m.WParam == SYSMENU_ABOUT_ID))
{
MessageBox.Show("Custom About Dialog");
}
}
}
And here's what the finished product looks like:
I've taken Cody Gray's solution one step further and made a reusable class out of it. It's part of my application log submit tool that should hide its About info in the system menu.
https://github.com/ygoe/FieldLog/blob/master/LogSubmit/Unclassified/UI/SystemMenu.cs
It can easily be used like this:
class MainForm : Form
{
private SystemMenu systemMenu;
public MainForm()
{
InitializeComponent();
// Create instance and connect it with the Form
systemMenu = new SystemMenu(this);
// Define commands and handler methods
// (Deferred until HandleCreated if it's too early)
// IDs are counted internally, separator is optional
systemMenu.AddCommand("&About…", OnSysMenuAbout, true);
}
protected override void WndProc(ref Message msg)
{
base.WndProc(ref msg);
// Let it know all messages so it can handle WM_SYSCOMMAND
// (This method is inlined)
systemMenu.HandleMessage(ref msg);
}
// Handle menu command click
private void OnSysMenuAbout()
{
MessageBox.Show("My about message");
}
}
I know this answer is old but I really liked LonelyPixel's answer. However, it needed some work to work correctly with WPF. Below is a WPF version I wrote, so you do not have to :).
/// <summary>
/// Extends the system menu of a window with additional commands.
/// Adapted from:
/// https://github.com/dg9ngf/FieldLog/blob/master/LogSubmit/Unclassified/UI/SystemMenu.cs
/// </summary>
public class SystemMenuExtension
{
#region Native methods
private const int WM_SYSCOMMAND = 0x112;
private const int MF_STRING = 0x0;
private const int MF_SEPARATOR = 0x800;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool AppendMenu(IntPtr hMenu, int uFlags, int uIDNewItem, string lpNewItem);
#endregion Native methods
#region Private data
private Window window;
private IntPtr hSysMenu;
private int lastId = 0;
private List<Action> actions = new List<Action>();
private List<CommandInfo> pendingCommands;
#endregion Private data
#region Constructors
/// <summary>
/// Initialises a new instance of the <see cref="SystemMenu"/> class for the specified
/// <see cref="Form"/>.
/// </summary>
/// <param name="window">The window for which the system menu is expanded.</param>
public SystemMenuExtension(Window window)
{
this.window = window;
if(this.window.IsLoaded)
{
WindowLoaded(null, null);
}
else
{
this.window.Loaded += WindowLoaded;
}
}
#endregion Constructors
#region Public methods
/// <summary>
/// Adds a command to the system menu.
/// </summary>
/// <param name="text">The displayed command text.</param>
/// <param name="action">The action that is executed when the user clicks on the command.</param>
/// <param name="separatorBeforeCommand">Indicates whether a separator is inserted before the command.</param>
public void AddCommand(string text, Action action, bool separatorBeforeCommand)
{
int id = ++this.lastId;
if (!this.window.IsLoaded)
{
// The window is not yet created, queue the command for later addition
if (this.pendingCommands == null)
{
this.pendingCommands = new List<CommandInfo>();
}
this.pendingCommands.Add(new CommandInfo
{
Id = id,
Text = text,
Action = action,
Separator = separatorBeforeCommand
});
}
else
{
// The form is created, add the command now
if (separatorBeforeCommand)
{
AppendMenu(this.hSysMenu, MF_SEPARATOR, 0, "");
}
AppendMenu(this.hSysMenu, MF_STRING, id, text);
}
this.actions.Add(action);
}
#endregion Public methods
#region Private methods
private void WindowLoaded(object sender, RoutedEventArgs e)
{
var interop = new WindowInteropHelper(this.window);
HwndSource source = PresentationSource.FromVisual(this.window) as HwndSource;
source.AddHook(WndProc);
this.hSysMenu = GetSystemMenu(interop.EnsureHandle(), false);
// Add all queued commands now
if (this.pendingCommands != null)
{
foreach (CommandInfo command in this.pendingCommands)
{
if (command.Separator)
{
AppendMenu(this.hSysMenu, MF_SEPARATOR, 0, "");
}
AppendMenu(this.hSysMenu, MF_STRING, command.Id, command.Text);
}
this.pendingCommands = null;
}
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_SYSCOMMAND)
{
if ((long)wParam > 0 && (long)wParam <= lastId)
{
this.actions[(int)wParam - 1]();
}
}
return IntPtr.Zero;
}
#endregion Private methods
#region Classes
private class CommandInfo
{
public int Id { get; set; }
public string Text { get; set; }
public Action Action { get; set; }
public bool Separator { get; set; }
}
#endregion Classes
The value-add is rather small for the amount of pinvoke you'll need. But it is possible. Use GetSystemMenu() to retrieve the system menu handle. Then InsertMenuItem to add an entry. You have to do this in an override of OnHandleCreated() so you recreate the menu when the window gets recreated.
Override WndProc() to recognize the WM_SYSCOMMAND message that's generated when the user clicks it. Visit pinvoke.net for the pinvoke declarations you'll need.
VB.NET version of accepted answer:
Imports System.Windows.Forms
Imports System.Runtime.InteropServices
Public Class CustomForm
Inherits Form
' P/Invoke constants
Private Const WM_SYSCOMMAND As Integer = &H112
Private Const MF_STRING As Integer = &H0
Private Const MF_SEPARATOR As Integer = &H800
' P/Invoke declarations
<DllImport("user32.dll", CharSet := CharSet.Auto, SetLastError := True)> _
Private Shared Function GetSystemMenu(hWnd As IntPtr, bRevert As Boolean) As IntPtr
End Function
<DllImport("user32.dll", CharSet := CharSet.Auto, SetLastError := True)> _
Private Shared Function AppendMenu(hMenu As IntPtr, uFlags As Integer, uIDNewItem As Integer, lpNewItem As String) As Boolean
End Function
<DllImport("user32.dll", CharSet := CharSet.Auto, SetLastError := True)> _
Private Shared Function InsertMenu(hMenu As IntPtr, uPosition As Integer, uFlags As Integer, uIDNewItem As Integer, lpNewItem As String) As Boolean
End Function
' ID for the About item on the system menu
Private SYSMENU_ABOUT_ID As Integer = &H1
Public Sub New()
End Sub
Protected Overrides Sub OnHandleCreated(e As EventArgs)
MyBase.OnHandleCreated(e)
' Get a handle to a copy of this form's system (window) menu
Dim hSysMenu As IntPtr = GetSystemMenu(Me.Handle, False)
' Add a separator
AppendMenu(hSysMenu, MF_SEPARATOR, 0, String.Empty)
' Add the About menu item
AppendMenu(hSysMenu, MF_STRING, SYSMENU_ABOUT_ID, "&About…")
End Sub
Protected Overrides Sub WndProc(ByRef m As Message)
MyBase.WndProc(m)
' Test if the About item was selected from the system menu
If (m.Msg = WM_SYSCOMMAND) AndAlso (CInt(m.WParam) = SYSMENU_ABOUT_ID) Then
MessageBox.Show("Custom About Dialog")
End If
End Sub
End Class

Categories

Resources