I want to perform action on button click that debugging should be stopped when the button is clicked:
private void button3_Click(object sender, EventArgs e)
{
//write code here to stop debugging
}
How do you mean 'stop debugging' you can use the Detach option in Visual Studio, but that isn't in code. To exit the application while debugging use this (windows forms code):
if (Debugger.IsAttached)
{
Application.Exit();
}
If you are after programmatically detaching debugger, you need to get a reference to the currently running EnvDTE80.DTE2 object. Once you have that, you could try:
var dte = ...
dte.Debugger.DetachAll()
To get a reference to EnvDTE80.DTE2, adabyron's approach seems to work:
Get the reference of the DTE2 object in Visual C# 2010
You can wrap it all in some class like so:
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using EnvDTE80;
class DetachDebugger
{
[DllImport("ole32.dll")]
private static extern void CreateBindCtx(int reserved, out IBindCtx ppbc);
[DllImport("ole32.dll")]
private static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable prot);
public static void Detach()
{
var dte = GetCurrent();
dte.Debugger.DetachAll();
}
/// <summary>
/// Gets the current visual studio's solution DTE2
/// </summary>
private static DTE2 GetCurrent()
{
List<DTE2> dte2s = new List<DTE2>();
IRunningObjectTable rot;
GetRunningObjectTable(0, out rot);
IEnumMoniker enumMoniker;
rot.EnumRunning(out enumMoniker);
enumMoniker.Reset();
IntPtr fetched = IntPtr.Zero;
IMoniker[] moniker = new IMoniker[1];
while (enumMoniker.Next(1, moniker, fetched) == 0)
{
IBindCtx bindCtx;
CreateBindCtx(0, out bindCtx);
string displayName;
moniker[0].GetDisplayName(bindCtx, null, out displayName);
// add all VisualStudio ROT entries to list
if (displayName.StartsWith("!VisualStudio"))
{
object comObject;
rot.GetObject(moniker[0], out comObject);
dte2s.Add((DTE2)comObject);
}
}
// get path of the executing assembly (assembly that holds this code) - you may need to adapt that to your setup
string thisPath = System.Reflection.Assembly.GetExecutingAssembly().Location;
// compare dte solution paths to find best match
KeyValuePair<DTE2, int> maxMatch = new KeyValuePair<DTE2, int>(null, 0);
foreach (DTE2 dte2 in dte2s)
{
int matching = GetMatchingCharsFromStart(thisPath, dte2.Solution.FullName);
if (matching > maxMatch.Value)
maxMatch = new KeyValuePair<DTE2, int>(dte2, matching);
}
return (DTE2)maxMatch.Key;
}
/// <summary>
/// Gets index of first non-equal char for two strings
/// Not case sensitive.
/// </summary>
private static int GetMatchingCharsFromStart(string a, string b)
{
a = (a ?? string.Empty).ToLower();
b = (b ?? string.Empty).ToLower();
int matching = 0;
for (int i = 0; i < Math.Min(a.Length, b.Length); i++)
{
if (!char.Equals(a[i], b[i]))
break;
matching++;
}
return matching;
}
}
Then, using your event handler:
private void button3_Click(object sender, EventArgs e)
{
//write code here to stop debugging
DetachDebugger.Detach();
}
Related
In a Winform application I have an array of this format:
float[][,]
I need to copy it via the clipboard to another Winform app.
How could I achieve this?
Thanks
Short answer: Using the System.Windows.Forms.Clipboard is simple.
Use Clipboard.SetData("CustomDataName", nonStandardData); to put your custom data on the clipboard and use var clipboardData = (float[][,])Clipboard.GetData("CustomDataName"); to get your custom data off the clipboard. You should check if the data is available on the clipboard using Clipboard.ContainsData("CustomDataName")
Note: By default the Windows clipboard only contains one entry at
a time be it an "image", "text" or "custom data". By putting your
custom data on the clipboard the previous data contained on the
clipboard will be gone.
The clipboard is best used for sharing more universal data between
unrelated programs, such as an image which can be pasted into a web browser or image editor. A grid of csv values which can be pasted into different data capturing and spreadsheet programs. float[][,] on the other hand has no real meaning other than to your software.
Long Answer (Here is an example):
// A Format/"Type" name for your clipboard data
static readonly string JOE_SOAP_DATA_V1 =
"JOE_SOAP_DATA_V1 {F1193E97-0B29-4ACD-935C-835A8EDFBB9D}";
// Some dummy data used to demonstrate
private readonly float[][,] JoeSoapData = new float[3][,]
{
new float[,]{{54,90},{34,56}},
new float[,]
{
{
// random values to create some variation
// will be different each time program is started
(float)new Random().NextDouble() * 2,
(float)new Random().NextDouble() * 6
},
{
(float)new Random().NextDouble() * 4,
(float)new Random().NextDouble() * 7
},
{
(float)new Random().NextDouble() * 4,
(float)new Random().NextDouble() * 7
}
},
new float[,]{{54,90},{34,56}}
};
private string JoeSoapDataToString(float[][,] data)
{
var sb = new StringBuilder();
foreach(var x in data)
{
foreach (var y in x)
sb.Append(y).Append(", ");
sb.Append('\n');
}
return sb.ToString();
}
private void BtnCopyToClipboard_Click(object sender, EventArgs e)
{
// On button click add the dummy data to the clipboard
Clipboard.SetData(JOE_SOAP_DATA_V1, JoeSoapData);
}
private void BtnPasteFromClipboard_Click(object sender, EventArgs e)
{
// Check if the clipboard contains our unique Format and notify the user if it does not
if (!Clipboard.ContainsData(JOE_SOAP_DATA_V1))
{
MessageBox.Show($"No {nameof(JOE_SOAP_DATA_V1)} clipboard data found :(");
return;
}
// extract the clipboard data and show the user
var clipboardData = (float[][,])Clipboard.GetData(JOE_SOAP_DATA_V1);
// show the copied data
LblFromClipboard.Text = "Data from clipboard\n" + JoeSoapDataToString(clipboardData);
}
private void Form1_Load(object sender, EventArgs e)
{
LblDataToCopy.Text = "Data that will be copied to the clipboard\n"
+ JoeSoapDataToString(JoeSoapData);
}
You can use AddClipboardFormatListener (for listening the clipboard update message) combined with Clipboard class to copy the data.
Receiver app:
public partial class ReceiverAppMainForm : Form
{
public ReceiverAppMainForm()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
ClipboardWrapper.AddClipboardFormatListener(Handle);
base.OnLoad(e);
}
protected override void OnClosing(CancelEventArgs e)
{
ClipboardWrapper.RemoveClipboardFormatListener(Handle);
base.OnClosing(e);
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == ClipboardWrapper.WM_CLIPBOARDUPDATE)
{
var data = Clipboard.GetDataObject();
if (data != null && data.GetDataPresent("clipboard-demoapp-data"))
{
var array = (float[][,])data.GetData("clipboard-demoapp-data");
// TODO: use the shared data
}
}
}
internal static class ClipboardWrapper
{
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool AddClipboardFormatListener(IntPtr hwnd);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool RemoveClipboardFormatListener(IntPtr hwnd);
internal const int WM_CLIPBOARDUPDATE = 0x031D;
}
}
Sender app:
public partial class SenderAppMainForm : Form
{
// ... ignored for brevity
private static void CopyAppDataToClipboard(float[][,] data)
{
// variant #1 (to avoid duplication)
var dataObject = new DataObject();
dataObject.SetData("clipboard-demoapp-data", data);
Clipboard.SetDataObject(dataObject, false, 10, 100);
// variant #2 (the event is sending twice on my computer running Win10)
//Clipboard.SetData("cliboard-demoapp-data", data);
}
}
I'm navigating through a webapp using WatiN and I needed to download a document. However, I've found out that WatiN doesn't support the download with Internet Explorer 11. That's why I'm trying to do it using the method described here :
How to enable automatic downloads in IE11 (the top answer)
Using User32.dll SendMessage To Send Keys With ALT Modifier
Basically I'm calling user32.dll to handle the small popup DL window (F6 to select it, tab, enter, etc.)
However nothing happens, the popup isn't responding to my commands.
Is this the right way to do it ?
My code looks like this :
//Before my method
[DllImport("user32.dll")]
public static extern IntPtr SetActiveWindow(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, uint wParam, uint lParam);
private void button1_Click(object sender, EventArgs e)
{
//some code....
IE myPopup = IE.AttachTo<IE>(Find.ByUrl("http://abcd.do"));
FileDownloadHandler fileDownloadHandler = new FileDownloadHandler("test");
myPopup.AddDialogHandler(fileDownloadHandler);
myPopup.Link(Find.ByUrl("httpabcdefg.do")).ClickNoWait();
ushort action = (ushort)260; //WM_SYSKEYDOWN
System.Threading.Thread.Sleep(2000);
ushort key = (ushort)System.Windows.Forms.Keys.F6;
ushort key2 = (ushort)System.Windows.Forms.Keys.Tab;
ushort key3 = (ushort)System.Windows.Forms.Keys.Enter;
ushort key4 = (ushort)System.Windows.Forms.Keys.Right;
SendMessage(myPopup.hWnd, action, key, 0);
SendMessage(myPopup.hWnd, action, key2, 0);
SendMessage(myPopup.hWnd, action, key3, 0);
SendMessage(myPopup.hWnd, action, key4, 0);
SendMessage(myPopup.hWnd, action, key4, 0);
SendMessage(myPopup.hWnd, action, key3, 0);
}
I know the popup (myPopup) is handled correctly, I can click on it using watin.
A couple of things are unclear to me, and I put the action WM_SYSKEYDOWN as default, to follow the example cited above.
Thanks in advance for any kind of help !
--------------EDIT-----------------
I managed to do it, I know it's not optimal at all but I dropped the Dllimport and the SendMessage all together and used SendKeys.Send instead. Just like that :
private void button1_Click(object sender, EventArgs e)
{
//some code....
IE myPopup = IE.AttachTo<IE>(Find.ByUrl("http://abcd.do"));
FileDownloadHandler fileDownloadHandler = new FileDownloadHandler("test");
myPopup.AddDialogHandler(fileDownloadHandler);
myPopup.Link(Find.ByUrl("httpabcdefg.do")).ClickNoWait();
SendKeys.Send("{F6}");
SendKeys.Send("{ENTER}");
SendKeys.Send("{RIGHT}");
SendKeys.Send("{RIGHT}");
SendKeys.Send("{ENTER}");
}
The code that you are using is not a proper way to automate and WatiN does not completely interact with Windows controls and help in windows is very little. I had the same problem in handling the windows controls as we had multiple versions of IE. IE 9.0 and higher have different file handling when compared to <= IE 8.0 version. The below code works fine for IE 9.0 and higher. Please make sure proper references are added (refer using's).
Refer below code and modify it as per you requirement.
using System.Threading;
using System.Windows.Automation;
using WatiN.Core;
using WatiN.Core.Native.Windows;
namespace TestFramework.Util
{
public static class WindowsHelper
{
#region Public Methods
/// <summary>
/// Download IE file.
/// </summary>
/// <param name="action">Action can be Save/Save As/Open/Cancel.</param>
/// <param name="path">Path where file needs to be saved (for Save As function).</param>
public static void DownloadIEFile(string action, string path = "", string regexPatternToMatch = "")
{
Browser browser = null;
if (Utility.Browser != null) // Utility.Browser is my WatiN browser instance.
{
if (string.IsNullOrEmpty(regexPatternToMatch))
{
browser = Utility.Browser;
}
else
{
Utility.Wait(() => (browser = Browser.AttachTo<IE>(Find.ByUrl(new System.Text.RegularExpressions.Regex(regexPatternToMatch)))) != null);
}
}
else
{
return;
}
// If doesn't work try to increase sleep interval or write your own waitUntill method
Thread.Sleep(3000);
// See information here (http://msdn.microsoft.com/en-us/library/windows/desktop/ms633515(v=vs.85).aspx)
Window windowMain = null;
Utility.Wait(() => (windowMain = new Window(NativeMethods.GetWindow(browser.hWnd, 5))).ProcessID != 0);
TreeWalker trw = new TreeWalker(Condition.TrueCondition);
AutomationElement mainWindow = trw.GetParent(AutomationElement.FromHandle(browser.hWnd));
Window windowDialog = null;
Utility.Wait(() => (windowDialog = new Window(NativeMethods.GetWindow(windowMain.Hwnd, 5))).ProcessID != 0);
windowDialog.SetActivate();
AutomationElementCollection amc = null;
Utility.Wait(() => (amc = AutomationElement.FromHandle(windowDialog.Hwnd).FindAll(TreeScope.Children, Condition.TrueCondition)).Count > 1);
foreach (AutomationElement element in amc)
{
// You can use "Save ", "Open", ''Cancel', or "Close" to find necessary button Or write your own enum
if (element.Current.Name.Equals(action))
{
// If doesn't work try to increase sleep interval or write your own waitUntil method
// WaitUntilButtonExsist(element,100);
Thread.Sleep(1000);
AutomationPattern[] pats = element.GetSupportedPatterns();
// Replace this for each if you need 'Save as' with code bellow
foreach (AutomationPattern pat in pats)
{
// '10000' button click event id
if (pat.Id == 10000)
{
InvokePattern click = (InvokePattern)element.GetCurrentPattern(pat);
click.Invoke();
}
}
}
else if (element.Current.Name.Equals("Save") && action == "Save As")
{
AutomationElementCollection bmc = element.FindAll(TreeScope.Children, Automation.ControlViewCondition);
InvokePattern click1 = (InvokePattern)bmc[0].GetCurrentPattern(AutomationPattern.LookupById(10000));
click1.Invoke();
Thread.Sleep(1000);
AutomationElementCollection main = mainWindow.FindAll(TreeScope.Children, Condition.TrueCondition);
foreach (AutomationElement el in main)
{
if (el.Current.LocalizedControlType == "menu")
{
// First array element 'Save', second array element 'Save as', third second array element 'Save and open'
InvokePattern clickMenu = (InvokePattern)
el.FindAll(TreeScope.Children, Condition.TrueCondition)[1].GetCurrentPattern(AutomationPattern.LookupById(10000));
clickMenu.Invoke();
Thread.Sleep(1000);
ControlSaveDialog(mainWindow, path);
break;
}
}
}
}
}
/// <summary>
/// Control for save dialog.
/// </summary>
/// <param name="mainWindow">Main window.</param>
/// <param name="path">Path.</param>
private static void ControlSaveDialog(AutomationElement mainWindow, string path)
{
// Obtain the save as dialog
var saveAsDialog = mainWindow
.FindFirst(TreeScope.Descendants,
new PropertyCondition(AutomationElement.NameProperty, "Save As"));
// Get the file name box
var saveAsText = saveAsDialog
.FindFirst(TreeScope.Descendants,
new AndCondition(
new PropertyCondition(AutomationElement.NameProperty, "File name:"),
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit)))
.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
// Fill the filename box
saveAsText.SetValue(path);
Thread.Sleep(500);
Utility.PressKey("LEFT");
Utility.PressKey("LEFT");
Thread.Sleep(1000);
// Find the save button
var saveButton =
saveAsDialog.FindFirst(TreeScope.Descendants,
new AndCondition(
new PropertyCondition(AutomationElement.NameProperty, "Save"),
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button)));
// Invoke the button
var pattern = saveButton.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;
pattern.Invoke();
}
#endregion
}
}
public static class Utility
{
public static IE Browser { get; set; }
// Wait specified number of seconds
public static void Wait(int seconds)
{
System.Threading.Thread.Sleep(seconds * 1000);
}
// Wait for condition to evaluate true, timeout after 30 seconds
public static void Wait(Func<bool> condition)
{
int count = 0;
while (!condition() && count < 30)
{
System.Threading.Thread.Sleep(1000);
count++;
}
}
//Send tab key press to browser
public static void PressTab()
{
System.Windows.Forms.SendKeys.SendWait("{TAB}");
System.Threading.Thread.Sleep(300);
}
//Send specified key press to browser
public static void PressKey(string keyname)
{
System.Windows.Forms.SendKeys.SendWait("{" + keyname.ToUpper() + "}");
System.Threading.Thread.Sleep(300);
}
}
I have an annoying problem...
I just wanted to show a dialog, but it's always showing twice... Here's the code:
private void tmr_sysdt_Tick(object sender, EventArgs e)
{
lbl_time.Text = System.DateTime.Now.ToLongTimeString();
lbl_date.Text = System.DateTime.Now.ToLongDateString();
if (GetLastInputTime() > Program.timeout)
{
frm_lockscreen login= new frm_lockscreen();
tmr_sysdt.Enabled = false;
if (login.ShowDialog(this) == DialogResult.OK) tmr_sysdt.Enabled = true;
}
}
The tmr_sysdt.Interval is 1000.
The problem is simple - but unsolvable for me - the dialog is showing in duplicate (the second is coming up AT THE SAME TIME as the first).
I have no idea, so any idea will be appreciated :)
Thank you, and if you need more details please comment!
Ps.: Sorry for bad eng
EDIT: GetLastInputTime()
[DllImport("user32.dll")]
static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
static uint GetLastInputTime()
{
uint idleTime = 0;
LASTINPUTINFO lastInputInfo = new LASTINPUTINFO();
lastInputInfo.cbSize = (uint)Marshal.SizeOf(lastInputInfo);
lastInputInfo.dwTime = 0;
uint envTicks = (uint)Environment.TickCount;
if (GetLastInputInfo(ref lastInputInfo))
{
uint lastInputTick = lastInputInfo.dwTime;
idleTime = envTicks - lastInputTick;
}
return ((idleTime > 0) ? (idleTime / 1000) : 0);
}
[StructLayout(LayoutKind.Sequential)]
struct LASTINPUTINFO
{
public static readonly int SizeOf = Marshal.SizeOf(typeof(LASTINPUTINFO));
[MarshalAs(UnmanagedType.U4)]
public UInt32 cbSize;
[MarshalAs(UnmanagedType.U4)]
public UInt32 dwTime;
}
Could you try this code? Just to make sure the logic in your handler isn't called twice at the same time:
static bool busy = false;
private void tmr_sysdt_Tick(object sender, EventArgs e)
{
if (busy)
{
return;
}
busy = true;
try
{
lbl_time.Text = System.DateTime.Now.ToLongTimeString();
lbl_date.Text = System.DateTime.Now.ToLongDateString();
if (GetLastInputTime() > Program.timeout)
{
frm_lockscreen login = new frm_lockscreen();
tmr_sysdt.Enabled = false;
if (login.ShowDialog(this) == DialogResult.OK) tmr_sysdt.Enabled = true;
}
}
finally
{
busy = false;
}
}
The problem you were having was that the code to check the last activity of the user and show the login box, was in a form that is created multiple times.
So in fact, each of these 'main forms' were checking the user activity and showing the login box.
By putting the busy bool as static, each instance of the 'main form' read this bool as the same value. Therefore, the check+show login only gets executed one time.
I would suggest moving this code to a form you only create once, and keep open during the full lifetime of the application. (If you have such a form)
You want to reuse your frm_lockscreen and prevent reentry to your Tick event
private frm_lockscreen _lockScreen;
private frm_lockscreen LockScreen
{
get { return _lockScreen ?? (_lockScreen = new frm_lockscreen()); }
}
private void tmr_sysdt_Tick(object sender, EventArgs e)
{
// prevent reentry
if (!Monitor.TryEnter(tmr_sysdt)) return;
try {
lbl_time.Text = System.DateTime.Now.ToLongTimeString();
lbl_date.Text = System.DateTime.Now.ToLongDateString();
if (GetLastInputTime() > Program.timeout)
{
tmr_sysdt.Enabled = false;
if (LockScreen.ShowDialog(this) == DialogResult.OK) tmr_sysdt.Enabled = true;
}
}
finally {
Monitor.Exit(tmr_sysdt);
}
}
The reason is that your outer if condition is returning true twice:
if (GetLastInputTime() > Program.timeout)
To find the culprit you have to check the values returned by GetLastInputTime() and Program.Timeout
I have an interop question about embedding WinForms controls into a WPF application and differences in the .Net framework versions.
The following should show a transparent wpf control (red box) over a WebBrowser control and works as expected when using .Net 3.5:
... but after compilation with .Net 4.5 the following occurs.
Everything works again when switching back to .Net 3.5
The following Code works for WPF with Win32 using .Net 3.0 up to .Net 3.5 but not with .Net 4.0/4.5:
Win32HostRenderer.xaml:
<UserControl x:Class="WPFInterop.Interop.Win32HostRenderer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Image Name="_interopRenderer" Stretch="None"/>
</Grid>
</UserControl>
Win32HostRenderer.xaml.cs:
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using Winforms = System.Windows.Forms;
using GDI = System.Drawing;
using System.Runtime;
using System.Runtime.InteropServices;
namespace WPFInterop.Interop
{
public partial class Win32HostRenderer : System.Windows.Controls.UserControl
{
[DllImport("user32.dll")]
private static extern bool PrintWindow(IntPtr hwnd, IntPtr hdcBlt, uint nFlags);
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
[DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory")]
private static extern void CopyMemory(IntPtr Destination, IntPtr Source, int Length);
private DispatcherTimer _rendererTimer;
private int _rendererInterval = 33;
private InteropForm _interopForm;
private Winforms.Control _winformControl;
private BitmapSource _bitmapSource;
private BitmapBuffer _bitmapSourceBuffer;
private GDI.Bitmap _gdiBitmap;
private GDI.Graphics _gdiBitmapGraphics;
private IntPtr _hGDIBitmap;
public Win32HostRenderer()
{
InitializeComponent();
}
private void InitializeInteropForm()
{
if (_winformControl == null) return;
if (_interopForm != null)
{
TearDownInteropForm();
}
_interopForm = new InteropForm();
_interopForm.Opacity = 0.01;
_interopForm.Controls.Add(_winformControl);
_interopForm.Width = _winformControl.Width;
_interopForm.Height = _winformControl.Height;
}
private void TearDownInteropForm()
{
if (_interopForm == null) return;
_interopForm.Hide();
_interopForm.Close();
_interopForm.Dispose();
_interopForm = null;
}
private void InitializeRendererTimer()
{
TearDownRenderTimer();
_rendererTimer = new DispatcherTimer();
_rendererTimer.Interval = new TimeSpan(0, 0, 0, 0, _rendererInterval);
_rendererTimer.Tick += new EventHandler(_rendererTimer_Tick);
_rendererTimer.Start();
}
void _rendererTimer_Tick(object sender, EventArgs e)
{
RenderWinformControl();
}
private void TearDownRenderTimer()
{
if (_rendererTimer == null) return;
_rendererTimer.IsEnabled = false;
}
private void RegisterEventHandlers()
{
Window currentWindow = Window.GetWindow(this);
currentWindow.LocationChanged += new EventHandler(delegate(object sender, EventArgs e)
{
PositionInteropFormOverRender();
});
currentWindow.SizeChanged += new SizeChangedEventHandler(delegate(object sender, SizeChangedEventArgs e)
{
PositionInteropFormOverRender();
});
currentWindow.Deactivated += new EventHandler(delegate(object sender, EventArgs e)
{
//_interopForm.Opacity = 0;
});
currentWindow.Activated += new EventHandler(delegate(object sender, EventArgs e)
{
// _interopForm.Opacity = 0.01;
});
currentWindow.StateChanged += new EventHandler(delegate(object sender, EventArgs e)
{
PositionInteropFormOverRender();
});
_interopRenderer.SizeChanged += new SizeChangedEventHandler(delegate(object sender, SizeChangedEventArgs e)
{
PositionInteropFormOverRender();
});
}
private void PositionInteropFormOverRender()
{
if (_interopForm == null) return;
Window currentWindow = Window.GetWindow(this);
Point interopRenderScreenPoint = _interopRenderer.PointToScreen(new Point());
_interopForm.Left = (int)interopRenderScreenPoint.X;
_interopForm.Top = (int)interopRenderScreenPoint.Y;
int width = 0;
if ((int)_interopRenderer.ActualWidth > (int)currentWindow.Width)
{
width = (int)currentWindow.Width;
}
else
{
width = (int)_interopRenderer.ActualWidth;
}
if ((int)currentWindow.Width < width)
width = (int)currentWindow.Width;
_interopForm.Width = width;
}
private void InitializeBitmap()
{
if (_bitmapSource == null)
{
TearDownBitmap();
}
int interopRenderWidth = _winformControl.Width;
int interopRenderHeight = _winformControl.Height;
int bytesPerPixel = 4;
int totalPixels = interopRenderWidth * interopRenderHeight * bytesPerPixel;
byte[] dummyPixels = new byte[totalPixels];
_bitmapSource = BitmapSource.Create(interopRenderWidth,
interopRenderHeight,
96,
96,
PixelFormats.Bgr32,
null,
dummyPixels,
interopRenderWidth * bytesPerPixel);
_interopRenderer.Source = _bitmapSource;
_bitmapSourceBuffer = new BitmapBuffer(_bitmapSource);
_gdiBitmap = new GDI.Bitmap(_winformControl.Width,
_winformControl.Height,
GDI.Imaging.PixelFormat.Format32bppRgb);
_hGDIBitmap = _gdiBitmap.GetHbitmap();
_gdiBitmapGraphics = GDI.Graphics.FromImage(_gdiBitmap);
}
private void TearDownBitmap()
{
_bitmapSource = null;
_bitmapSourceBuffer = null;
if (_gdiBitmap != null)
{
_gdiBitmap.Dispose();
}
if (_gdiBitmapGraphics != null)
{
_gdiBitmapGraphics.Dispose();
}
if (_hGDIBitmap != IntPtr.Zero)
{
DeleteObject(_hGDIBitmap);
}
}
private void InitializeWinformControl()
{
InitializeInteropForm();
InitializeBitmap();
RegisterEventHandlers();
PositionInteropFormOverRender();
_interopForm.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
_interopForm.Show();
InitializeRendererTimer();
}
public Winforms.Control Child
{
get { return _winformControl; }
set
{
_winformControl = value;
InitializeWinformControl();
}
}
private void RenderWinformControl()
{
PaintWinformControl(_gdiBitmapGraphics, _winformControl);
GDI.Rectangle lockRectangle = new GDI.Rectangle(0,
0,
_gdiBitmap.Width,
_gdiBitmap.Height);
GDI.Imaging.BitmapData bmpData = _gdiBitmap.LockBits(lockRectangle,
GDI.Imaging.ImageLockMode.ReadOnly,
GDI.Imaging.PixelFormat.Format32bppRgb);
System.IntPtr bmpScan0 = bmpData.Scan0;
CopyMemory(_bitmapSourceBuffer.BufferPointer,
bmpScan0,
(int)_bitmapSourceBuffer.BufferSize);
_gdiBitmap.UnlockBits(bmpData);
_interopRenderer.InvalidateVisual();
}
private void PaintWinformControl(GDI.Graphics graphics, Winforms.Control control)
{
IntPtr hWnd = control.Handle;
IntPtr hDC = graphics.GetHdc();
PrintWindow(hWnd, hDC, 0);
graphics.ReleaseHdc(hDC);
}
}
}
BitmapBuffer.cs:
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime;
using System.Runtime.InteropServices.ComTypes;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace WPFInterop
{
class BitmapBuffer
{
private BitmapSource _bitmapImage = null;
private object _wicImageHandle = null;
private object _wicImageLock = null;
private uint _bufferSize = 0;
private IntPtr _bufferPointer = IntPtr.Zero;
private uint _stride = 0;
private int _width;
private int _height;
public BitmapBuffer(BitmapSource Image)
{
//Keep reference to our bitmap image
_bitmapImage = Image;
//Get around the STA deal
_bitmapImage.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new System.Windows.Threading.DispatcherOperationCallback(delegate
{
//Cache our width and height
_width = _bitmapImage.PixelWidth;
_height = _bitmapImage.PixelHeight;
return null;
}), null);
//Retrieve and store our WIC handle to the bitmap
SetWICHandle();
//Set the buffer pointer
SetBufferInfo();
}
/// <summary>
/// The pointer to the BitmapImage's native buffer
/// </summary>
public IntPtr BufferPointer
{
get
{
//Set the buffer pointer
SetBufferInfo();
return _bufferPointer;
}
}
/// <summary>
/// The size of BitmapImage's native buffer
/// </summary>
public uint BufferSize
{
get { return _bufferSize; }
}
/// <summary>
/// The stride of BitmapImage's native buffer
/// </summary>
public uint Stride
{
get { return _stride; }
}
private void SetBufferInfo()
{
int hr = 0;
//Get the internal nested class that holds some of the native functions for WIC
Type wicBitmapNativeMethodsClass = Type.GetType("MS.Win32.PresentationCore.UnsafeNativeMethods+WICBitmap, PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
//Get the methods of all the static methods in the class
MethodInfo[] info = wicBitmapNativeMethodsClass.GetMethods(BindingFlags.Static | BindingFlags.NonPublic);
//This method looks good
MethodInfo lockmethod = info[0];
//The rectangle of the buffer we are
//going to request
Int32Rect rect = new Int32Rect();
rect.Width = _width;
rect.Height = _height;
//Populate the arguments to pass to the function
object[] args = new object[] { _wicImageHandle, rect, 2, _wicImageHandle };
//Execute our static Lock() method
hr = (int)lockmethod.Invoke(null, args);
//argument[3] is our "out" pointer to the lock handle
//it is set by our last method invoke call
_wicImageLock = args[3];
//Get the internal nested class that holds some of the
//other native functions for WIC
Type wicLockMethodsClass = Type.GetType("MS.Win32.PresentationCore.UnsafeNativeMethods+WICBitmapLock, PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
//Get all the native methods into our array
MethodInfo[] lockMethods = wicLockMethodsClass.GetMethods(BindingFlags.Static | BindingFlags.NonPublic);
//Our method to get the stride value of the image
MethodInfo getStrideMethod = lockMethods[0];
//Fill in our arguments
args = new object[] { _wicImageLock, _stride };
//Execute the stride method
getStrideMethod.Invoke(null, args);
//Grab out or byref value for the stride
_stride = (uint)args[1];
//This one looks perty...
//This function will return to us
//the buffer pointer and size
MethodInfo getBufferMethod = lockMethods[1];
//Fill in our arguments
args = new object[] { _wicImageLock, _bufferSize, _bufferPointer };
//Run our method
hr = (int)getBufferMethod.Invoke(null, args);
_bufferSize = (uint)args[1];
_bufferPointer = (IntPtr)args[2];
DisposeLockHandle();
}
private void DisposeLockHandle()
{
MethodInfo close = _wicImageLock.GetType().GetMethod("Close");
MethodInfo dispose = _wicImageLock.GetType().GetMethod("Dispose");
close.Invoke(_wicImageLock, null);
dispose.Invoke(_wicImageLock, null);
}
private void SetWICHandle()
{
//Get the type of bitmap image
Type bmpType = typeof(BitmapSource);
//Use reflection to get the private property WicSourceHandle
FieldInfo fInfo = bmpType.GetField("_wicSource",
BindingFlags.NonPublic | BindingFlags.Instance);
//Retrieve the WIC handle from our BitmapImage instance
_wicImageHandle = fInfo.GetValue(_bitmapImage);
}
}
}
The InteropForm is just a derived System.Windows.Forms.Form and has no special magic.
Integration into WPF-Page is simple:
<interop:Win32HostRenderer x:Name="_host" Grid.Row="0">
</interop:Win32HostRenderer>
And after window Loaded event:
System.Windows.Forms.WebBrowser browser = new System.Windows.Forms.WebBrowser();
browser.Navigate("http://www.youtube.com");
browser.Width = 700;
browser.Height = 500;
_host.Child = browser;
(code parts from Jeremiah Morrill, see this blog for more info)
Ok, your first problem is with this bit of code:
//Get the methods of all the static methods in the class
MethodInfo[] info = wicBitmapNativeMethodsClass.GetMethods(BindingFlags.Static | BindingFlags.NonPublic);
//This method looks good
MethodInfo lockmethod = info[0];
In .Net 4, the "Lock" method is at index 1 (probably because a new "static" method has been added)...so your reflection code is using the wrong one.
Instead you can just use this to get it by name (suggest altering your other GetMethods to GetMethod):
MethodInfo lockmethod = wicBitmapNativeMethodsClass.GetMethod("Lock", BindingFlags.Static | BindingFlags.NonPublic);
Here it is working (in .NET 4)...I made it fill the client space...as I didn't have your full source code...alter as necessary to do your visual reflection, etc:
The changes I made were:
MethodInfo lockmethod = wicBitmapNativeMethodsClass.GetMethod("Lock", BindingFlags.Static | BindingFlags.NonPublic);
private void PositionInteropFormOverRender()
{
if (_interopForm == null) return;
Window currentWindow = Window.GetWindow(this);
FrameworkElement firstchild = this.Content as FrameworkElement;
if (firstchild != null)
{
Point interopRenderScreenPoint = currentWindow.PointToScreen(new Point());
_interopForm.Left = (int)interopRenderScreenPoint.X;
_interopForm.Top = (int)interopRenderScreenPoint.Y;
_interopForm.Width = (int)firstchild.RenderSize.Width;
_interopForm.Height = (int)firstchild.RenderSize.Height;
}
}
private void _host_Loaded(object sender, RoutedEventArgs e)
{
System.Windows.Forms.WebBrowser browser = new System.Windows.Forms.WebBrowser();
browser.Navigate("http://www.youtube.com");
browser.Width = 700;
browser.Height = 500;
browser.Dock = System.Windows.Forms.DockStyle.Fill;
_host.Child = browser;
}
private void InitializeInteropForm()
{
if (_winformControl == null) return;
if (_interopForm != null)
{
TearDownInteropForm();
}
_interopForm = new InteropForm();
_interopForm.Opacity = 0.5;
_interopForm.Controls.Add(_winformControl);
_interopForm.Width = _winformControl.Width;
_interopForm.Height = _winformControl.Height;
}
There are other things you need to fix/do/be aware of i.e.:
when the screen DPI is not 96dpi, you will have to convert the RenderSize.Width/Height values accordingly (see: How do I convert a WPF size to physical pixels?)
your "browser" window is hidden in the z-order when you drag the main Window...you just need to bring it back to the front.
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;
}