I want my program to trigger an action only when certain other programs are currently being used. I get the current foreground HWND with GetForegroundWindow(). But HWNDs change over time, so that's not a way to identify those programs. The same goes for Process IDs and handles. What is a way to identify the foreground program over reboots?
I first thought GetModuleFileNameExA should work, but my code crashes because it is not found:
Handle handle = GetProcessHandleFromHwnd(hWID);
String Name = null;
GetModuleFileNameExA(
handle,
null,
Name,
2147483647
);
[DllImport("Kernel32.dll", CharSet = CharSet.Ansi)]
private static extern IntPtr GetModuleFileNameExA(
Handle hProcess,
Object hModule,
String lpFilename,
Int32 nSize
);
You need to use GetWindowThreadProcessId. This is explained better in the answers to this question: Find process id by window's handle.
You should then be able to use System.Diagnostics.Process.GetProcesses() to match the pid to a process and its modules. You may find that you cannot access the details of 64 bit processes from a 32 bit process, or that security blocks access to some information.
Related
I am trying to call WaitForSingleObject method from C#, as documented here:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms687032(v=vs.85).aspx
In order to call this function I need to create a Handle, or I need to get a Handle of type IntPtr, how can it be done?
I've tried this function that I found:
http://www.pinvoke.net/default.aspx/kernel32.WaitForSingleObject
[DllImport("coredll.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto)]
public static extern IntPtr CreateEvent(HANDLE lpEventAttributes, [In, MarshalAs(UnmanagedType.Bool)] bool bManualReset, [In, MarshalAs(UnmanagedType.Bool)] bool bIntialState, [In, MarshalAs(UnmanagedType.BStr)] string lpName);
Or for instance, when I am getting handle from console:
IntPtr handle = Process.GetCurrentProcess().MainWindowHandle;
It throws a DllNotFoundException.
What's the issue here?
I need it in order to run the process with this function call, and to take a dump form its process, for my ClrMd library learning.
Any help will be appreciated.
Code sample:
static void Main(string[] args)
{
var autoEvent = new AutoResetEvent(false);
//this is where I get the DllNotFoundException
WaitForSingleObject(autoEvent.Handle, WAIT_TIMEOUT );
}
[DllImport("kernel32.dll")]
static extern uint WaitForMultipleObjects(uint nCount, IntPtr[] lpHandles, bool bWaitAll, uint dwMilliseconds);
public const Int32 WAIT_TIMEOUT = 0x102;
I would not go through WinApi to get this from C#: you have EventWaitHandler and other synchronization objects in C#, use them:
WaitHandle wh = new EventWaitHandler();
//do whatever you need
...
WaitHandler.WaitOne(wh); // equivalent to WaitForSingleObject in WinApi
you can use wh.SafeWaitHandle if you really need to interop with WinApi
Also I suspect Process.GetCurrentProcess().MainWindowHandle cannot work in a Console Application, that has not any window at all
I want to call native method (WaitForMultipleObjects) which waits for some handle (don't really mind which one), then I want to see it on thread stack using ClrMd library, from dump file
OK, so what about new ManualResetEvent(false).WaitOne()? This should show up in the dump file. And it's reliable.
Just picking any existing handle is not reliable because it might be signaled or be destroyed at any time. Or, you might change its state by waiting. There is no need, a ManualResetEvent can create you a fresh handle.
My mistake I've posted WaitForMultipleObjects instead of WaitForSingleObject, the main issue was that WaitForSingleObject stayed with DllImport("coredll.dll"...) I don't know where did I found it but I did...
Sorry for the confusion
I'm having problems with switching application and assigning them to a variable. This is what I have so far.
[DllImport("user32.dll")]
public static extern void SwitchToThisWindow(IntPtr hWnd, bool b);
Process[] procs = Process.GetProcessesByName("Excel");
foreach (Process proc in procs)
{
SwitchToThisWindow(proc.MainWindowHandle, false);
}
In the end I want to assign that Excel window to be assigned to xlApp. I tried:
xlAppl = SwitchToThisWindow(proc.MainWindowHandle, false); but obviously this won't work because the interface is a void. I tried looking at GetActiveWindow I was thinking of getting active window and assign it but it's return value is not an Object it's IntPtr.
Is there a way out of this?
There are a couple things I want to point out about what you have so far:
For SwitchToThisWindow MSDN states - [This function is not intended for general use. It may be altered or unavailable in subsequent versions of Windows.]. So I personally wouldn't use that one if I were you.
Your Process.GetProcessByName call is going to return all excel processes. Then you're going to try to bring each one to the foreground. Only the last one of course will actually be in the foreground, since they can't all be in the foreground. You should probably decide on a more accurate way of choosing which one you want.
You are assuming that MainWindowHandle is not IntPtr.Zero which is not a valid assumption in all cases. MainWindowHandle will only have a value once there is a visible window that has been rendered.
If I were you I'd try using the SetForegroundWindow and SetActiveWindow APIs instead.
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern bool SetForegroundWindow(
IntPtr hWnd
);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SetActiveWindow(
IntPtr hWnd
);
Guys i found the solution. Pretty simple here is the answer if anyone else is looking for this:
Excel.Application xlApp = (Excel.Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");
My purpose want to catch TASKKILL event in Command Prompt, and use this event.
Maybe, I think need to use kernel32.dll but I can't find a handler for this.
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool TerminateProcess(IntPtr hProcess, uint uExitCode);
Update
Follow ways #Ben Voigt suggest:
_Using WMI:
Step 1: Run the command mgmtclassgen Win32_Process /n root\cimv2 /o WMI.Win32 to generate the class Process. And then renaming the class Process to Win32_Process.
http://notepad.cc/share/3SQfeJgEQR
Step 2: Create a class with name ProcessWatcher
http://notepad.cc/share/UIR1Tw5twy
Step 3: Using this class with while loop for waiting my application status.
This is easy way but not my choice.
http://notepad.cc/share/JXLGogGbai
_Using Window Hook:
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DuplicateHandle(IntPtr hSourceProcessHandle,
ushort hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle,
uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwOptions);
But TerminateProcess and DuplicateHandle, I can't find a handler for this.
From some other process, which we shall call the watchdog, you must get a handle to the process you will monitor for termination (the target process). You can have a handle created using DuplicateHandle and communicated via an IPC mechanism. If you know the PID of the target process, you can use OpenProcess or System.Diagnostics.Process.GetProcessById. If the target process is spawned by the watchdog, you get a handle from CreateProcess or System.Diagnostics.Process.Start. Or you can enumerate running processes, for example using System.Diagnostics.Process.GetProcessesByName.
In any case, once you have a handle to the process, you can pass it to one of the wait functions such as WaitForSingleObject, WaitForMultipleObjects, or MsgWaitForMultipleObjectsEx. When the process ceases running, for example because TASKKILL terminated it, the process handle becomes signaled and the wait will complete.
If you use the .NET Process class and its WaitForExit method, be aware that unlike the Win32 wait functions, there is no multi-handle version; you'll need to dedicate an entire thread.
A possibly easier way is to use WMI and subscribe to process events. I tend not to use WMI myself, but it could be useful if you don't have a parent/child relationship between watchdog and target, making the handle otherwise difficult to get. You can read about it on this blog:
Using WMI to monitor process creation, deletion and modification in .NET
I am shelling out to do some work and one of the requirements is to kill the process if it is hung.
My first thought was Process.Responding, however, I am not sure what it really means.
Is it the same thing as when Win7 adds a (Not Responding) to the window title of an application? On my machine, this happens even when MS Word tries to open a file from a really slow remote share.
What are the conditions for having a Process.Responding be false?
Under the hood, when you check the Process.Responding property, the Windows function SendMessageTimeout is called.
This function basically sends a message to the main window of another process and checks whether the window is accepting the message within a timeout of 5000 ms (Therefore checking this property on a console application has no effect).
If you want to use a custom timeout you can just as well call the SendMessageTimeout function yourself:
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr SendMessageTimeout(
HandleRef hWnd,
int msg,
IntPtr wParam,
IntPtr lParam,
int flags,
int timeout,
out IntPtr pdwResult);
const int SMTO_ABORTIFHUNG = 2;
public bool RespondingWithinMs(Process process, int timeoutMs)
{
IntPtr ptr2;
return SendMessageTimeout(
new HandleRef(process, process.MainWindowHandle),
0,
IntPtr.Zero,
IntPtr.Zero,
SMTO_ABORTIFHUNG,
timeoutMs,
out ptr2) != IntPtr.Zero;
}
Responding means the application window is responding to the user. the process should have a MainWindowHandle from msdn:
true if the user interface of the associated process is responding to the system; otherwise, false.
If the process does not have a MainWindowHandle, this property returns true.
You can modify the timeout used by application.Responding check this.
From http://msdn.microsoft.com/en-us/library/system.diagnostics.process.responding.aspx
If a process has a user interface, the Responding property contacts the user interface to determine whether the process is responding to user input. If the interface does not respond immediately, the Responding property returns false. Use this property to determine whether the interface of the associated process has stopped responding.
In order to get the application name of the foreground Window (or the name of application file) I want to use GetActiveWindow with GetWindowModuleFileName.
I found a similar question relating to GetWindowText here
That implementation of GetWindowText works fine, but GetWindowModuleFileName only returns a value for visual studio (when I click inside the devenv) for all other applications it stays blank.
Any hint how I can find out what goes wrong? Might this have to do with permission/security of my application querying the applicationfilename of another process?
EDIT: http://support.microsoft.com/?id=228469 looks like this doesn't work under Win >=XP
Any alternatives how to get the application file name?
In order to get the application name of the foreground Window (or the name of application file) I want to use GetActiveWindow with GetWindowModuleFileName.
... querying the applicationfilename of another process ...
In my opinion your problem with use of GetActiveWindow() function. It is used for gathering information from the calling thread/process only. If calling thread is inactive GetActiveWindow return 0;
From MSDN:
GetActiveWindow Retrieves the window handle to the active window attached to the calling thread's message queue.
Try to use GetForegroundWindow() function instead of GetActiveWindow()
By chance do you have UAC turned off?
Starting with Vista, if your code touches an HWND in another process, your process needs to be run at the same privilege level.
In other words, if the window is hosted in a process running as administrator, your app must also run as administrator.
I found a workaround using this:
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
IntPtr handle = IntPtr.Zero;
handle = GetForegroundWindow();
uint processId;
if (GetWindowThreadProcessId(handle, out processId) > 0)
{
Console.WriteLine(Process.GetProcessById((int)processId).MainModule.FileName);
}