I'm using System.Diagnostics.Process.Start to launch a remote application on another domain machine.
Unfortunately, if the remote process is already running, but in the background of the remote machine's desktop, the application does not gain focus using Process.Start.
Question 1: Is there another API or mechanism to force the remote application to gain focus, or flash to get the user's attention?
The other issue I noticed, is if the remote process is already running, a new instance may be executed in addition to the original. This violates MSDN's documentation which says:
"If the process is already running, no additional process resource is started. Instead, the existing process resource is reused and no new Process component is created. In such a case, instead of returning a new Process component, Start returns null to the calling procedure."
Question 2: Has anyone found a way to prevent a second instance of the application from launching in this case? Is WMI a better choice to use for remote launching of applications?
Well, don't know how well this will work for you, but it is an example class that you could use in the helper program. This is only the start, if you plan on using it, you will need a networking system (not to bad with C# though). Tell me how it works for you.
/// <summary>
/// Allows you to start a specified program, or if it is already running, bring it into focus
/// </summary>
static class SFProgram
{
static public void StartFocus(string FileName, string ProcessName)
{
if (!ProcessStarted(ProcessName))
Process.Start(FileName);
else
SFProgram.BringWindowToTop("notepad");
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
/// <summary>
/// Bring specified process to focus
/// </summary>
/// <param name="windowName">Process Name</param>
/// <returns>If it was successful</returns>
private static bool BringWindowToTop(string windowName)
{
Process[] processes = Process.GetProcessesByName(windowName);
foreach (Process p in processes)
{
int hWnd = (int)p.MainWindowHandle;
if (hWnd != 0)
{
return SetForegroundWindow((IntPtr)hWnd);
}
//p.CloseMainWindow();
}
return false;
}
private static bool ProcessStarted(string ProcessName)
{
Process[] processes = Process.GetProcessesByName(ProcessName);
return (processes.Length > 0);
}
}
Related
I am trying to find out what programs a user is running while my program is running and output them to a file. Now I'm facing the situation that when retrieving all Processes using Process.GetProcesses() I'm greeted with a list of about 269 processes which amounts to about all the Task Manager is showing, including Windows Processes like 77 svchost processes.
Now I want to filter out some system processes (At least those displayed as "Windows-Processes" in the Task Manager). Is there any way to do this or will I have to maintain a list of process names (or file directories) of all Windows Processes?
Short answer:
The solution within the taskmanager is hard coded based on the following list (taken from the Windows 10 version):
%windir%\explorer.exe
%windir%\system32\ntoskrnl.exe
%windir%\system32\WerFault.exe
%windir%\system32\backgroundTaskHost.exe
%windir%\system32\backgroundTransferHost.exe
%windir%\system32\winlogon.exe
%windir%\system32\wininit.exe
%windir%\system32\csrss.exe
%windir%\system32\lsass.exe
%windir%\system32\smss.exe
%windir%\system32\services.exe
%windir%\system32\taskeng.exe
%windir%\system32\taskhost.exe
%windir%\system32\dwm.exe
%windir%\system32\conhost.exe
%windir%\system32\svchost.exe
%windir%\system32\sihost.exe
Long answer:
It took some time to get to that list - below is the path to enlightenment ;-)
Original answer:
To answer your question Find out whether a Process is a System Process is not as easy as it seems. In order to get this information you have to get the owner of the process which on windows systems is typically realted to Security identifiers.
A security identifier (SID) is a unique value of variable length used to identify a trustee. Each account has a unique SID issued by an authority, such as a Windows domain controller, and stored in a security database. Each time a user logs on, the system retrieves the SID for that user from the database and places it in the access token for that user. The system uses the SID in the access token to identify the user in all subsequent interactions with Windows security. When a SID has been used as the unique identifier for a user or group, it cannot ever be used again to identify another user or group.
You will have seen one of those for sure, it is something like S-1-5-18 or S-1-5-21-2557247...-...-...-1001.
There is a complete list of WellKnown SIDs which also includes a bunch of SIDs you would probably all consider as System Process-related.
If I am right in my assumption, you want to get all processes that are running under the local system account which would be S-1-5-18.
Stop talking, let's code:
First of all we (which is you, I have already tested it ;-) ) need to import GetSecurityInfo from advapi32.dll like this:
[DllImport("advapi32.dll", SetLastError = true)]
private static extern uint GetSecurityInfo(IntPtr handle,
SE_OBJECT_TYPE objectType,
SECURITY_INFORMATION securityInfo,
out IntPtr sidOwner,
out IntPtr sidGroup,
out IntPtr dacl,
out IntPtr sacl,
out IntPtr securityDescriptor);
...which requires two enumerations for SE_OBJECT_TYPE and SECURITY_INFORMATION to be defined like this:
private enum SE_OBJECT_TYPE
{
SE_UNKNOWN_OBJECT_TYPE,
SE_FILE_OBJECT,
SE_SERVICE,
SE_PRINTER,
SE_REGISTRY_KEY,
SE_LMSHARE,
SE_KERNEL_OBJECT,
SE_WINDOW_OBJECT,
SE_DS_OBJECT,
SE_DS_OBJECT_ALL,
SE_PROVIDER_DEFINED_OBJECT,
SE_WMIGUID_OBJECT,
SE_REGISTRY_WOW64_32KEY
}
private enum SECURITY_INFORMATION
{
OWNER_SECURITY_INFORMATION = 1,
GROUP_SECURITY_INFORMATION = 2,
DACL_SECURITY_INFORMATION = 4,
SACL_SECURITY_INFORMATION = 8,
}
Now we are almost there. If you call GetSecurityInfo in the following manner...
uint returnValue = GetSecurityInfo(process.Handle,
SE_OBJECT_TYPE.SE_KERNEL_OBJECT,
SECURITY_INFORMATION.OWNER_SECURITY_INFORMATION,
out IntPtr ownerSid,
out IntPtr groupSid,
out IntPtr dacl,
out IntPtr sacl,
out IntPtr securityDescriptor);
... and get ERROR_SUCESS as a result (which is 0), you can use an instance of the SecurityIdentifier class to check whether the retrieved SID is the local system account or not, like this:
SecurityIdentifier securityIdentifier = new SecurityIdentifier(ownerSid);
if (securityIdentifier.IsWellKnown(WellKnownSidType.LocalSystemSid))
{
// The process is running unter the local system account.
}
That's it.
To achieve the final result you will have to check for multiple SIDs like System, Local service, Network service and so on...
Here is a small example, that does this for all processes on the local machine.
You will need to run this with the right priviledges of course, otherwise you will get access denied errors.
private static void Main(string[] args)
{
const uint ERROR_SUCCESS = 0;
Process[] processes = Process.GetProcesses();
foreach (Process process in processes)
{
try
{
uint returnValue = GetSecurityInfo(process.Handle,
SE_OBJECT_TYPE.SE_KERNEL_OBJECT,
SECURITY_INFORMATION.OWNER_SECURITY_INFORMATION,
out IntPtr ownerSid,
out IntPtr groupSid,
out IntPtr dacl,
out IntPtr sacl,
out IntPtr securityDescriptor);
if (returnValue != ERROR_SUCCESS)
{
// If the function succeeds, the return value is ERROR_SUCCESS.
// If the function fails, the return value is a nonzero error code defined in WinError.h.
continue;
}
SecurityIdentifier securityIdentifier = new SecurityIdentifier(ownerSid);
Console.WriteLine("Owner of process {0} is {1}", process.ProcessName, securityIdentifier);
if (securityIdentifier.IsWellKnown(WellKnownSidType.LocalSystemSid))
{
Console.WriteLine("Running under System Account");
}
}
catch (Exception e)
{
Console.WriteLine("Unable to retrieve owner for process {0}: {1}", process.ProcessName, e.Message);
}
}
Update:
If you compare the result (of the original answer) with the list of processes in the task manager, there is still a discrepancy. As I investigated this issue further, I came accross an article that states, that processes which are marked as critical, will be shown under windows processes.
If the process has a visible window, then Task Manager calls it an "App".
If the process is marked as critical, then Task Manager calls it a "Windows Process".
Otherwise, Task Manager calls it a "Background Process".
This can be evaluated by simply calling IsProcessCritical. Therefore an DllImport is needed...
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool IsProcessCritical(IntPtr hProcess, ref bool Critical);
...afterwards it can be called like this:
bool criticalProcess = false;
if (!IsProcessCritical(process.Handle, ref criticalProcess))
{
// Could not retrieve process information
}
if (criticalProcess)
{
// This is a critical process, it should be listed
// in the "Windows processes" section.
}
Although this sounds promising, it is not - it still leads to incorrect results.
So after installing API Monitor (which is an incredible piece of software by the way) and filtering and searching through more than 5 millions of (already pre-filtered) api calls, I noticed, that Taskmgr.exe calls ExpandEnvironmentString multiple times with arguments, that are seemingly not retrieved prior to the calls.
After further investigation (and logical conclusion) I noticed, that there is a hard coded list embedded within Taskmgr.exe. It can be simply found by using the Process explorer:
Starting the process explorer
Right-click on Taskmgr.exe
Navigating to the strings tab
Scrolling down
Being disappointed
There are the following entries:
%windir%\explorer.exe
%windir%\system32\ntoskrnl.exe
%windir%\system32\WerFault.exe
%windir%\system32\backgroundTaskHost.exe
%windir%\system32\backgroundTransferHost.exe
%windir%\system32\winlogon.exe
%windir%\system32\wininit.exe
%windir%\system32\csrss.exe
%windir%\system32\lsass.exe
%windir%\system32\smss.exe
%windir%\system32\services.exe
%windir%\system32\taskeng.exe
%windir%\system32\taskhost.exe
%windir%\system32\dwm.exe
%windir%\system32\conhost.exe
%windir%\system32\svchost.exe
%windir%\system32\sihost.exe
So my conclusion is:The solution within the taskmanager is hard coded based on the above list (taken from the Windows 10 version).
One way to do this is by filtering out all processes whose path starts with the path of the windows directory.
You can get the path of the windows directory by calling Environment.GetFolderPath
with Environment.SpecialFolder.Windows like so:
var windowsPath = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
And then you can filter out all processes whose image is located somewhere in that folder:
var processes = Process.GetProcesses();
foreach (var process in processes) {
if (!process.MainModule.FileName.StartsWith(windowsPath)) {
// Do something with process
}
}
Just filter the result:
Process.GetProcesses().Where(x => x.MainWindowHandle != IntPtr.Zero)
Checking the path, could be bypassed
I've got a console application that executes my code without user interaction. If the user clicks within the console window, on purpose or on accident, all execution stops.
This has something to do with copying text from the console window. The only way for the application to start executing again is if the user selects text and then right-clicks on the console window, copying it to the clipboard.
To see this in action, create a console application and add the following code.
class Program
{
static void Main(string[] args)
{
var task = Task.Run(async () =>
{
int i = 0;
while (true)
{
Console.WriteLine(i++);
await Task.Delay(1000);
}
});
Console.ReadLine();
}
}
When you click on the console window, the Task thread stops executing. This is not desirable behavior at all, and I want to prevent this from happening in my console application.
How can I prevent this? None of the properties/events on the console window have anything to do with controlling this behavior, as far as I can see.
As you can see, when i'm click within window appear cursor. When i press any key - cursor gone and app continue working
This happens if you have Quick Edit Mode enabled on the console window. If you right-click on the title bar and select Properties, then select the Options tab, you can check to see if Quick Edit Mode is enabled. If you disable Quick Edit Mode, then the scrolling doesn't stop when you click in the window.
The reason scrolling stops is because a mouse clicked in the window is used to select text.
You can disable Quick Edit Mode on the console in your program, but doing so requires calling the GetConsoleMode and SetConsoleMode API functions. Here's how you would do it:
[DllImport("kernel32.dll", SetLastError=true)]
public static extern IntPtr GetConsoleWindow();
[DllImport("kernel32.dll", SetLastError=true)]
public static extern bool GetConsoleMode(
IntPtr hConsoleHandle,
out int lpMode);
[DllImport("kernel32.dll", SetLastError=true)]
public static extern bool SetConsoleMode(
IntPtr hConsoleHandle,
int ioMode);
/// <summary>
/// This flag enables the user to use the mouse to select and edit text. To enable
/// this option, you must also set the ExtendedFlags flag.
/// </summary>
const int QuickEditMode = 64;
// ExtendedFlags must be combined with
// InsertMode and QuickEditMode when setting
/// <summary>
/// ExtendedFlags must be enabled in order to enable InsertMode or QuickEditMode.
/// </summary>
const int ExtendedFlags = 128;
void DisableQuickEdit()
{
IntPtr conHandle = GetConsoleWindow();
int mode;
if (!GetConsoleMode(conHandle, out mode))
{
// error getting the console mode. Exit.
return;
}
mode = mode & ~(QuickEditMode | ExtendedFlags);
if (!SetConsoleMode(conHandle, mode))
{
// error setting console mode.
}
}
void EnableQuickEdit()
{
IntPtr conHandle = GetConsoleWindow();
int mode;
if (!GetConsoleMode(conHandle, out mode))
{
// error getting the console mode. Exit.
return;
}
mode = mode | (QuickEditMode | ExtendedFlags);
if (!SetConsoleMode(conHandle, mode))
{
// error setting console mode.
}
}
If you go down this route, it's probably a good idea to save the original console mode setting when your program starts, and restore it when your program exits. So at startup:
GetConsoleMode(GetConsoleWindow(), ref saveConsoleMode);
and when your program terminates:
SetConsoleMode(GetConsoleWindow(), saveConsoleMode);
With appropriate error handling, of course. You wouldn't want to restore the console mode if the call to GetConsoleMode failed.
I just saw that this answer linked in the comments of OP's question contained what I found by myself. I will keep my answer because people might not see it, just like me, and it would spare them a lot of time.
Jim's answer did not work for me, I couldn't figure out why.
I dug around and found a solution that works, so I'll share my findings, hopefully helping someone in the same situation.
The problem was with the handle that I got from GetConsoleWindow(), it gave a Win32 error (0x6) where the handle is invalid when I tried to use it. The call to SetConsoleMode() did nothing.
To get a working handle, I used GetStdHandle() to get the Input handle for the console. Add this to Jim's code :
public const int STD_INPUT_HANDLE = -10;
[DllImport("Kernel32.dll", SetLastError = true)]
public static extern IntPtr GetStdHandle(int nStdHandle);
Then replace GetConsoleWindow() by GetStdHandle(STD_INPUT_HANDLE) in DisableQuickEdit() and EnableQuickEdit() in Jim's code.
After calling DisableQuickEdit(), the selection is disabled in the console.
Thanks Jim !
I want my c# winform application to switch to another running instance if a certain event occurs.
For example if I have a application with just a button and three instances are running at the moment. Now if I
press the button in first instance, focus to second instance
press the button in second instance, focus to third instance
press the button in third instance, focus to first instance
How do i do that?
if you know the handle of the other instances you should just call the Windows API: SetForegroundWindow:
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
you can use the FindWindow API call to get the handle of the other instances, for example:
public static int FindWindow(string windowName)
{
int hWnd = FindWindow(null, windowName);
return hWnd;
}
you can search for those api calls here in SO for more examples, for example found this one:
How do I focus a foreign window?
SetForegroundWindow is a great solution. An alternative is to use named Semaphores to send signals to other applications.
Lastly you could look for a Inter-Process Communication (IPC) solution which would allow you to send messages between processes.
I wrote a simple .Net XDMessaging library that makes this really easy. Using it you can send instructions from one application to other, and in the latest version even pass serilaized objects. It's a multicast implementation that uses a concept of channels.
App1:
IXDBroadcast broadcast = XDBroadcast.CreateBroadcast(
XDTransportMode.WindowsMessaging);
broadcast.SendToChannel("commands", "focus");
App2:
IXDListener listener = XDListener.CreateListener(
XDTransportMode.WindowsMessaging);
listener.MessageReceived+=XDMessageHandler(listener_MessageReceived);
listener.RegisterChannel("commands");
// process the message
private void listener_MessageReceived(object sender, XDMessageEventArgs e)
{
// e.DataGram.Message is the message
// e.DataGram.Channel is the channel name
switch(e.DataGram.Message)
{
case "focus":
// check requires invoke
this.focus();
break;
case "close"
this.close();
break;
}
}
I am creating a scheduled task to run process monitor at its highest privileges. I have a windows service that executes the scheduled task on start. Thus on start of my service, process monitor.exe will be executed shown in a window. But I don't want to see the window. I just want the process monitor.exe to run in the background without displaying any windows.
In AutoIT, there is a command: Run (Procmon.exe,"",#SW_Hide) #SW_Hide = Hidden Window
I tried this:
foreach (Process pr in Process.GetProcesses())
{
if(pr.ProcessName == "procmon")
{
hWnd = pr.MainWindowHandle.ToInt32();
ShowWindow(hWnd, SW_HIDE);
}
}
It's better to tell the process to show no window in the first place, instead of hiding it afterwards.
When running a program from .net you usually already have a ProcessStartInfo. Then just set its WindowStyle property to ProcessWindowStyle.Hidden and that should take care of it.
I haven't tried this myself, but that's the way you usually do it when calling the WinApi functions directly.
http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.windowstyle.aspx
http://msdn.microsoft.com/en-us/library/system.diagnostics.processwindowstyle.aspx
You need to get Window Handle of Process Monitor first and then you need to call ShowWindow with SW_HIDE to hide it.
You can use FindWindow to get the Window Handle of the ProcMon window.
Edit:
After looking at your code, I tried at my end and it works with the following code:
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
public static void HideWindow()
{
int SW_HIDE = 0;
foreach (Process pr in Process.GetProcesses())
{
if (pr.ProcessName.Contains("Procmon"))
{
//Int32 hWnd = pr.MainWindowHandle.ToInt32();
ShowWindow(pr.MainWindowHandle, SW_HIDE);
}
}
}
static void Main(string[] args)
{
HideWindow();
}
Most likely, the problem with your code is that you are trying to find an exact match of the process name which isn't there.
Procmon has built-in functionality to automatically log at startup, if that's what you're trying to accomplish.
I am using the below code to block the taskbar which is working perfectly.
But since my application is running in background, the only way to exit the application
is by killing the .exe from task manager. So while exiting like this, the blocked task bar remains at the same state. But actually it shud resume the taskbar on exiting the application.
The reason i am doing this is, it is a kiosk application.
what is the way to overcome this.
public class Taskbar
{
[DllImport("user32.dll")]
public static extern int FindWindow(string className, string windowText);
[DllImport("user32.dll")]
public static extern int ShowWindow(int hwnd, int command);
public const int SW_HIDE = 0;
public const int SW_SHOW = 1;
public int _taskbarHandle;
protected static int Handle
{
get
{
return FindWindow("Shell_TrayWnd", "");
}
}
public Taskbar()
{
_taskbarHandle = FindWindow("Shell_TrayWnd", "");
}
public static void Show()
{
ShowWindow(Handle, SW_SHOW);
}
public static void Hide()
{
ShowWindow(Handle, SW_HIDE);
}
}
Why not just use this implementation to run completely fullscreen?
http://www.codeproject.com/KB/cs/FullScreenDotNetApp.aspx
Like the others have said when you killin the application no.
Your post is a bit sparse on why you cannot close your application gracefully, so il suggest this method.
1)
Hotkeys ( http://www.codeproject.com/KB/system/Hotkeys.aspx ) that can be pressed that will close down your application gracefully. I personaly like this method, as i use hotkeys in many of my apps.
2)
Starting a seperate application that will wake up every XXX and check if the main application is running, if its not running run the Show code and then kill itself. This method is very simular to how viruses often work, so its tried and works :)
If your only way to exit is by killing it, then I am afraid you can't reset the property back to normal.
There can be a workaround to this.
Create a service which monitors your application through polling and when it finds your application as 'not running', it restores the TaskBar to normal.
There can be other similar workarounds to this but I can't think of a way of doing this from within your application given the limitation.
For exiting the application, i am registering a hot key combination and resuming the task bar and kill the process from taskbar programatically.
you can make your application check for the existance of a file (or any other thing you can control from outside and the app have access to it), if found: dispose & exit.
it's dirty but gives a little bit more control of how your application terminate than killing it from the task manager.