I'm currently writing a C# program which basically functions as a watchdog for a 3rd party program: If the traffic falls below a certain value, the process is killed and then restarted [using a simple taskkill /im].
This is working perfectly fine, with one exception - if the program is minimized to the system tray, it will not respond to taskkill /im and you have to force-terminate it with taskkill /f /im.
As one can guess, this is by far not the best way to end a program. So I'm currently searching for a way to access the Windows system tray and basically perform a click on the program/call a method to maximize it back to normal.
I had thought about literally simulating a click on the tray, but found this method to be way to inaccurate and unpredictable.
Is there another way to accomplish this [by using a system api, for example]?
Thanks!
You can try using the user32.dll to maximize the processes window.
// Pinvoke declaration for ShowWindow
private const int SW_SHOWMAXIMIZED = 3;
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
// Sample usage
ShowWindow(proc.MainWindowHandle, SW_SHOWMAXIMIZED);
Taken from this SO answer.
For a complete list of window states visit this MSDN Page
You can get the WindowHandle by doing something like this
var processes = Process.GetProcessesByName("nameOfYourProcess");
foreach(var process in processes)
{
var windowHandle = process.getMainWindowHandle;
}
Though this will only work if the process still has a Window.
The only way you can iteract with the tray icon of another application in windows is by simulation the mouse click. This has the mayor flaw that if the user is interacting with the mouse at the same time you "steel" hes cursor and he cannot work.
If you know you will not end up with a memory leek (if its a managed app you've won) and the application you are killing isn't doing some important stuff when it's exiting a process kill is the better option.
Related
I'm trying to create a screen that would pop up that would stop the user from doing anything on the screen, I want it so the user can only type in the box provided and they can't press any other key to exit the screen (like win R, alt f4, ....), any Ideas on what I can do here? thank you.
There isn't much you can do to prevent all the ways of killing a task. If you were creating a Kiosk style application, you can replace explorer.exe and use a custom shell that you write. Exiting your application then immediately logs off, but you still need to handle things like task manager. But in your case, you want to share the desktop with other users. I think the easiest way is to lock the workstation programmatically
using System.Runtime.InteropServices;
[DllImport("user32.dll")]
public static extern bool LockWorkStation();
Then call LockWorkStation(); when you have occasion to do so. Let the OS handle the blocking of the user operations.
Our company has application that runs as a task bar icon - there is no UI besides the task bar icon.
Certain events cause the task bar to launch a explorer.exe to show a directory. User interaction does not cause this, so our application does not have focus.
I am able to show the directory in windows explorer using code like this:
Process.Start("explorer.exe", "c:\somedirectory");
The problem is, the folder launches in the background and I can't seem to give it focus.
Part of the problem is that the explorer.exe process exits immediately, launching the explorer.exe process separately. I am able to find the launched window using Process.processes() and looking at the window title and start time of the process.
Once I finally get a handle on the process (and wait for it to open), I'm trying to focus it. Here's what I've tried:
//trying to bring the application to the front
form.TopMost = true;
form.Activate();
form.BringToFront();
form.Focus();
Process process = ...;
ShowWindow(process.Handle, WindowShowStyle.ShowNormal);
SetForegroundWindow(process.Handle);
SwitchToThisWindow(process.Handle, true);
ShowWindow(process.MainWindowHandle, WindowShowStyle.ShowNormal);
SetForegroundWindow(process.MainWindowHandle);
SwitchToThisWindow(process.MainWindowHandle, true);
This makes the window blink in the task bar, but it still isn't focused.
How can I get the window to come to the front of the screen?
You could use the Shell.Application scripting interface to ask Explorer to create and show a new window. I believe this is also possible using a typed interface, but the exact one escapes me at the moment.
var shellApplication = Type.GetTypeFromProgID("Shell.Application");
dynamic shell = Activator.CreateInstance(shellApplication);
shell.Open(#"C:\drop\");
This seems open the window with focus (Tested on Win 8.1 using a timer which opens after 30 seconds, then navigating around in a focused web browser until the timer fires).
To focus explorer.exe, the application itself needed focus. WinForms intentionally makes this difficult since it could be abused.
Here's how you can steal focus in WinForms. Keep in mind that it may have bad consequences.
Once your application has focus, you can focus another process:
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
SetForegroundWindow(otherProcess.MainWindowHandle);
Here's how I found the explorer process. Like I said, the explorer.exe seems to launch another process and close, so the best option seemed to be to find the most recently launched explorer.exe process:
public static Process GetExplorerProcess()
{
var all = Process.GetProcessesByName("explorer");
Process process = null;
foreach (var p in all)
if (process == null || p.StartTime > process.StartTime)
process = p;
return process;
}
Another option that wouldn't require stealing focus is to show a message from your tray icon. Then you can setup a click handler to open/focus the folder. The application would naturally have focus from the click.
trayIcon.ShowBalloonTip(3000, "", msg, ToolTipIcon.Info);
This falls more in line with "don't annoy the user" but in my case the user is far more annoyed at having to click the bubble.
Update
Finding the explorer process requires admin privileges for your app. I've found that if you focus your own application first, then launch the folder, then the folder is automatically focused. In other words, there is no need to search through the current processes and call SetForegroundWindow.
In my application I'd like to accomplish 2 things when a user wants to take a break and clicks a log out button.
Lock the machine
Launch a custom screen saver that would show the time the user is logged out.
I managed to do the lock easily by:
[DllImport("user32.dll")]
private static extern void LockWorkStation();
I found a tutorial on how to make a custom screen saver. I downloaded the sample code and it worked fine. But when I added the LockWorkStation(); line it killed the screen saver.
Can you help me with this or suggest a workaround?
EDIT
This screen saver from tutorial is just w WinForm. Should I somehow install it to the system? Is it possible form my application level?
The solution most likely is the following:
Lock the workstation
Show the screensaver
For the second step, the following is important:
You application is simply a program showing a window. As such any windows it tries to show are not shown to the user when the workstation is locked.
Your window will only be shown when you register your program as a real screensaver, set it as the current screensaver and than start it, for example using the SC_SCREENSAVE message.
I've got a command line application that starts up and does some work. During that time, it listens to keystrokes (s => show status). It's not the typical command prompt where you press 's' and <ENTER> - it's the type which reacts as soon as the key is pressed the status is shown.
Now I'm trying to "control" that command line application from a fancy GUI application by sending keystrokes. I've tried the more conventional approach of writing to the Process' StandardInput but that doesn't seem to have an effect at all. Also, because the actual process doesn't have a window (it's started with CreateNoWindow=true) I can't try the Win32 API for sending keystrokes to a window.
Is there any other way of doing it?
Fancy console applications are problematic.
They have a tendency to directly read the keyboard input, instead of going through stdin. They also have a tendency to directly control their console, instead of going through stdout.
AFAIK, there is no way to programmatically control these apps. If you really, really need to, I would explore something like AutoHotKey controlling the app on a private desktop (AHK uses a virtual keyboard/mouse driver). I'm not sure how you would read the results off the console, though; it may be possible to create an intermediate console app that's started by your program (in the private desktop) and starts the target app. The intermediate app would then share its console with the target app and use low-level I/O to detect changes.
Or you could use Detours to bend the target app to your will.
Well, I seem to have found an answer to my own question.
It's a real "kludged together" solution, but it works - and for all the intents and purposes of the application I'm building, it doesn't matter.
So, what I did was use two WinAPI functions called
static extern bool ShowWindow(IntPtr WindowHandle, int nCmdShow);
static extern bool SetForegroundWindow(IntPtr WindowHandle);
The first one can be used to Show/Hide a window by changing nCmdShow to 1 and 0 respectively. The other one puts the window (determined by WindowHandle) to the front. Combining these two together, I was able to programmaticly bring the console window up front, do a simple SendKeys.Send(); operation and then hide it again.
// Use a WIN API command to bring the command line to front
SetForegroundWindow(workerProcess.MainWindowHandle);
// Send a keystore to re-display the STATUS of the worker
SendKeys.Send("s");
// Hide the window again.
ShowWindow(workerProcess.MainWindowHandle, 0);
Now, it's a real kludge job, but it gets the job done. One potential pitfall would be if a user is using the computer for something else, and would nail that 1 in a 10000000 moment when the window is active with a 'q' - it would quit the worker program. But the application is intended to be used on dedicated machines that most likely won't even have monitors, keyboards or mice attached to them so it wouldn't be an issue.
Thanks to all who answered, since you did - in one way or another, steer me towards the right solution.
I found an even better way to accomplish the functionality without the theoretical risk of causing problems with simultaneous user input and window-switching.
The trick is to use the WinAPI functions called PostMessage to send up KeyDown (or KeyUp) message to the process which does the same thing. No need to bring the process window to the front and hide it immediately afterwards!
I'm sending the key-down command with key 'S' as the argument:
// 0x0100 - VM_KEYDOWN
// 0x0101 - VM_KEYUP
// 0x53 - S-key
PostMessage(workerProcess.MainWindowHandle, 0x0100, 0x53, 0);
PostMessage(workerProcess.MainWindowHandle, 0x0101, 0x53, 0);
I'm currently working on an access control program in C# and I've come across the problem of blocking windows. The original idea I've come up with is rendering a plain black form over the position given by the IntPtr window handle of the process. The problem within that is updating the form's position smoothly and z-index of the position (being I don't want it topmost). I've also noticed a ridiculously high resource use with my solution as I was using a loop to constantly check position.
Thus why I ask: What would be the best solution for this without eating major resources? The entry point is merely the name of the running process.
Presently the idea is only blocking off browsers (IE: a school application to prevent distraction when a lecture is active).
More Information:
I'm not looking to close a window in
my own application, I'm trying to
obscure windows from other processes.
My application is not a
virus/annoying program, it's
essentially meant to prevent uses of
potentially distracting applications
in a school environment. It's made
for lectures in a school computer
lab.
I'm presently pulling the main window
from the process caught by the
process name of browsers.
I can't completely disable the computers either.
I really hate hate hate any application trying to mess around with other application's windows. Sorry, this comes from very deep.
The only thing I can think of that is somewhat sensible is to lock the current user session, and swith the computer to another desktop belonging to another account with no rights to do anything except what's required under the circumstances.
Or if it is acceptable to disable the use of the computers entirely, you could put all the monitors on a single power switch on the teacher's desk.
If you are looking to hide your application window, I can suggest 3 things. First thing, try setting your form's visible property true and calling the hide() method. Second thing is to set your form's transparency to 100%, which will hide it. The third is, maybe consider your application should be a windows service instead of a windows form application.
If you are looking to hide other windows so that your application is always on top, set the TopMost property to true in your form: http://msdn.microsoft.com/en-us/library/system.windows.forms.form.topmost(VS.71).aspx
This might help too: http://www.codeproject.com/KB/cs/windowhider.aspx
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
static void MinimizeProcess(string procName)
{
foreach (Process p in Process.GetProcesses())
{
if (p.ProcessName == procName)
{
ShowWindow(p.MainWindowHandle,11);
}
}
}
If you have an array of process names you'd obviously want to refactor this to take in an array so that you're not looping through every process for everything you want to minimize, but you get the idea.
You could use the windows api to hide the offending window. user32.showwindow
Not exactly what you've asked for, but might be simpler.
You can do:
this.Hide()
this.Visible = false;
Or if this is the main for you dont want to show, simply dont run the forum when you start the app.