Why does my application just stop and does not keep running? - c#

I am trying to make a program (C#) which do this:
If I click my left mouse button the mouse should move to the left by a DeltaX.
The problem is that when I run the program, it simple opens the console application and nothing happens. I am not sure what I am doing wrong here.
It should keep running and checking if I click the left mouse button or not, and when I click it, the cursor should move to the left by a DeltaX.
code
using System.Data;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
namespace Teste
{
class Program
{
static void Main(string[] args)
{
var someClass = new Up();
someClass.Update();
}
}
public class Up
{
[DllImport("user32.dll")] static extern short GetAsyncKeyState(int vKey);
[DllImport("USER32.dll")] static extern short GetKeyState(int nVirtKey);
int msShootTime = 225;
System.DateTime lastClick = System.DateTime.Now;
bool isRunning = true;
public async void Update()
{
while (true)
{
if (isRunning)
{
await Task.Delay(10);
continue;
}
int res = GetKeyState((int)1);
if (res >= 0)
{
await Task.Delay(1);
continue;
}
Move(-10, 0, true);
}
}
private const int MOUSEEVENTF_LEFTDOWN = 0x02;
private const int MOUSEEVENTF_LEFTUP = 0x04;
private const int MOUSEEVENTF_RIGHTDOWN = 0x08;
private const int MOUSEEVENTF_RIGHTUP = 0x10;
[DllImport("user32.dll")]
static extern void mouse_event(int dwFlags, int dx, int dy, uint dwData, UIntPtr dwExtraInfo);
public void Move(int xDelta, int yDelta, bool pressDown = false)
{
if (pressDown)
{
if (System.DateTime.Now.Subtract(lastClick).TotalMilliseconds < msShootTime)
{
pressDown = false;
}
else
{
lastClick = System.DateTime.Now;
}
}
mouse_event(pressDown ? (MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP) : 0x0001, xDelta, yDelta, 0, UIntPtr.Zero);
}
}
}

Some mistakes were in code, I've added some comments directly to code
class Program
{
static async Task Main(string[] args) // made Main async
{
var someClass = new Up();
await someClass.Update(); // awaiting async method, you didn't wait it and app have to exit immediately
}
public class Up
{
[DllImport("user32.dll")]
private static extern short GetAsyncKeyState(int vKey);
[DllImport("user32.dll")]
private static extern short GetKeyState(int nVirtKey);
// just copied mouse_event and Flags from one of my projects but yours worked too
[DllImport("user32.dll")]
private static extern void mouse_event(MouseFlags dwFlags, int dx, int dy, uint dwData, UIntPtr dwExtraInfo);
[Flags]
private enum MouseFlags : uint
{
MOUSEEVENTF_ABSOLUTE = 0x8000, // If set, dx and dy contain normalized absolute coordinates between 0 and 65535. The event procedure maps these coordinates onto the display surface. Coordinate (0,0) maps onto the upper-left corner of the display surface, (65535,65535) maps onto the lower-right corner.
MOUSEEVENTF_LEFTDOWN = 0x0002, // The left button is down.
MOUSEEVENTF_LEFTUP = 0x0004, // The left button is up.
MOUSEEVENTF_MIDDLEDOWN = 0x0020, // The middle button is down.
MOUSEEVENTF_MIDDLEUP = 0x0040, // The middle button is up.
MOUSEEVENTF_MOVE = 0x0001, // Movement occurred.
MOUSEEVENTF_RIGHTDOWN = 0x0008, // The right button is down.
MOUSEEVENTF_RIGHTUP = 0x0010, // The right button is up.
MOUSEEVENTF_WHEEL = 0x0800, // The wheel has been moved, if the mouse has a wheel.The amount of movement is specified in dwData
MOUSEEVENTF_XDOWN = 0x0080, // An X button was pressed.
MOUSEEVENTF_XUP = 0x0100, // An X button was released.
MOUSEEVENTF_HWHEEL = 0x01000 // The wheel button is tilted.
}
int msShootTime = 225;
DateTime lastClick = DateTime.Now;
bool isRunning = false; // it was initially true
public async Task Update() // async Task
{
while (true)
{
if (isRunning) // it was always true
{
await Task.Delay(10);
continue; // this was always executed
}
isRunning = true; // added this
int res = GetKeyState((int)1);
if (res >= 0)
{
await Task.Delay(1);
isRunning = false; // added this
continue;
}
Move(-10, 0, true);
isRunning = false; // added this
}
}
public void Move(int xDelta, int yDelta, bool pressDown = false)
{
if (pressDown)
{
if (System.DateTime.Now.Subtract(lastClick).TotalMilliseconds < msShootTime)
{
pressDown = false;
}
else
{
lastClick = System.DateTime.Now;
}
}
// updated for new Flags enum
// I'm not sure if sending both MOUSEEVENTF_LEFTDOWN and MOUSEEVENTF_LEFTUP will give any effect
// try to send it sequentially with some delay: DOWN, then UP
mouse_event(pressDown ? (MouseFlags.MOUSEEVENTF_LEFTDOWN | MouseFlags.MOUSEEVENTF_LEFTUP) : MouseFlags.MOUSEEVENTF_MOVE, xDelta, yDelta, 0, UIntPtr.Zero);
}
}
}
Note: Microsoft recommends using SendInput instead of mouse_event.
This function has been superseded. Use SendInput instead.

Related

Progress Bar Value > Integer doesn't work in if statement?

I have an application I am testing out where audio is played and an action is performed when the audio is played. I have the audio captured in a progress bar which works flawlessly however after I start the program everything will work but the if statement below. Is there something I am missing? I am using NAudio to capture the audio etc as well.
All audio is going to the progress bar when selecting any audio source and will detect at the right level. When I click on a button in the Windows Form Application it right clicks fine, but when I have audio coming through past a certain level it doesn't right click again which is what I am trying to accomplish.
Here is the main code which gets the audio and the mouse buttons:
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, uint dwExtraInfo);
private const int MOUSEEVENTF_RIGHTDOWN = 0x08;
private const int MOUSEEVENTF_RIGHTUP = 0x10;
public void DoMouseClick()
{
uint X = (uint)Cursor.Position.X;
uint Y = (uint)Cursor.Position.Y;
mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, X, Y, 0, 0);
}
private WaveIn recorder;
public Form1()
{
InitializeComponent();
recorder = new WaveIn();
recorder.StartRecording();
MMDeviceEnumerator enumerator = new MMDeviceEnumerator();
var devices = enumerator.EnumerateAudioEndPoints(DataFlow.All, DeviceState.Active);
audioSourcesList.Items.AddRange(devices.ToArray());
}
private void timer1_Tick(object sender, EventArgs e)
{
if (audioSourcesList.SelectedItem != null)
{
var device = (MMDevice)audioSourcesList.SelectedItem;
device.AudioEndpointVolume.Mute = false;
Label.Text = (Math.Round(device.AudioMeterInformation.MasterPeakValue * 100)).ToString();
progressBar.Value = (int)(device.AudioMeterInformation.MasterPeakValue * 100);
}
}
Here is the block that starts the main program and where it seems to not work:
private void test()
{
Thread.Sleep(2500);
DoMouseClick();
if (progressBar.Value >= 5)
{
DoMouseClick();
Thread.Sleep(2000);
DoMouseClick();
}
}
I did not work with NAudio but set up the threads you need.
If the program stops when it reaches the if condition, it is because the control is for another thread and you are using it for another thread. With the BeginInvoke command, that allowed two threads to run asynchronously so that both threads had access to the required controls.
If you want more information, refer to this link Calling Synchronous Methods Asynchronously
change Form1 constructor to this
public Form1()
{
Thread thread = new Thread(delegate ()
{
recorder = new WaveIn();
recorder.StartRecording();
});
System.Timers.Timer t = new System.Timers.Timer();
t.Interval = 500;
t.Elapsed += T_Elapsed;
t.Start();
MMDeviceEnumerator enumerator = new MMDeviceEnumerator();
var devices = enumerator.EnumerateAudioEndPoints(DataFlow.All, DeviceState.Active);
audioSourcesList.Items.AddRange(devices.ToArray());
}
Delete the timer1_Tick method and add the following lines to the program
public delegate void InvokeDelegate();
private void T_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
this.BeginInvoke(new InvokeDelegate(InvokeMethod));
}
void InvokeMethod()
{
if (audioSourcesList.SelectedItem != null)
{
var device = (MMDevice)audioSourcesList.SelectedItem;
device.AudioEndpointVolume.Mute = false;
Label.Text = (Math.Round(device.AudioMeterInformation.MasterPeakValue * 100)).ToString();
progressBar.Value = (int)(device.AudioMeterInformation.MasterPeakValue * 100);
}
}

How to cancel left mouse down without click on C#?

I'm try to wrote app, that will send right click, when user take long press left mouse button.
I found https://github.com/gmamaladze/globalmousekeyhook project and hook events with it.
When I hook left up, then send right click with mouse event, first fire left click, than right click fire.
Is any method to cancel first left mouse down?
using Gma.System.MouseKeyHook;
using System;
using System.Windows.Forms;
namespace MouseRClick
{
class ClassRightClick
{
// API
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
public const int MOUSEEVENTF_LEFTDOWN = 0x02;
public const int MOUSEEVENTF_LEFTUP = 0x04;
public const int MOUSEEVENTF_RIGHTDOWN = 0x08;
public const int MOUSEEVENTF_RIGHTUP = 0x10;
// Hook module
private IKeyboardMouseEvents _hook;
private bool _rclick_activated = false;
private int _down_cursor_x;
private int _down_cursor_y;
private Timer timer;
public ClassRightClick(int delay)
{
timer = new Timer();
timer.Interval = delay;
timer.Tick += timer_Tick;
}
public void Subscribe()
{
_hook = Hook.GlobalEvents();
_hook.MouseDownExt += onMouseDown;
_hook.MouseUpExt += onMouseUp;
}
public void Unsubscribe()
{
_hook.MouseDownExt -= onMouseDown;
_hook.MouseUpExt -= onMouseUp;
//It is recommened to dispose it
_hook.Dispose();
}
private void onMouseDown(object sender, MouseEventExtArgs e)
{
if (e.Button == MouseButtons.Left)
{
_down_cursor_x = e.Location.X;
_down_cursor_y = e.Location.Y;
_rclick_activated = false;
timer.Enabled = true;
}
}
private void onMouseUp(object sender, MouseEventExtArgs e)
{
if (e.Button == MouseButtons.Left)
{
timer.Enabled = false;
Unsubscribe();
if (_rclick_activated)
{
mouse_event(MOUSEEVENTF_RIGHTDOWN, _down_cursor_x, _down_cursor_y, 0, 0);
mouse_event(MOUSEEVENTF_RIGHTUP, _down_cursor_x, _down_cursor_y, 0, 0);
e.Handled = true;
}
_rclick_activated = false;
Subscribe();
}
}
private void timer_Tick(object sender, EventArgs e)
{
_rclick_activated = true;
}
}
}
Actually, you cannot do this. Problem is, left mouse click is already fired by the time you dispatch the right-mouse click. You can't just go back in time.
I'm try to wrote app, that will send right click, when user take long
press left mouse button.
You can start record the time when receive left mouse button down message by starting a timer. If the timer timeout a WM_TIMER message will be sent out and you can set the _rclick_activated to true to indicate the left button pressed long enough. When handle the left mouse button up message check the _rclick_activated, if it is true send right mouse button down event. After receive right mouse button down message send right mouse button up event.
The following code is Windows desktop API C++ implement as a simple example. You can use as a reference.
// Mouse hook
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode < 0) // do not process the message
return CallNextHookEx(NULL, nCode,
wParam, lParam);
if (WM_LBUTTONDOWN == wParam)
{
OutputDebugString(L"\n Left button down \n");
_rclick_activated = false;
SetTimer(m_windowHandle, // handle to main window
IDT_TIMER1, // timer identifier
2000, // 2-second interval
(TIMERPROC)NULL); // no timer callback
}
else if (WM_LBUTTONUP == wParam)
{
OutputDebugString(L"\n Left button up \n");
if (_rclick_activated)
{
MOUSEINPUT mouseData = {};
mouseData.dx = GET_X_LPARAM(lParam);
mouseData.dy = GET_Y_LPARAM(lParam);
mouseData.dwFlags = MOUSEEVENTF_RIGHTDOWN;
INPUT inputData = {};
inputData.type = INPUT_MOUSE;
inputData.mi = mouseData;
UINT result = SendInput(1, &inputData, sizeof(INPUT));
if (result == 1)
{
OutputDebugString(L"\n successfully insert right button down \n");
}
}
}
else if (WM_RBUTTONDOWN == wParam)
{
OutputDebugString(L"\n Right button down \n");
if (_rclick_activated)
{
MOUSEINPUT mouseData = {};
mouseData.dx = GET_X_LPARAM(lParam);
mouseData.dy = GET_Y_LPARAM(lParam);
mouseData.dwFlags = MOUSEEVENTF_RIGHTUP;
INPUT inputData = {};
inputData.type = INPUT_MOUSE;
inputData.mi = mouseData;
UINT result = SendInput(1, &inputData, sizeof(INPUT));
if (result == 1)
{
OutputDebugString(L"\n successfully insert right button up \n");
}
_rclick_activated = false;
}
}
else if (WM_RBUTTONUP == wParam)
{
OutputDebugString(L"\n Right button up \n");
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
//...
// Rigister mouse hook
HHOOK m_msgHook = SetWindowsHookEx(WH_MOUSE, MouseProc, NULL, GetCurrentThreadId());
//...
//...
case WM_TIMER:
// process the 2-second timer
_rclick_activated = true;
KillTimer(hWnd, IDT_TIMER1);
return 0;
//...

C# - Wait for process to exit and close 'ShowDialog()'

Being new to .net, i am not able to get how to close the show dialog modal window once its open. As i have learnt we cannot close that automatically until explicitly its to be called. Here is my code:
//process - notepad.exe
Process p = Process.Start(process);
frm_Save fsave = new frm_Save();
Using (p)
{
do
{
if(!p.HasExited)
{
p.Refresh();
fsave.ShowDialog(); // it just stuck here and doesn't go to next line
}
}
while(!p.WaitForExit(1000));
}
//frm_Save.cs
public frm_Save()
{
InitializeComponent();
}
private void frm_Save_Load(...,....)
{
//
}
private void frm_Save_Shown(...,...)
{
Sleep(100);
Forms.Application.DoEvents();
Close();
}
As you have explained, you want to show a dialog with icon that you are saving a video in the background and prevent the user to do something. One regular way to do that is with a BackgroundWorker in your Dialog. Here is the code how it would work:
public class frm_Save : Form
{
public FrmProgress(List<TransferOptions> transferOptions)
{
InitializeComponent();
BackgroundWorker BgrdWorker = new System.ComponentModel.BackgroundWorker();
this.BgrdWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(this.BgrdWorker_DoWork);
this.BgrdWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.BgrdWorker_RunWorkerCompleted);
}
private void FrmProgress_Load(object sender, EventArgs e)
{
// Show image and message...
}
private void BgrdWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Call your video Process start Function
// after that
var stopWatch = new StopWatch();
stopWatch.Start()
while (true)
{
if (stopWatch.ElapsedMilliseconds >1000 || videoProcessHasReturnedSuccessfully)
{
break
}
}
}
private void BgrdWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// inform the user the video processing is finished
this.Close();
}
}
Then in the main form of your console app when you want to start the whole process, you call:
frm_Save fsave = new frm_Save();
fsave.ShowDialog()
Tip: You can also use BgrdWorker.ProgressChanged to show the progress of background task to the user by communicating between the background task and the UI if necessary, but you have not requested that in your question.
This approach may work for you, note the use of TopMost.
using System.Runtime.InteropServices;
private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
private const UInt32 SWP_NOSIZE = 0x0001;
private const UInt32 SWP_NOMOVE = 0x0002;
private const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE;
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter,
int X, int Y, int cx, int cy, uint uFlags);
....
frm_Save fsave = new frm_Save();
fsave.Show();
SetWindowPos(frm_Save.Handle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS);
Process p = Process.Start(process);
using (p)
{
while (!p.WaitForExit(1000))
{
fsave.Refresh();
}
}
fsave.Close();

Click when mouse is in an area for some time

I am trying to do something like Kinect adventures with Kinect SDK i.e. when the mouse stays in a certain area for a specific period of time, the native click event is to be fired.
The problem is that I do not get the expected results, since I get random clicking most times. I tried to check with breakpoints etc.
Most often, when my hand is not visible, the cursor goes to the corner of the screen and starts clicking. This is most probably because Math.Abs(lastX - cursorX) < threshold sets to true.
I have tried changing the threshold values to 200, but it fires a click st the start, and afterwards I am not get expected left clicks, when I hold the cursor in a certain position for some time. Any suggestions would be greatly appreciated. Here's the code:
//SkeletonFrameReadyevent
foreach (SkeletonData sd in e.SkeletonFrame.Skeletons)
{
if (sd.TrackingState == SkeletonTrackingState.Tracked)
{
// make sure both hands are tracked
if (sd.Joints[JointID.HandLeft].TrackingState == JointTrackingState.Tracked &&
sd.Joints[JointID.HandRight].TrackingState == JointTrackingState.Tracked)
{
int cursorX, cursorY;
// get the left and right hand Joints
Joint jointRight = sd.Joints[JointID.HandRight];
Joint jointLeft = sd.Joints[JointID.HandLeft];
// scale those Joints to the primary screen width and height
Joint scaledRight = jointRight.ScaleTo((int)SystemParameters.PrimaryScreenWidth, (int)SystemParameters.PrimaryScreenHeight, SkeletonMaxX, SkeletonMaxY);
Joint scaledLeft = jointLeft.ScaleTo((int)SystemParameters.PrimaryScreenWidth, (int)SystemParameters.PrimaryScreenHeight, SkeletonMaxX, SkeletonMaxY);
// figure out the cursor position based on left/right handedness
cursorX = (int)scaledRight.Position.X;
cursorY = (int)scaledRight.Position.Y;
//default false, for mouse move, set to true for mouse click
bool leftClick;
if (lastY == 0)
{
lastX = cursorX;
lastY = cursorY;
}
leftClick = false;
if (Math.Abs(lastX - cursorX) < threshold && Math.Abs(lastY - cursorY) < threshold)
{
if (Math.Abs(lastClick.Subtract(DateTime.Now).TotalSeconds) > 1)
{
//Mouse click here
leftClick = true;
}
}
//Mouse click when leftDown is true, else Mousemove
NativeMethods.SendMouseInput(cursorX, cursorY, (int)SystemParameters.PrimaryScreenWidth, (int)SystemParameters.PrimaryScreenHeight, leftClick);
return;
}
}
}
NativeMthods.cs class has this function:
public static void SendMouseInput(int positionX, int positionY, int maxX, int maxY, bool leftDown)
{
if(positionX > int.MaxValue)
throw new ArgumentOutOfRangeException("positionX");
if(positionY > int.MaxValue)
throw new ArgumentOutOfRangeException("positionY");
Input[] i = new Input[2];
// move the mouse to the position specified
i[0] = new Input();
i[0].Type = InputMouse;
i[0].MouseInput.X = (positionX * 65535) / maxX;
i[0].MouseInput.Y = (positionY * 65535) / maxY;
i[0].MouseInput.Flags = MouseEventAbsolute | MouseEventMove;
// determine if we need to send a mouse down or mouse up event
if(leftDown)
{
i[1] = new Input();
i[1].Type = InputMouse;
i[1].MouseInput.Flags = MouseEventLeftDown;
i[1].MouseInput.Flags |= MouseEventLeftUp;
}
// send it off
uint result = SendInput(2, i, Marshal.SizeOf(i[0]));
if(result == 0)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
Try something like this:
public class myMouseClass: IMessageFilter //Must implement IMessageFilter
{
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern void mouse_event(long dwFlags, long dx, long dy, long cButtons, long dwExtraInfo);
//Constants
private const int MOUSEEVENTF_LEFTDOWN = 0x02;
private const int MOUSEEVENTF_LEFTUP = 0x04;
private const int MOUSEEVENTF_RIGHTDOWN = 0x08;
private const int MOUSEEVENTF_RIGHTUP = 0x10;
private const int WM_MOUSEMOVE = 0x0200;
private int Threshold = 20; //how far the mouse should move to reset timer
private int StartLocationX = 0; //stores starting X value
private int StartLocationY = 0; //stores starting Y value
private Timer timerHold = new Timer(); //timer to trigger mouse click
//Start Mouse monitoring by calling this. This will also set the timer.
private void StartFilterMouseEvents()
{
timerHold.Interval = 1000; //how long mouse must be in threshold.
timerHold.Tick = new EventHandler(timerHold_Tick);
Application.AddMessageFilter((IMessageFilter) this);
}
//Stop Mouse monitoring by calling this and unset the timer.
private void StopFilterMouseEvents()
{
timerHold.Stop();
timerHold -= timerHold_Tick;
Application.RemoveMessageFilter((IMessageFilter) this);
}
//This will start when Application.AddMessageFilter() is called
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == WM_MOUSEMOVE)
{
if((Cursor.Position.X > StartLocationX + 20)||(Cursor.Position.X > StartLocationX - 20))&&
((Cursor.Position.Y > StartLocationY + 20)||(Cursor.Position.Y > StartLocationY - 20))
{
timerHold.Stop(); //stops timer if running
timerHold.Start(); //starts it again with new position.
StartLocationX = Cursor.Position.X;
StartLocationY = Cursor.Position.Y;
}
}
return false;
}
//This event gets fired when the timer has not been reset for 1000ms
public void timerHold_Tick(object sender, EventArgs e)
{
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
}
}

C# Force Form Focus

So, I did search google and SO prior to asking this question. Basically I have a DLL that has a form compiled into it. The form will be used to display information to the screen. Eventually it will be asynchronous and expose a lot of customization in the dll. For now I just want it to display properly. The problem that I am having is that I use the dll by loading it in a Powershell session. So when I try to display the form and get it to come to the top and have focus, It has no problem with displaying over all the other apps, but I can't for the life of me get it to display over the Powershell window. Here is the code that I am currently using to try and get it to display. I am sure that the majority of it won't be required once I figure it out, this just represents all the things that I found via google.
CLass Blah
{
[DllImport("user32.dll", EntryPoint = "SystemParametersInfo")]
public static extern bool SystemParametersInfo(uint uiAction, uint uiParam, uint pvParam, uint fWinIni);
[DllImport("user32.dll", EntryPoint = "SetForegroundWindow")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("User32.dll", EntryPoint = "ShowWindowAsync")]
private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);
private const int WS_SHOWNORMAL = 1;
public void ShowMessage(string msg)
{
MessageForm msgFrm = new MessageForm();
msgFrm.lblMessage.Text = "FOO";
msgFrm.ShowDialog();
msgFrm.BringToFront();
msgFrm.TopMost = true;
msgFrm.Activate();
SystemParametersInfo((uint)0x2001, 0, 0, 0x0002 | 0x0001);
ShowWindowAsync(msgFrm.Handle, WS_SHOWNORMAL);
SetForegroundWindow(msgFrm.Handle);
SystemParametersInfo((uint)0x2001, 200000, 200000, 0x0002 | 0x0001);
}
}
As I say I'm sure that most of that is either not needed or even flat out wrong, I just wanted to show the things that I had tried. Also, as I mentioned, I plan to have this be asynchronously displayed at some point which I suspect will wind up requiring a separate thread. Would splitting the form out into it's own thread make it easier to cause it to get focus over the Powershell session?
#Joel, thanks for the info. Here is what I tried based on your suggestion:
msgFrm.ShowDialog();
msgFrm.BringToFront();
msgFrm.Focus();
Application.DoEvents();
The form still comes up under the Powershell session. I'll proceed with working out the threading. I've spawned threads before but never where the parent thread needed to talk to the child thread, so we'll see how it goes.
Thnks for all the ideas so far folks.
Ok, threading it took care of the problem. #Quarrelsome, I did try both of those. Neither (nor both together) worked. I am curious as to what is evil about using threading? I am not using Application.Run and I have yet to have a problem. I am using a mediator class that both the parent thread and the child thread have access to. In that object I am using a ReaderWriterLock to lock one property that represents the message that I want displayed on the form that the child thread creates. The parent locks the property then writes what should be displayed. The child thread locks the property and reads what it should change the label on the form to. The child has to do this on a polling interval (I default it to 500ms) which I'm not real happy about, but I could not find an event driven way to let the child thread know that the proerty had changed, so I'm stuck with polling.
I also had trouble activating and bringing a window to the foreground. Here is the code that eventually worked for me. I'm not sure if it will solve your problem.
Basically, call ShowWindow() then SetForegroundWindow().
using System.Diagnostics;
using System.Runtime.InteropServices;
// Sets the window to be foreground
[DllImport("User32")]
private static extern int SetForegroundWindow(IntPtr hwnd);
// Activate or minimize a window
[DllImportAttribute("User32.DLL")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
private const int SW_SHOW = 5;
private const int SW_MINIMIZE = 6;
private const int SW_RESTORE = 9;
private void ActivateApplication(string briefAppName)
{
Process[] procList = Process.GetProcessesByName(briefAppName);
if (procList.Length > 0)
{
ShowWindow(procList[0].MainWindowHandle, SW_RESTORE);
SetForegroundWindow(procList[0].MainWindowHandle);
}
}
Here is some code that I've used on one form or another for a few years. There are a few gotchas to making a window in another app pop up. Once you have the window handle do this:
if (IsIconic(hWnd))
ShowWindowAsync(hWnd, SW_RESTORE);
ShowWindowAsync(hWnd, SW_SHOW);
SetForegroundWindow(hWnd);
// Code from Karl E. Peterson, www.mvps.org/vb/sample.htm
// Converted to Delphi by Ray Lischner
// Published in The Delphi Magazine 55, page 16
// Converted to C# by Kevin Gale
IntPtr foregroundWindow = GetForegroundWindow();
IntPtr Dummy = IntPtr.Zero;
uint foregroundThreadId = GetWindowThreadProcessId(foregroundWindow, Dummy);
uint thisThreadId = GetWindowThreadProcessId(hWnd, Dummy);
if (AttachThreadInput(thisThreadId, foregroundThreadId, true))
{
BringWindowToTop(hWnd); // IE 5.5 related hack
SetForegroundWindow(hWnd);
AttachThreadInput(thisThreadId, foregroundThreadId, false);
}
if (GetForegroundWindow() != hWnd)
{
// Code by Daniel P. Stasinski
// Converted to C# by Kevin Gale
IntPtr Timeout = IntPtr.Zero;
SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, Timeout, 0);
SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, Dummy, SPIF_SENDCHANGE);
BringWindowToTop(hWnd); // IE 5.5 related hack
SetForegroundWindow(hWnd);
SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, Timeout, SPIF_SENDCHANGE);
}
I won't post the whole unit since since it does other things that aren't relevant
but here are the constants and imports for the above code.
//Win32 API calls necesary to raise an unowned processs main window
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
private static extern bool IsIconic(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool SystemParametersInfo(uint uiAction, uint uiParam, IntPtr pvParam, uint fWinIni);
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr lpdwProcessId);
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
[DllImport("user32.dll")]
static extern bool BringWindowToTop(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, Int32 nMaxCount);
[DllImport("user32.dll")]
private static extern int GetWindowThreadProcessId(IntPtr hWnd, ref Int32 lpdwProcessId);
[DllImport("User32.dll")]
public static extern IntPtr GetParent(IntPtr hWnd);
private const int SW_HIDE = 0;
private const int SW_SHOWNORMAL = 1;
private const int SW_NORMAL = 1;
private const int SW_SHOWMINIMIZED = 2;
private const int SW_SHOWMAXIMIZED = 3;
private const int SW_MAXIMIZE = 3;
private const int SW_SHOWNOACTIVATE = 4;
private const int SW_SHOW = 5;
private const int SW_MINIMIZE = 6;
private const int SW_SHOWMINNOACTIVE = 7;
private const int SW_SHOWNA = 8;
private const int SW_RESTORE = 9;
private const int SW_SHOWDEFAULT = 10;
private const int SW_MAX = 10;
private const uint SPI_GETFOREGROUNDLOCKTIMEOUT = 0x2000;
private const uint SPI_SETFOREGROUNDLOCKTIMEOUT = 0x2001;
private const int SPIF_SENDCHANGE = 0x2;
Doesn't ShowDialog() have different window behavior than just Show()?
What if you tried:
msgFrm.Show();
msgFrm.BringToFront();
msgFrm.Focus();
TopMost = true;
.Activate() ?
Either of those any good?
Splitting it out into its own thread is a bit evil as it wont work properly if you don't call it with Application.Run and that will swallow up the thread. In the worst case scenario I guess you could separate it out into a different process and communicate via the disk or WCF.
The following solution should meet your requirements:
Assembly can be loaded into PowerShell and main class instantiated
When ShowMessage method on this instance is called, a new window is shown and activated
If you call ShowMessage multiple times, this same window updates its title text and is activated
To stop using the window, call Dispose method
Step 1: Let's create a temporary working directory (you can naturally use your own dir)
(powershell.exe)
mkdir C:\TEMP\PshWindow
cd C:\TEMP\PshWindow
Step 2: Now let's define class that we will be interacting with in PowerShell:
// file 'InfoProvider.cs' in C:\TEMP\PshWindow
using System;
using System.Threading;
using System.Windows.Forms;
namespace PshWindow
{
public sealed class InfoProvider : IDisposable
{
public void Dispose()
{
GC.SuppressFinalize(this);
lock (this._sync)
{
if (!this._disposed)
{
this._disposed = true;
if (null != this._worker)
{
if (null != this._form)
{
this._form.Invoke(new Action(() => this._form.Close()));
}
this._worker.Join();
this._form = null;
this._worker = null;
}
}
}
}
public void ShowMessage(string msg)
{
lock (this._sync)
{
// make sure worker is up and running
if (this._disposed) { throw new ObjectDisposedException("InfoProvider"); }
if (null == this._worker)
{
this._worker = new Thread(() => (this._form = new MyForm(this._sync)).ShowDialog()) { IsBackground = true };
this._worker.Start();
while (this._form == null || !this._form.Created)
{
Monitor.Wait(this._sync);
}
}
// update the text
this._form.Invoke(new Action(delegate
{
this._form.Text = msg;
this._form.Activate();
}));
}
}
private bool _disposed;
private Form _form;
private Thread _worker;
private readonly object _sync = new object();
}
}
As well as the Form that will be shown:
// file 'MyForm.cs' in C:\TEMP\PshWindow
using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
namespace PshWindow
{
internal sealed class MyForm : Form
{
public MyForm(object sync)
{
this._sync = sync;
this.BackColor = Color.LightGreen;
this.Width = 200;
this.Height = 80;
this.FormBorderStyle = FormBorderStyle.SizableToolWindow;
}
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
this.TopMost = true;
lock (this._sync)
{
Monitor.PulseAll(this._sync);
}
}
private readonly object _sync;
}
}
Step 3: Let's compile the assembly...
(powershell.exe)
csc /out:PshWindow.dll /target:library InfoProvider.cs MyForm.cs
Step 4: ... and load the assembly in PowerShell to have fun with it:
(powershell.exe)
[System.Reflection.Assembly]::LoadFile('C:\TEMP\PshWindow\PshWindow.dll')
$a = New-Object PshWindow.InfoProvider
$a.ShowMessage('Hello, world')
A green-ish window with title 'Hello, world' should now pop-up and be active. If you reactivate the PowerShell window and enter:
$a.ShowMessage('Stack overflow')
The Window's title should change to 'Stack overflow' and the window should be active again.
To stop working with our window, dispose the object:
$a.Dispose()
This solution works as expected in both Windows XP SP3, x86 and Windows Vista SP1, x64. If there are question about how this solution works I can update this entry with detailed discussion. For now I'm hoping the code if self-explanatory.
Huge thanks people.
I think I've made it a bit shorter, here's what I put on a seperate thread and seems to be working ok.
private static void StatusChecking()
{
IntPtr iActiveForm = IntPtr.Zero, iCurrentACtiveApp = IntPtr.Zero;
Int32 iMyProcID = Process.GetCurrentProcess().Id, iCurrentProcID = 0;
IntPtr iTmp = (IntPtr)1;
while (bIsRunning)
{
try
{
Thread.Sleep(45);
if (Form.ActiveForm != null)
{
iActiveForm = Form.ActiveForm.Handle;
}
iTmp = GetForegroundWindow();
if (iTmp == IntPtr.Zero) continue;
GetWindowThreadProcessId(iTmp, ref iCurrentProcID);
if (iCurrentProcID == 0)
{
iCurrentProcID = 1;
continue;
}
if (iCurrentProcID != iMyProcID)
{
SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, IntPtr.Zero, 0);
SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, IntPtr.Zero, SPIF_SENDCHANGE);
BringWindowToTop(iActiveForm);
SetForegroundWindow(iActiveForm);
}
else iActiveForm = iTmp;
}
catch (Exception ex)
{
Definitions.UnhandledExceptionHandler(ex, 103106);
}
}
}
I don`t bother repasting the definitions...
You shouldn't need to import any win32 functions for this. If .Focus() isn't enough the form should also have a .BringToFront() method you can use. If that fails, you can set it's .TopMost property to true. You don't want to leave it true forever, so then call Application.DoEvents so the form can process that message and set it back to false.
Don't you just want the dialog to be a child of the calling form?
To do that you'll need the pass in the calling window and
use the ShowDialog( IWin32Window owner ) method.

Categories

Resources