I'm creating an application that needs to check the response of some process.
System.Diagnostics.Responding doesn't works properly. So I'm trying to find a solution using windows API.
I already tried SendMessageTimeOut like this:
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr SendMessageTimeout(
IntPtr hWnd,
uint msg,
UIntPtr wParam,
IntPtr lParam,
uint flags,
uint timeout,
out IntPtr pdwResult);
public static bool IsResponding(Process process)
{
IntPtr hProcess = IntPtr.Zero;
try
{
hProcess = OpenProcess(
ProcessAccessFlags.QueryInformation | ProcessAccessFlags.DuplicateHandle | ProcessAccessFlags.Synchronize,
false,
(uint)process.Id
);
if (hProcess == IntPtr.Zero)
return false;
IntPtr hResult = SendMessageTimeout(hProcess, 13, UIntPtr.Zero, IntPtr.Zero, SMTO_ABORTIFHUNG, 2000, out IntPtr pdwResult);
return hResult != IntPtr.Zero;
}
catch
{
return false;
}
finally
{
if (hProcess != IntPtr.Zero)
CloseHandle(hProcess);
}
}
Other attempt was with IsHungAppWindow.
[DllImport("user32.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsHungAppWindow(IntPtr hwnd);
I really do not know why it always returns me that the application is not responding.
My PInvokes works fine, I think, don't throw anytime. And OpenProcess are working too.
I've create a WPF app that can respond and not respond every 15 sec. Task Manager can get No responding properly but my app no.
What I did wrong? All I read about advice me to use that two functions.
The problem is that SendMessageTimeout and IsHungAppWindow require you to pass window handles, but you are passing process handles. Obtain a handle to the application's main window, and pass that instead.
Related
I'm trying to install either a WH_KEYBOARD_LL or WH_MOUSE_LL hook into a certain Process/Window.
[DllImport("User32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, HookHandler fn, IntPtr module, uint dwThreadId);
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr lpdwProcessId);
public bool Install(Process process)
{
const int WH_KEYBOARD_LL = 0x0D;
if (instance == IntPtr.Zero)
{
var threadId = GetWindowThreadProcessId(process.MainWindowHandle, IntPtr.Zero);
instance = SetWindowsHookEx(WH_KEYBOARD_LL, handler, IntPtr.Zero, threadId);
}
return instance != IntPtr.Zero;
}
Where handler is a reference to my IntPtr Callback(int nCode, IntPtr wParam, IntPtr lParam) callback.
I can successfully hook globaly by replacing the third SetWindowsHookEx argument to a result of the LoadLibrary("User32") call and making threadId 0, like so:
var module = LoadLibrary("User32");
SetWindowsHookEx(WH_KEYBOARD_LL, handler, module, 0u);
How do I get it to work?
You can't.
As documented, a WH_KEYBOARD_LL can be installed as a global hook only. Both low-level keyboard and mouse hooks are executed long before the eventual input receiver has been determined.
The diagram posted under When something gets added to a queue, it takes time for it to come out the front of the queue illustrates, when low-level hooks run.
I'm randomly encountering the WriteFile(..) method returning false. When I call GetLastError() the value returned is 6. Does anyone know what is causing this issue? Here is some of my code:
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool WriteFile(IntPtr hFile, byte[] lpBuffer,
uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten,
IntPtr lpOverlapped);
public void Connect()
{
IntPtr m_WriteHandleToUsbDevice = CreateFile(m_DevicePathName,
GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING,
0, IntPtr.Zero).DangerousGetHandle();
}
public void SendCommand()
{
if (!WriteFile(m_WriteHandleToUsbDevice, command, bytesToWrite, out bytesToWrite, IntPtr.Zero))
{
uint lastError = GetLastError(); // lastError = 6
}
}
I can send many commands successfully, but it eventually fails at some point. I've tried many different signatures of the WriteFile method but none seem to get around this issue (most just swapping the last parameter for the System.Threading.NativeOverlapped struct). A lot of similar issues were caused by an event property of the NativeOverlapped struct not being initialized, but I've tried that with no success. Below is one of the alternate signatures and logic I've tried:
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool WriteFile(IntPtr hFile, byte[] lpBuffer,
uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten,
[In] ref NativeOverlapped lpOverlapped);
public void SendCommand()
{
var ewh = new EventWaitHandle(false, EventResetMode.AutoReset);
var unusedOutVariable = new NativeOverlapped {EventHandle = ewh.SafeWaitHandle.DangerousGetHandle()};
if (!WriteFile(m_WriteHandleToUsbDevice, command, bytesToWrite, out bytesToWrite, IntPtr.Zero))
{
uint lastError = GetLastError(); // lastError = 6
}
}
Using DangerousGetHandle() does not keep the file open. During the next garbage collector pass, the SafeHandle object will be detected as unreachable and its finalizer will be scheduled. The finalizer will close the handle. Then your other code begins to fail. Why did you think the word Dangerous was in the function name?
Save the SafeHandle instance in your class member data rather than the IntPtr.
So there is a beautiful technique for acquiring a COM pointer cross-process (same machine) from an Excel.exe session if you know its Hwnd using the Accessibility API. The specific Windows API function is AccessibleObjectFromWindow; if called with parameter of OBJID_NATIVEOM then Excel.exe marshals back a COM pointer to an Excel.Window object. Very cool.
So I was wondering if developers can implement the same technique for their own applications. The answer is yes, they respond to a certain message, WM_GETOBJECT, in their message pump code. Whilst this is doable for a C++ application, I am puzzled as how to do this for a C# application.
I'm presuming the answer is to do something to get access to the message pump handling code and alter it. It maybe the case that some magic attribute could be used. I'm open to either technique so long as it works.
Here is the code that acquires COM pointer from Excel
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("oleacc.dll", SetLastError = true)]
internal static extern int AccessibleObjectFromWindow(IntPtr hwnd, uint id, ref Guid iid,
[In, Out, MarshalAs(UnmanagedType.IUnknown)] ref object ppvObject);
bool IXlMoniker.GetExcelByHwnd(int lhwndApp2, ref object appRetVal)
{
bool bRetVal = false;
IntPtr lhwndApp = (IntPtr)lhwndApp2;
IntPtr lHwndDesk = FindWindowEx(lhwndApp, IntPtr.Zero, "XLDESK", "");
if (lHwndDesk != IntPtr.Zero)
{
IntPtr lHwndExcel7 = FindWindowEx(lHwndDesk, IntPtr.Zero, "EXCEL7", null);
if (lHwndExcel7 != IntPtr.Zero)
{
Guid IID_IDispatch = new Guid("{00020400-0000-0000-C000-000000000046}");
const uint OBJID_NATIVEOM = 0xFFFFFFF0;
object app = null;
if (AccessibleObjectFromWindow(lHwndExcel7, OBJID_NATIVEOM, ref IID_IDispatch, ref app) == 0)
{
dynamic appWindow = app;
appRetVal = appWindow.Application;
return true;
}
}
}
return bRetVal;
}
This looks promising Return an IOleCommandTarget from processing WM_GETOBJECT in a NativeWindow
My program, finds any subwindows, the case if an 3-part app shows an Error box, then i want to close it.. and it works fine, when the server not is locked.
Code:
[DllImport("user32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private extern static bool PostMessage(IntPtr hwnd, uint msg, IntPtr WParam, IntPtr lParam);
[DllImport("user32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private extern static bool BringWindowToTop(IntPtr hwnd);
public static bool FocusWindowAndSendEnter(IntPtr hWnd)
{
try
{ // hWnd = pointer to subwindow, like messagebox.
uint WM_KEYDOWN = 0x0100;
//Set focus
var res1 = BringWindowToTop(hWnd);
//Send enter_Key
var res2 = PostMessage(hWnd, WM_KEYDOWN, (IntPtr)Keys.Enter, IntPtr.Zero);
return (res1 == res2 == true);
}
catch (Exception ex)
{
Logger.LogException(MethodInfo.GetCurrentMethod().Name, ex);
}
return false;
}
Can this be done if the server is locked, but running?
When a computer is locked, you are very limited in what you can do - for security purposes. I doubt there is a way around this unless you either stop the computer being locked, or stop the message box from showing up in the first place.
You're attempting to solve a problem in the wrong methods.
Find out what you can about that error message box and do what you can to prevent that happening. Don't expect to have much you can do when the PC is locked.
I am using the below code to exit a process programatically.
since i am new to this concept. I want to know how to use this below code.
Logic : I will have the process name to be terminated, i shud assign that to
this function.
Say if want to terminate a notepad, how to pass parameter [Process Name]
to this function ?
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
static uint WM_CLOSE = 0xF060;
public void CloseWindow(IntPtr hWindow)
{
SendMessage(hWindow, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
}
Use the Process.CloseMainWindow instead of manually sending the message. This will send the message to the main window of the process:
using System.Diagnostics;
// ...
foreach (var process in Process.GetProcessesByName("notepad.exe"))
process.CloseMainWindow();
Alternatively, you can use MainWindowHandle to get the handle of the main window of a Process and send a message to it:
foreach (var process in Process.GetProcessesByName("notepad.exe"))
CloseWindow(process.MainWindowHandle); // CloseWindow is defined by OP.
If you want to kill the process immediately instead of closing the main window, this is not a good approach. You should use the Process.Kill method instead.
Although I agree with Mehrdad's answer but if you really want to re-invent the wheel, then this is how to do it (This is without any error checking etc. Please add that yourself).
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
static uint WM_CLOSE = 0x10;
static bool CloseWindow(IntPtr hWnd)
{
SendMessage(hWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
return true;
}
static void Main()
{
IntPtr hWnd = FindWindowByCaption(IntPtr.Zero, "Untitled - Notepad");
bool ret = CloseWindow(hWnd);
}
BTW, Here is a good place to view Managed declarations of native API's