Try to use InputSimulator to simulate keyboard inputs. Everything works fine except using sim.Keyboard.ModifiedKeyStroke to simulate the input of ASCII character.
I tried to simulate Alt down + numpad1 + numpad4 + numpad7 + Alt up using the following two different ways:
sim.Keyboard.ModifiedKeyStroke(VirtualKeyCode.LMENU, new[] { VirtualKeyCode.NUMPAD1, VirtualKeyCode.NUMPAD4, VirtualKeyCode.NUMPAD7});
and
sim.Keyboard.KeyDown(VirtualKeyCode.LMENU);
sim.Keyboard.KeyDown(VirtualKeyCode.NUMPAD1);
sim.Keyboard.KeyUp(VirtualKeyCode.NUMPAD1);
sim.Keyboard.KeyDown(VirtualKeyCode.NUMPAD4);
sim.Keyboard.KeyUp(VirtualKeyCode.NUMPAD4);
sim.Keyboard.KeyDown(VirtualKeyCode.NUMPAD7);
sim.Keyboard.KeyUp(VirtualKeyCode.NUMPAD7);
sim.Keyboard.KeyUp(VirtualKeyCode.LMENU);
Neither works. I try to print out key status in console, the real key press and simulated key press both give the same result:
LMenu key down
NumPad1 key down
NumPad1 key up
NumPad4 key down
NumPad4 key up
NumPad7 key down
NumPad7 key up
LMenu key up
I think should be some problems with the library: issue 1. Could anyone help me with this please? Is there any other way to do this?
Update 1
I found "Alt+Tab" is also not working in Win8. I thought this may be the same reason so I try to fix this first. It turns out they are two different problems:
To make "Alt+Tab" working, I need to set uiAccess=true in "app.manifest" and sign the ".exe" file using a test digital signature;
Simulating the input of ASCII characters still not working.
Use keybd_event with MapVirtualKey or if using SendInput add to INPUT's Scan MapVirtualKey.
Here is example with both,
using System;
using System.Threading;
using System.Runtime.InteropServices;
namespace kbd_events_example
{
static class Program
{
#pragma warning disable 649
internal struct INPUT
{
public UInt32 Type;
public KEYBOARDMOUSEHARDWARE Data;
}
[StructLayout(LayoutKind.Explicit)]
//This is KEYBOARD-MOUSE-HARDWARE union INPUT won't work if you remove MOUSE or HARDWARE
internal struct KEYBOARDMOUSEHARDWARE
{
[FieldOffset(0)]
public KEYBDINPUT Keyboard;
[FieldOffset(0)]
public HARDWAREINPUT Hardware;
[FieldOffset(0)]
public MOUSEINPUT Mouse;
}
internal struct KEYBDINPUT
{
public UInt16 Vk;
public UInt16 Scan;
public UInt32 Flags;
public UInt32 Time;
public IntPtr ExtraInfo;
}
internal struct MOUSEINPUT
{
public Int32 X;
public Int32 Y;
public UInt32 MouseData;
public UInt32 Flags;
public UInt32 Time;
public IntPtr ExtraInfo;
}
internal struct HARDWAREINPUT
{
public UInt32 Msg;
public UInt16 ParamL;
public UInt16 ParamH;
}
#pragma warning restore 649
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, uint extraInfo);
[DllImport("user32.dll", SetLastError = true)]
static extern int MapVirtualKey(uint uCode, uint uMapType);
[DllImport("user32.dll", SetLastError = true)]
static extern UInt32 SendInput(UInt32 numberOfInputs, INPUT[] inputs, Int32 sizeOfInputStructure);
enum VK
{
MENU = 0x12,
NUMPAD0 = 0x60,
NUMPAD1 = 0x61,
NUMPAD2 = 0x62,
NUMPAD3 = 0x63,
NUMPAD4 = 0x64,
NUMPAD5 = 0x65,
NUMPAD6 = 0x66,
NUMPAD7 = 0x67,
NUMPAD8 = 0x68,
NUMPAD9 = 0x69
}
const uint KEYEVENTF_KEYUP = 0x0002;
public const int INPUT_KEYBOARD = 1;
[STAThread]
static void Main()
{
Thread.Sleep(5000); //wait 5 seconds, so you can focus the Notepad
keybd_event((int)VK.MENU, (byte)MapVirtualKey((uint)VK.MENU, 0), 0, 0); //Alt Press
keybd_event((int)VK.NUMPAD1, (byte)MapVirtualKey((uint)VK.NUMPAD1, 0), 0, 0); // N1 Press
keybd_event((int)VK.NUMPAD1, (byte)MapVirtualKey((uint)VK.NUMPAD1, 0), KEYEVENTF_KEYUP, 0); // N1 Release
keybd_event((int)VK.MENU, (byte)MapVirtualKey((uint)VK.MENU, 0), KEYEVENTF_KEYUP, 0); // Alt Release
Thread.Sleep(2000); //wait 2 seconds
INPUT[] inputs = new INPUT[] {
new INPUT {Type = INPUT_KEYBOARD, Data = new KEYBOARDMOUSEHARDWARE { Keyboard = new KEYBDINPUT { Vk = (ushort)VK.MENU, Flags = 0, Scan = (ushort)MapVirtualKey((uint)VK.MENU, 0), ExtraInfo = IntPtr.Zero, Time = 0}}},
new INPUT {Type = INPUT_KEYBOARD, Data = new KEYBOARDMOUSEHARDWARE { Keyboard = new KEYBDINPUT { Vk = (ushort)VK.NUMPAD2, Flags = 0, Scan = (ushort)MapVirtualKey((uint)VK.NUMPAD2, 0), ExtraInfo = IntPtr.Zero, Time = 0}}},
new INPUT {Type = INPUT_KEYBOARD, Data = new KEYBOARDMOUSEHARDWARE { Keyboard = new KEYBDINPUT { Vk = (ushort)VK.NUMPAD2, Flags = 2, Scan = (ushort)MapVirtualKey((uint)VK.NUMPAD2, 0), ExtraInfo = IntPtr.Zero, Time = 0}}},
new INPUT {Type = INPUT_KEYBOARD, Data = new KEYBOARDMOUSEHARDWARE { Keyboard = new KEYBDINPUT { Vk = (ushort)VK.MENU, Flags = 2, Scan = (ushort)MapVirtualKey((uint)VK.MENU, 0), ExtraInfo = IntPtr.Zero, Time = 0}}}
};
SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(typeof(INPUT)));
}
}
}
As for InputSimulator, you need to write your own class using the SendInput or keybd_event with MapVirtualKey to handle inputs...
Cœur, what version of InputSimulator were you using. Version 1.0.4 has the ModifiedKeyStroke(IEnumerable modifierKeyCodes, IEnumerable keyCodes) method which should allow the following:
sim.Keyboard.ModifiedKeyStroke(new[] { VirtualKeyCode.LMENU }, new[] { VirtualKeyCode.NUMPAD1, VirtualKeyCode.NUMPAD4, VirtualKeyCode.NUMPAD7 });
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.IO;
using System.Threading;
using WindowsInput;
namespace WindowsMacros
{
class Program
{
// import function in your class
[DllImport("User32.dll")]
static extern int SetForegroundWindow(IntPtr point);
static string programnamepath = #"C:\Users\Murzic2\Desktop\Resize.exe — ярлык.lnk";
static string FolderDeafault = #"C:\Users\Murzic2\Desktop\Resize";
static void Main(string[] args)
{
MethodResizezing();
}// End Main
private static void MethodResizezing()
{
if (!Directory.Exists(FolderDeafault))
{
Directory.CreateDirectory(FolderDeafault);
}
Process process = Process.Start(programnamepath);
Process[] processes = Process.GetProcessesByName("Resize"); // Programm Name
Process resize = new Process();
foreach (Process str in processes)
{
if (str.ProcessName.Contains("Resize"))
{
resize = str;
Console.WriteLine(resize.ProcessName);
}
}
if (resize != null)
{
Console.WriteLine("Begin programm into focus ...");
IntPtr h = resize.MainWindowHandle;
SetForegroundWindow(h);
Console.WriteLine("Getting Out Of Programm");
InputSimulator isim = new InputSimulator(); // From Library InputSimulator
if (resize.StartInfo.CreateNoWindow)
{
Console.WriteLine(resize.StartTime);
}
Thread.Sleep(3000);// After need write according process stoped
MouseMove(isim, 222, 83); // Move Mouse to Open Folder Window
isim.Mouse.LeftButtonClick(); // Open Directory Window Window
MouseMove(isim, 297, 269); //
isim.Mouse.LeftButtonClick();
Thread.Sleep(200);
isim.Keyboard.ModifiedKeyStroke(WindowsInput.Native.VirtualKeyCode.CONTROL, WindowsInput.Native.VirtualKeyCode.VK_A);// Select all Key Stroke Ctrl+A
Thread.Sleep(200);
isim.Keyboard.KeyPress(WindowsInput.Native.VirtualKeyCode.RETURN); // Enter Key
MouseMove(isim, 1468, 796);
isim.Mouse.LeftButtonClick();
Thread.Sleep(200);
isim.Keyboard.KeyPress(WindowsInput.Native.VirtualKeyCode.RETURN); // Enter Key
Thread.Sleep(800);
}
Console.ReadKey();
// open this code after finishing ))
foreach (Process prc in processes)
{
if (prc.ProcessName.Contains("Resize"))
{
prc.Kill();
}
}
}
private static void MouseMove(InputSimulator isim,double X, double Y)
{
//X = 222; -- Coordintes on scree`enter code here`n which you need
//Y = 83; -- Coordinates on screen which you need
Thread.Sleep(350);
X = X * 65535 / 1535; // Converting trgarding yours screen
Y = Y * 65535 / 862;// Converting trgarding yours screen
isim.Mouse.MoveMouseTo(Convert.ToDouble(X), Convert.ToDouble(Y)); //
Thread.Sleep(150);
}
}
}
Related
I would like to load the icons from multiple files dragged onto a listview and some of the information of those files, but the process is going very slow.
In my test, a list of 381 files dragged (some are not exe so those are skipped in my code), takes over 2 minutes to load the icon from the file, and add them to the listview.
Following is condensed version of my code :
public Form1()
{
InitializeComponent();
listView1.DragEnter += ListView1_DragEnter;
listView1.DragDrop += ListView1_DragDrop;
listView1.LargeImageList = new ImageList() { ImageSize = new Size( 64, 64) };
listView1.SmallImageList = new ImageList();
listView1.AllowDrop = true;
}
private void ListView1_DragDrop(object sender, DragEventArgs e)
{
if(e.Data.GetDataPresent(DataFormats.FileDrop))
{
string[] values = (string[])e.Data.GetData(DataFormats.FileDrop);
Convert.IconExtractor i = new Convert.IconExtractor();
foreach (var v in values)
{
var info = new FileInfo(v);
if(info.Extension.ToLower() == ".exe")
{
ListViewItem item = new ListViewItem();
listView1.LargeImageList.Images.Add(i.Extract(info.FullName, Convert.IconSize.Large));
listView1.SmallImageList.Images.Add(i.Extract(info.FullName, Convert.IconSize.Small));
item.Text = Path.GetFileNameWithoutExtension(info.FullName);
item.ImageIndex = listView1.SmallImageList.Images.Count -1;
item.Tag = info.FullName;
listView1.Items.Add(item);
}
}
listView1.Refresh();
}
}
private void ListView1_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
e.Effect = DragDropEffects.All;
}
}
The method used for extraction is pasted here for convenience :
public enum IconSize
{
Small,
Large
}
public class IconExtractor
{
//----------------------------------------------------------------------------
//
// Description: Extracts the icon associated with any file on your system.
// Author: WidgetMan http://softwidgets.com
//
// Remarks...
//
// Class requires the IconSize enumeration that is implemented in this
// same file. For best results, draw an icon from within a control's Paint
// event via the e.Graphics.DrawIcon method.
//
//----------------------------------------------------------------------------
private const int SHGFI_ICON = 0x100;
private const int SHGFI_SMALLICON = 0x1;
private const int SHGFI_LARGEICON = 0x0;
private struct SHFILEINFO
{
public IntPtr hIcon;
public int iIcon;
public int dwAttributes;
[VBFixedString(260), MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szDisplayName;
[VBFixedString(80), MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string szTypeName;
}
[DllImport("shell32", EntryPoint = "SHGetFileInfoA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int SHGetFileInfo(string pszPath, int dwFileAttributes, ref SHFILEINFO psfi, int ByValcbFileInfo, int uFlags);
public IconExtractor()
{
}
public System.Drawing.Icon Extract(string File, IconSize Size)
{
SHFILEINFO aSHFileInfo = default(SHFILEINFO);
int cbFileInfo = 0;
int uflags = 0;
System.Drawing.Icon Icon = default(System.Drawing.Icon);
switch (Size)
{
case IconSize.Large:
uflags = SHGFI_ICON | SHGFI_LARGEICON;
break;
default:
uflags = SHGFI_ICON | SHGFI_SMALLICON;
break;
}
cbFileInfo = Marshal.SizeOf(aSHFileInfo);
SHGetFileInfo(File, 0, ref aSHFileInfo, cbFileInfo, uflags);
Icon = System.Drawing.Icon.FromHandle(aSHFileInfo.hIcon);
return Icon;
}
public System.Drawing.Icon Extract(string File)
{
return this.Extract(File, IconSize.Small);
}
}
}
What can I do to make this process quick. The concept is to make a quick launcher for multiple applications.
Also of note, while this process is happening, the drag-collection icon is still 'hung' on windows explorer until the drag & drop task has completed fully (looped through all the files).
Here is a rough draft to give a visual of the application:
(yes, i know the icons extracted look like crap as well, but I think that is a separate issue from the slow issue I am having)
My advice is not to load all 381 icons in one step, particularly if the items are not visible. Load them on demand as items are scrolled into view, of if the view is large enough, load them in the background using your threading/task/concurrency technology of choice.
That's what Windows does.
To speed up loading, you may want to use the System Image List which would most likely benefit from caching. Here's some code for getting the large icon. Just change size to be SHGFI_ICON for your use.
public static Icon GetLargeIcon(string FileName, bool jumbo, bool useFileAttributes=false)
{
var shinfo = new SHFILEINFO();
uint flags;
flags = SHGFI_SYSICONINDEX;
if (useFileAttributes)
{
flags |= SHGFI_USEFILEATTRIBUTES;
}
var res = SHGetFileInfo(FileName, FILE_ATTRIBUTE_NORMAL, ref shinfo, (uint) Marshal.SizeOf(shinfo), flags);
if (res == IntPtr.Zero)
{
throw (new FileNotFoundException());
}
var iconIndex = shinfo.iIcon;
// Get the System IImageList object from the Shell:
var iidImageList = new Guid("46EB5926-582E-4017-9FDF-E8998DAA0950");
IImageList iml;
var size = jumbo
? SHIL_JUMBO
: SHIL_EXTRALARGE;
var hres = SHGetImageList(size, iidImageList, out iml);
if (hres != 0)
{
throw (new Exception("Error SHGetImageList"));
}
IntPtr hIcon;
const int ILD_TRANSPARENT = 1;
hres = iml.GetIcon(iconIndex, ILD_TRANSPARENT, out hIcon);
var icon = Icon.FromHandle(hIcon);
icon = icon.Clone() as Icon;
var bm = icon.ToBitmap();
DestroyIcon(hIcon);
return icon;
}
I want to use SystemIcons.Warning but it is too big for my need. I want to resize it.
I have tried :
Icon sizedIcon = new Icon(SystemIcons.Warning, new Size(10,10));
But it does not work, icon remains the same.
Any suggestions?
The .NET Icon class is pretty crippled, it is stuck in the previous decade due to once-relevant support for Windows 98 and Windows 2000. On top of this, Windows does not support loading the system icons in any size other than the system's default icon size. Usually 32x32. Resizing it is going to inevitably look bad.
Do keep in mind that no icon is ever going to look good at 10x10. It is but a fleck on a modern monitor. You do get ahead a bit by starting with an icon size that's close to the desired final size, the less drastic the required resize, the higher the odds that it still looks reasonable. When you can, do favor sizes that are likely to be present in the icon. Like 16x16, 32x32, 48x48.
Anyhoo, this is fixable. Do keep in mind that this takes considerable hack-o-rama since Windows doesn't directly support this. What's required is reading the icon directly from the system DLL resources. And using a more modern winapi, LoadImage() instead of the one that .NET uses, LoadIcon(). Works on XP and up.
Add a new class to your project and paste this code:
using System;
using System.Drawing;
using System.Reflection;
using System.Runtime.InteropServices;
public class IconEx : IDisposable {
public enum SystemIcons {
Application = 100,
Asterisk = 104,
Error = 103,
Exclamation = 101,
Hand = 103,
Information = 104,
Question = 102,
Shield = 106,
Warning = 101,
WinLogo = 100
}
public IconEx(string path, Size size) {
IntPtr hIcon = LoadImage(IntPtr.Zero, path, IMAGE_ICON, size.Width, size.Height, LR_LOADFROMFILE);
if (hIcon == IntPtr.Zero) throw new System.ComponentModel.Win32Exception();
attach(hIcon);
}
public IconEx(SystemIcons sysicon, Size size) {
IntPtr hUser = GetModuleHandle("user32");
IntPtr hIcon = LoadImage(hUser, (IntPtr)sysicon, IMAGE_ICON, size.Width, size.Height, 0);
if (hIcon == IntPtr.Zero) throw new System.ComponentModel.Win32Exception();
attach(hIcon);
}
public Icon Icon {
get { return this.icon; }
}
public void Dispose() {
if (icon != null) icon.Dispose();
}
private Icon icon;
private void attach(IntPtr hIcon) {
// Invoke the private constructor so we can get the Icon object to own the handle
var ci = typeof(Icon).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance,
null, new Type[] { typeof(IntPtr), typeof(bool) }, null);
this.icon = (Icon)ci.Invoke(new object[] { hIcon, true});
}
private const int IMAGE_ICON = 1;
private const int LR_LOADFROMFILE = 0x10;
private const int LR_SHARED = 0x8000;
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr GetModuleHandle(string name);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr LoadImage(IntPtr hinst, string lpszName, int uType,
int cxDesired, int cyDesired, int fuLoad);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr LoadImage(IntPtr hinst, IntPtr resId, int uType,
int cxDesired, int cyDesired, int fuLoad);
}
Sample usage:
using (var icon = new IconEx(IconEx.SystemIcons.Warning, new Size(10, 10))) {
e.Graphics.DrawIcon(icon.Icon, 0, 0);
}
It does look better than SystemIcons.Warning. It's squeaky clean when you use 16x16.
I know how to print an image using PrintDocument. However, i want to print my Image using the default windows print function. Like when you right click an image and click print, the dialog comes up that allows you to set size choose printer etc. Does anyone know how to achieve this in C#? Do i have to use WINAPI ?
Cheers
Edit:
I'm talking about this print dialog.
You can launch that dialog with the Process class.
private void button1_Click(object sender, EventArgs e)
{
string fileName = #"C:\Development\myImage.tif";//pass in or whatever you need
var p = new Process();
p.StartInfo.FileName = fileName;
p.StartInfo.Verb = "Print";
p.Start();
}
The simple approach with launching a new process using verb "print" is not working on Windows XP at all (it opens Windows Picture and Fax Viewer instead of the Printing Wizard). Also it does not work as intended on Windows 10 (at first run the Default app chooser for images is opened, then the default photo viewer is opened).
The correct approach would be using CLSID_PrintPhotosDropTarget COM object.
My code is in C++ (and ATL) but I hope you could translate it in C#.
I jast pass file names, but AFAIK you can pass picture itself directly without writing it on disk implementing IDataObject interface.
bool DisplaySystemPrintDialogForImage(const std::vector<CString>& files, HWND hwnd) {
static const CLSID CLSID_PrintPhotosDropTarget ={ 0x60fd46de, 0xf830, 0x4894, { 0xa6, 0x28, 0x6f, 0xa8, 0x1b, 0xc0, 0x19, 0x0d } };
CComPtr<IShellFolder> desktop; // namespace root for parsing the path
HRESULT hr = SHGetDesktopFolder(&desktop);
if (!SUCCEEDED(hr)) {
return false;
}
CComPtr<IShellItem> psi;
CComPtr<IDataObject> pDataObject;
std::vector<LPITEMIDLIST> list;
for (const auto& fileName : files) {
PIDLIST_RELATIVE newPIdL;
hr = desktop->ParseDisplayName(hwnd, nullptr, const_cast<LPWSTR>(static_cast<LPCTSTR>(fileName)), nullptr, &newPIdL, nullptr);
if (SUCCEEDED(hr)) {
list.push_back(newPIdL);
}
}
if (!list.empty()) {
hr = desktop->GetUIObjectOf(hwnd, list.size(), const_cast<LPCITEMIDLIST*>(&list[0]), IID_IDataObject, 0, reinterpret_cast<void**>(&pDataObject));
if (SUCCEEDED(hr)) {
// Create the Photo Printing Wizard drop target.
CComPtr<IDropTarget> spDropTarget;
hr = CoCreateInstance(CLSID_PrintPhotosDropTarget, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&spDropTarget));
if (SUCCEEDED(hr)) {
// Drop the data object onto the drop target.
POINTL pt = { 0 };
DWORD dwEffect = DROPEFFECT_LINK | DROPEFFECT_MOVE | DROPEFFECT_COPY;
spDropTarget->DragEnter(pDataObject, MK_LBUTTON, pt, &dwEffect);
spDropTarget->Drop(pDataObject, MK_LBUTTON, pt, &dwEffect);
return true;
}
}
}
return false;
}
This works for me:
internal static class ShellHelper
{
[ComImport]
[Guid("00000122-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDropTarget
{
int DragEnter(
[In] System.Runtime.InteropServices.ComTypes.IDataObject pDataObj,
[In] int grfKeyState,
[In] Point pt,
[In, Out] ref int pdwEffect);
int DragOver(
[In] int grfKeyState,
[In] Point pt,
[In, Out] ref int pdwEffect);
int DragLeave();
int Drop(
[In] System.Runtime.InteropServices.ComTypes.IDataObject pDataObj,
[In] int grfKeyState,
[In] Point pt,
[In, Out] ref int pdwEffect);
}
internal static void PrintPhotosWizard(string p_FileName)
{
IDataObject v_DataObject = new DataObject(DataFormats.FileDrop, new string[] { p_FileName });
MemoryStream v_MemoryStream = new MemoryStream(4);
byte[] v_Buffer = new byte[] { (byte)5, 0, 0, 0 };
v_MemoryStream.Write(v_Buffer, 0, v_Buffer.Length);
v_DataObject.SetData("Preferred DropEffect", v_MemoryStream);
Guid CLSID_PrintPhotosDropTarget = new Guid("60fd46de-f830-4894-a628-6fa81bc0190d");
Type v_DropTargetType = Type.GetTypeFromCLSID(CLSID_PrintPhotosDropTarget, true);
IDropTarget v_DropTarget = (IDropTarget)Activator.CreateInstance(v_DropTargetType);
v_DropTarget.Drop((System.Runtime.InteropServices.ComTypes.IDataObject)v_DataObject, 0, new Point(), 0);
}
}
There's an existing question on StackOverflow on how to show a form without stealing focus. The answer is override ShowWithoutActivation and return true:
protected override bool ShowWithoutActivation
{
get { return true; }
}
This works well enough.
Now i want to go one step further. i want a show a Form (i.e. make it visible), but have it be behind other forms in the z-order.
Possible in .net?
If not, possible with P/Invoke?
Bonus Chatter
Calling SendToBack() doesn't work:
RunnerForm frm = new RunnerForm();
// frm.Show();
frm.Visible = true;
frm.SendToBack();
A little bit of PInvoke using SetWindowPos function
public static class HWND {
public static readonly IntPtr
NOTOPMOST = new IntPtr(-2),
BROADCAST = new IntPtr(0xffff),
TOPMOST = new IntPtr(-1),
TOP = new IntPtr(0),
BOTTOM = new IntPtr(1);
}
public static class SWP {
public static readonly int
NOSIZE = 0x0001,
NOMOVE = 0x0002,
NOZORDER = 0x0004,
NOREDRAW = 0x0008,
NOACTIVATE = 0x0010,
DRAWFRAME = 0x0020,
FRAMECHANGED = 0x0020,
SHOWWINDOW = 0x0040,
HIDEWINDOW = 0x0080,
NOCOPYBITS = 0x0100,
NOOWNERZORDER = 0x0200,
NOREPOSITION = 0x0200,
NOSENDCHANGING = 0x0400,
DEFERERASE = 0x2000,
ASYNCWINDOWPOS = 0x4000;
}
[DllImport("user32.dll")]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
private void button1_Click(object sender, EventArgs e) {
RunnerForm frm = new RunnerForm();
SetWindowPos(frm.Handle, HWND.BOTTOM, 0, 0, 0, 0, SWP.SHOWWINDOW | SWP.NOMOVE | SWP.NOOWNERZORDER | SWP.NOSIZE | SWP.NOACTIVATE);
}
To add on to the viable solution Lars provided, you could also prevent the user from ever changing the window's Z order altogether.
To do that, you would override the form's WndProc method and catch the WM_WINDOWPOSCHANGING message that is sent to a window when its size, position, or Z order position is about to change. The super cool thing about this message—I think it's one of my favorites—is that it actually allows you to change or modify the parameters of the change that is about to take place.
So in this case, you'll want to set the SWP_NOZORDER flag to prevent the window's Z order from being changed.
The notable thing about this approach is that it will always maintain your window in the Z order at the last position you left it. The user won't be able to bring it to the front, which may or may not be a good thing, depending on how your UI is designed. It will also work just fine with controls other than forms.
Sample code pulled from one of my libraries:
internal class NativeMethods
{
public const int WM_WINDOWPOSCHANGING = 0x46;
public const int WM_WINDOWPOSCHANGED = 0x47;
[Flags()]
public enum SetWindowPosFlags
{
SWP_NOSIZE = 0x1,
SWP_NOMOVE = 0x2,
SWP_NOZORDER = 0x4,
SWP_NOREDRAW = 0x8,
SWP_NOACTIVATE = 0x10,
SWP_FRAMECHANGED = 0x20,
SWP_DRAWFRAME = SWP_FRAMECHANGED,
SWP_SHOWWINDOW = 0x40,
SWP_HIDEWINDOW = 0x80,
SWP_NOCOPYBITS = 0x100,
SWP_NOOWNERZORDER = 0x200,
SWP_NOREPOSITION = SWP_NOOWNERZORDER,
SWP_NOSENDCHANGING = 0x400,
SWP_DEFERERASE = 0x2000,
SWP_ASYNCWINDOWPOS = 0x4000,
}
public enum WindowZOrder
{
HWND_TOP = 0,
HWND_BOTTOM = 1,
HWND_TOPMOST = -1,
HWND_NOTOPMOST = -2,
}
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWPOS
{
public IntPtr hWnd;
public IntPtr hwndInsertAfter;
public int x;
public int y;
public int cx;
public int cy;
public SetWindowPosFlags flags;
// Returns the WINDOWPOS structure pointed to by the lParam parameter
// of a WM_WINDOWPOSCHANGING or WM_WINDOWPOSCHANGED message.
public static WINDOWPOS FromMessage(Message msg)
{
// Marshal the lParam parameter to an WINDOWPOS structure,
// and return the new structure
return (WINDOWPOS)Marshal.PtrToStructure(msg.LParam, typeof(WINDOWPOS));
}
// Replaces the original WINDOWPOS structure pointed to by the lParam
// parameter of a WM_WINDOWPOSCHANGING or WM_WINDOWPSCHANGING message
// with this one, so that the native window will be able to see any
// changes that we have made to its values.
public void UpdateMessage(Message msg)
{
// Marshal this updated structure back to lParam so the native
// window can respond to our changes.
// The old structure that it points to should be deleted, too.
Marshal.StructureToPtr(this, msg.LParam, true);
}
}
}
And then I have a slick little subclassed form that raises .NET events corresponding to these messages and allows the event handler to modify the values or cancel the event if desired. I don't handle SWP_NOZORDER, but you can get an idea of how it might work.
public class FormEx : System.Windows.Forms.Form
{
// ...snip constructors and such
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case NativeMethods.WM_WINDOWPOSCHANGING:
this.WmWindowPosChanging(m);
return;
// ...snip
}
base.WndProc(m);
}
private void WmWindowPosChanging(ref Message m)
{
// Extract the WINDOWPOS structure corresponding to this message
NativeMethods.WINDOWPOS wndPos = NativeMethods.WINDOWPOS.FromMessage(m);
// Determine if the size is changing (absence of SWP_NOSIZE flag)
if (!((wndPos.flags & NativeMethods.SetWindowPosFlags.SWP_NOSIZE) == NativeMethods.SetWindowPosFlags.SWP_NOSIZE))
{
// Raise the LowLevelSizeChanging event
SizeChangingEventArgs e = new SizeChangingEventArgs(this.Size, new Size(wndPos.cx, wndPos.cy));
this.OnLowLevelSizeChanging(e);
// Determine if the user canceled the size changing event
if (e.Cancel)
{
// If so, add the SWP_NOSIZE flag
wndPos.flags = wndPos.flags | NativeMethods.SetWindowPosFlags.SWP_NOSIZE;
wndPos.UpdateMessage(m);
}
}
// Determine if the position is changing (absence of SWP_NOMOVE flag)
if (!((wndPos.flags & NativeMethods.SetWindowPosFlags.SWP_NOMOVE) == NativeMethods.SetWindowPosFlags.SWP_NOMOVE))
{
// Raise the LowLevelPositionChanging event
PositionChangingEventArgs e = new PositionChangingEventArgs(this.Location, new Point(wndPos.x, wndPos.y));
this.OnLowLevelPositionChanging(e);
// Determine if the user canceled the position changing event
if (e.Cancel)
{
// If so, add the SWP_NOMOVE flag
wndPos.flags = wndPos.flags | NativeMethods.SetWindowPosFlags.SWP_NOMOVE;
wndPos.UpdateMessage(m);
}
}
base.WndProc(m);
}
// ...snip event infrastructure
}
Edit: Hmm, this code was originally written in VB.NET and I ran it through an automatic translator. It looks like the refs didn't get properly inserted everywhere that they should be when making function calls. Follow the urging of the compiler to fix it...
This should do the trick:
RunnerForm frm = new RunnerForm();
myMainForm.Owner = frm;
How can one determine, in code, how long the machine is locked?
Other ideas outside of C# are also welcome.
I like the windows service idea (and have accepted it) for simplicity and cleanliness, but unfortunately I don't think it will work for me in this particular case. I wanted to run this on my workstation at work rather than home (or in addition to home, I suppose), but it's locked down pretty hard courtesy of the DoD. That's part of the reason I'm rolling my own, actually.
I'll write it up anyway and see if it works. Thanks everyone!
I hadn't found this before, but from any application you can hookup a SessionSwitchEventHandler. Obviously your application will need to be running, but so long as it is:
Microsoft.Win32.SystemEvents.SessionSwitch += new Microsoft.Win32.SessionSwitchEventHandler(SystemEvents_SessionSwitch);
void SystemEvents_SessionSwitch(object sender, Microsoft.Win32.SessionSwitchEventArgs e)
{
if (e.Reason == SessionSwitchReason.SessionLock)
{
//I left my desk
}
else if (e.Reason == SessionSwitchReason.SessionUnlock)
{
//I returned to my desk
}
}
I would create a Windows Service (a visual studio 2005 project type) that handles the OnSessionChange event as shown below:
protected override void OnSessionChange(SessionChangeDescription changeDescription)
{
if (changeDescription.Reason == SessionChangeReason.SessionLock)
{
//I left my desk
}
else if (changeDescription.Reason == SessionChangeReason.SessionUnlock)
{
//I returned to my desk
}
}
What and how you log the activity at that point is up to you, but a Windows Service provides quick and easy access to windows events like startup, shutdown, login/out, along with the lock and unlock events.
The solution below uses the Win32 API. OnSessionLock is called when the workstation is locked, and OnSessionUnlock is called when it is unlocked.
[DllImport("wtsapi32.dll")]
private static extern bool WTSRegisterSessionNotification(IntPtr hWnd,
int dwFlags);
[DllImport("wtsapi32.dll")]
private static extern bool WTSUnRegisterSessionNotification(IntPtr
hWnd);
private const int NotifyForThisSession = 0; // This session only
private const int SessionChangeMessage = 0x02B1;
private const int SessionLockParam = 0x7;
private const int SessionUnlockParam = 0x8;
protected override void WndProc(ref Message m)
{
// check for session change notifications
if (m.Msg == SessionChangeMessage)
{
if (m.WParam.ToInt32() == SessionLockParam)
OnSessionLock(); // Do something when locked
else if (m.WParam.ToInt32() == SessionUnlockParam)
OnSessionUnlock(); // Do something when unlocked
}
base.WndProc(ref m);
return;
}
void OnSessionLock()
{
Debug.WriteLine("Locked...");
}
void OnSessionUnlock()
{
Debug.WriteLine("Unlocked...");
}
private void Form1Load(object sender, EventArgs e)
{
WTSRegisterSessionNotification(this.Handle, NotifyForThisSession);
}
// and then when we are done, we should unregister for the notification
// WTSUnRegisterSessionNotification(this.Handle);
I know this is an old question but i have found a method to get the Lock State for a given session.
I found my answer here but it was in C++ so i translated as much as i can to C# to get the Lock State.
So here goes:
static class SessionInfo {
private const Int32 FALSE = 0;
private static readonly IntPtr WTS_CURRENT_SERVER = IntPtr.Zero;
private const Int32 WTS_SESSIONSTATE_LOCK = 0;
private const Int32 WTS_SESSIONSTATE_UNLOCK = 1;
private static bool _is_win7 = false;
static SessionInfo() {
var os_version = Environment.OSVersion;
_is_win7 = (os_version.Platform == PlatformID.Win32NT && os_version.Version.Major == 6 && os_version.Version.Minor == 1);
}
[DllImport("wtsapi32.dll")]
private static extern Int32 WTSQuerySessionInformation(
IntPtr hServer,
[MarshalAs(UnmanagedType.U4)] UInt32 SessionId,
[MarshalAs(UnmanagedType.U4)] WTS_INFO_CLASS WTSInfoClass,
out IntPtr ppBuffer,
[MarshalAs(UnmanagedType.U4)] out UInt32 pBytesReturned
);
[DllImport("wtsapi32.dll")]
private static extern void WTSFreeMemoryEx(
WTS_TYPE_CLASS WTSTypeClass,
IntPtr pMemory,
UInt32 NumberOfEntries
);
private enum WTS_INFO_CLASS {
WTSInitialProgram = 0,
WTSApplicationName = 1,
WTSWorkingDirectory = 2,
WTSOEMId = 3,
WTSSessionId = 4,
WTSUserName = 5,
WTSWinStationName = 6,
WTSDomainName = 7,
WTSConnectState = 8,
WTSClientBuildNumber = 9,
WTSClientName = 10,
WTSClientDirectory = 11,
WTSClientProductId = 12,
WTSClientHardwareId = 13,
WTSClientAddress = 14,
WTSClientDisplay = 15,
WTSClientProtocolType = 16,
WTSIdleTime = 17,
WTSLogonTime = 18,
WTSIncomingBytes = 19,
WTSOutgoingBytes = 20,
WTSIncomingFrames = 21,
WTSOutgoingFrames = 22,
WTSClientInfo = 23,
WTSSessionInfo = 24,
WTSSessionInfoEx = 25,
WTSConfigInfo = 26,
WTSValidationInfo = 27,
WTSSessionAddressV4 = 28,
WTSIsRemoteSession = 29
}
private enum WTS_TYPE_CLASS {
WTSTypeProcessInfoLevel0,
WTSTypeProcessInfoLevel1,
WTSTypeSessionInfoLevel1
}
public enum WTS_CONNECTSTATE_CLASS {
WTSActive,
WTSConnected,
WTSConnectQuery,
WTSShadow,
WTSDisconnected,
WTSIdle,
WTSListen,
WTSReset,
WTSDown,
WTSInit
}
public enum LockState {
Unknown,
Locked,
Unlocked
}
[StructLayout(LayoutKind.Sequential)]
private struct WTSINFOEX {
public UInt32 Level;
public UInt32 Reserved; /* I have observed the Data field is pushed down by 4 bytes so i have added this field as padding. */
public WTSINFOEX_LEVEL Data;
}
[StructLayout(LayoutKind.Sequential)]
private struct WTSINFOEX_LEVEL {
public WTSINFOEX_LEVEL1 WTSInfoExLevel1;
}
[StructLayout(LayoutKind.Sequential)]
private struct WTSINFOEX_LEVEL1 {
public UInt32 SessionId;
public WTS_CONNECTSTATE_CLASS SessionState;
public Int32 SessionFlags;
/* I can't figure out what the rest of the struct should look like but as i don't need anything past the SessionFlags i'm not going to. */
}
public static LockState GetSessionLockState(UInt32 session_id) {
IntPtr ppBuffer;
UInt32 pBytesReturned;
Int32 result = WTSQuerySessionInformation(
WTS_CURRENT_SERVER,
session_id,
WTS_INFO_CLASS.WTSSessionInfoEx,
out ppBuffer,
out pBytesReturned
);
if (result == FALSE)
return LockState.Unknown;
var session_info_ex = Marshal.PtrToStructure<WTSINFOEX>(ppBuffer);
if (session_info_ex.Level != 1)
return LockState.Unknown;
var lock_state = session_info_ex.Data.WTSInfoExLevel1.SessionFlags;
WTSFreeMemoryEx(WTS_TYPE_CLASS.WTSTypeSessionInfoLevel1, ppBuffer, pBytesReturned);
if (_is_win7) {
/* Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/ee621019(v=vs.85).aspx
* Windows Server 2008 R2 and Windows 7: Due to a code defect, the usage of the WTS_SESSIONSTATE_LOCK
* and WTS_SESSIONSTATE_UNLOCK flags is reversed. That is, WTS_SESSIONSTATE_LOCK indicates that the
* session is unlocked, and WTS_SESSIONSTATE_UNLOCK indicates the session is locked.
* */
switch (lock_state) {
case WTS_SESSIONSTATE_LOCK:
return LockState.Unlocked;
case WTS_SESSIONSTATE_UNLOCK:
return LockState.Locked;
default:
return LockState.Unknown;
}
}
else {
switch (lock_state) {
case WTS_SESSIONSTATE_LOCK:
return LockState.Locked;
case WTS_SESSIONSTATE_UNLOCK:
return LockState.Unlocked;
default:
return LockState.Unknown;
}
}
}
}
Note: The above code was extracted from a much larger project so if i missed a bit sorry. I havn't got time to test the above code but plan to come back in a week or two to check everything. I only posted it now because i didn't want to forget to do it.
NOTE: This is not an answer, but a (contribution) to Timothy Carter answer, because my reputation doesn't allow me to comment so far.
Just in case somebody tried the code from Timothy Carter's answer and did not get it to work right away in a Windows service, there's one property that need to be set to true in the constructor of the service.
Just add the line in the constructor:
CanHandleSessionChangeEvent = true;
And be sure not to set this property after the service is started otherwise an InvalidOperationException will be thrown.
If you're interested in writing a windows-service to "find" these events, topshelf (the library/framework that makes writing windows services much easier) has a hook.
public interface IMyServiceContract
{
void Start();
void Stop();
void SessionChanged(Topshelf.SessionChangedArguments args);
}
public class MyService : IMyServiceContract
{
public void Start()
{
}
public void Stop()
{
}
public void SessionChanged(SessionChangedArguments e)
{
Console.WriteLine(e.ReasonCode);
}
}
and now the code to wire up the topshelf service to the interface/concrete above
Everything below is "typical" topshelf setup.... except for 2 lines which I marked as
/* THIS IS MAGIC LINE */
Those are what get the SessionChanged method to fire.
I tested this with windows 10 x64. I locked and unlocked my machine and I got the desired result.
IMyServiceContract myServiceObject = new MyService(); /* container.Resolve<IMyServiceContract>(); */
HostFactory.Run(x =>
{
x.Service<IMyServiceContract>(s =>
{
s.ConstructUsing(name => myServiceObject);
s.WhenStarted(sw => sw.Start());
s.WhenStopped(sw => sw.Stop());
s.WhenSessionChanged((csm, hc, chg) => csm.SessionChanged(chg)); /* THIS IS MAGIC LINE */
});
x.EnableSessionChanged(); /* THIS IS MAGIC LINE */
/* use command line variables for the below commented out properties */
/*
x.RunAsLocalService();
x.SetDescription("My Description");
x.SetDisplayName("My Display Name");
x.SetServiceName("My Service Name");
x.SetInstanceName("My Instance");
*/
x.StartManually(); // Start the service manually. This allows the identity to be tweaked before the service actually starts
/* the below map to the "Recover" tab on the properties of the Windows Service in Control Panel */
x.EnableServiceRecovery(r =>
{
r.OnCrashOnly();
r.RestartService(1); ////first
r.RestartService(1); ////second
r.RestartService(1); ////subsequents
r.SetResetPeriod(0);
});
x.DependsOnEventLog(); // Windows Event Log
x.UseLog4Net();
x.EnableShutdown();
x.OnException(ex =>
{
/* Log the exception */
/* not seen, I have a log4net logger here */
});
});
My packages.config to provide hints about versions:
<package id="log4net" version="2.0.5" targetFramework="net45" />
<package id="Topshelf" version="4.0.3" targetFramework="net461" />
<package id="Topshelf.Log4Net" version="4.0.3" targetFramework="net461" />
In Windows Task Scheduler, you could create tasks that trigger on workstation lock and on workstation unlock. Each task could write a flag and timestamp to a file to state if the workstation is locked or unlocked and when it happened.
I realize that this is not a programmatic way. It is simpler than writing a service. It won't miss an event because your program happens to not be running at the time of lock/unlock transition.
Below is the 100% working code to find if the PC is locked or not.
Before using this use the namespace System.Runtime.InteropServices.
[DllImport("user32", EntryPoint = "OpenDesktopA", CharSet = CharSet.Ansi,SetLastError = true, ExactSpelling = true)]
private static extern Int32 OpenDesktop(string lpszDesktop, Int32 dwFlags, bool fInherit, Int32 dwDesiredAccess);
[DllImport("user32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern Int32 CloseDesktop(Int32 hDesktop);
[DllImport("user32", CharSet = CharSet.Ansi,SetLastError = true,ExactSpelling = true)]
private static extern Int32 SwitchDesktop(Int32 hDesktop);
public static bool IsWorkstationLocked()
{
const int DESKTOP_SWITCHDESKTOP = 256;
int hwnd = -1;
int rtn = -1;
hwnd = OpenDesktop("Default", 0, false, DESKTOP_SWITCHDESKTOP);
if (hwnd != 0)
{
rtn = SwitchDesktop(hwnd);
if (rtn == 0)
{
// Locked
CloseDesktop(hwnd);
return true;
}
else
{
// Not locked
CloseDesktop(hwnd);
}
}
else
{
// Error: "Could not access the desktop..."
}
return false;
}