Unlike Windows Vista, when Windows XP is shutting down - it doesn't tell you which program is requesting not to be shutdown. This leaves Windows still running and not telling you what program is preventing shutdown.
As I understand it, Windows sends WM_QUERYENDSESSION to all applications. If any of the applications return 0 to the function, shutdown is halted.
I am wondering is there a method to capture the return values from programs and determine why Windows XP is not shutting down.
Thanks
Phil
Enumerate all top-level windows (use EnumWindows()) and send WM_QUERYENDSESSION to each. For the window that returns a preventing value detect which process it is. To do the latter call GetWindowThreadProcessId(), enumerate all processes and find the process with that id.
Yes. I posted code in this question, but this is an improved version of the code:
void CQes_testDlg::OnBtnTest()
{
// enumerate all the top-level windows.
m_ctrl_ListMsgs.ResetContent();
EnumWindows (EnumProc, 0);
}
BOOL CALLBACK EnumProc (HWND hTarget, LPARAM lParam)
{
CString csTitle;
CString csMsg;
CWnd * pWnd = CWnd::FromHandle (hTarget);
BOOL bRetVal = TRUE;
DWORD dwPID;
if (pWnd)
pWnd->GetWindowText (csTitle);
else
csTitle = TEXT("<unknown>");
GetWindowThreadProcessId (hTarget, &dwPID);
if (pWnd->SendMessage (WM_QUERYENDSESSION, 0, ENDSESSION_LOGOFF))
{
csMsg.Format ("window 0x%X (PID=%d, Title='%s') returned TRUE",
hTarget, dwPID, csTitle);
}
else
{
csMsg.Format ("window 0x%X (PID=%d, Title='%s') returned FALSE *******",
hTarget, dwPID, csTitle);
bRetVal = FALSE;
}
mg_pThis->m_ctrl_ListMsgs.AddString (csMsg);
return bRetVal;
}
mg_pThis is a module-global pointer to the dialog object, so the enumerator can get access to it's control.
Related
I'm currently coding a windows service (installed as LocalSystem) that monitors several things on a pc/server including processes. For the processes, I'm watching the memory usage and also "try" to get the number of GDI Objects per process (as can be seen in task manager).
Sadly, C# Process objects don't have the gdi count built-in so I'm using the GetGuiResources method from 'user32.dll' as shown in this example:
https://www.pinvoke.net/default.aspx/user32.getguiresources.
Basically I have a list of executable names, for each of them I use GetProcessesByName to retrieve all process instances, and then for each unique process I take the handle and send it to the function to get the Gdi objects count back.
When I try this on my local machine as a simple console app (feeding a name through Console.ReadLine), it works no problem as long as the console app is launched as administrator; i get the same numbers as task manager.
However, when the monitoring service calls this function, I get either 0s (returning error code 87) or worse: processes tied to services (no gui) return me some random numbers (12, 7, 4, etc.) when the task manager actually shows 0 (and last error = 0).
So in summary, every process that shows some GID objects in Task Manager returns 0 (error 87), and each process who has 0 returns me a number (no error, or error 183 for the monitoring service itself).
I've tried this on Windows 10, Windows Server 2012, Windows Server 2008, Windows Server 2003, Windows Server 2016. On windows 10 (my machine) I get 0s everywhere, on other OS I get the mentionned results.
Here's a shortened version of the code I use:
// Monitoring processes exeName example: ssms, sqlbrowser
List<Process> result = Process.GetProcessesByName(exeName).ToList();
if (processes != null)
{
for (int i = 0; i < processes.Count; i++)
{
int gdiCount = processes[i].GetGDIObjectsCount(); // extension method
// logging and doing stuff with gdi count here (but i get 0s or random numbers as I told)
}
}
// Process extension method
public static class CProcessExtensions
{
[DllImport("User32", SetLastError = true)]
extern private static int GetGuiResources(IntPtr hProcess, int uiFlags);
private static int GetGDICount(IntPtr processHandle)
{
if (processHandle == IntPtr.Zero)
{
return -1;
}
int count = GetGuiResources(processHandle, 0);
// Logging Marshal.GetLastWin32Error() here
return count;
}
public static int GetGDIObjectsCount(this Process process)
{
IntPtr handle;
process.Refresh();
try
{
handle = process.Handle;
}
catch (Exception ex)
{
handle = IntPtr.Zero;
}
return GetGDICount(handle);
}
}
I've also tried getting the process handles with the OpenProcess dll method but had the same results.
Anyone faced this kind of problem before?
So, thanks to Jeremy Thompson's comment leading me to info about the session 0, and with further research, I was able to solve my problem.
References:
Application Loader to launch process in another session
Wait for process exit (ProcessWaitHandle)
Get Exit code
What I did is modify the sample code from the first reference to provide a process ID (the one I want the GDI objects count of) and launch my little console app (which takes the same process ID also, and returns the GDI count as exit code) in the same session by duplicating the token of the provided process and call CreateProcessAsUser.
By launching the console app in the same session I was able to retrieve the correct info on GDI objects on every OS I previously tested except Win Server 2003, which I can totally live without.
Even though there are command-line commands to start (most of?) the various Control Panel screens in Windows 10, a specific scenario seems to fail:
If the machine starts with Bluetooth turned off (not disabled), running the command which should open the Bluetooth settings screen, simply does nothing. The command could be either ms-settings:bluetooth, bthprops.cpl or ms-settings:Bluetooth.
I've also tried to directly launch the Bluetooth Devices screen (using the command %windir%\explorer.exe shell:::{28803F59-3A75-4058-995F-4EE5503B023C} as described here), but clicking on the "Bluetooth settings" in this window does nothing as well.
The only way to get directly to the Bluetooth settings screen without going through the main Control Panel window and without turning on Bluetooth first, is by right clicking on the relevant tile in Windows Action Center:
Although this seems like a bug on the operating system level, I was wondering if there's any way to know when the launch fails from within C# code. So I've tried using the following code:
try
{
var process = new Process();
process.StartInfo.FileName = "control";
process.StartInfo.Arguments = "bthprops.cpl";
process.Exited += (s, e) =>
{
if (process.ExitCode != 0)
{
TurnOnBt();
}
};
var res = process.Start();
if (!res)
{
TurnOnBt();
}
}
catch (System.Exception ex)
{
int test = 6; // just for breakpoint
}
Problem is, no exception was ever thrown, and most of the time the Process.Exit event was never called.
Further more, calling Windows.Devices.Radios.Radio.GetRadiosAsync() returns an empty list!
Currently the only solution I've found is to manually turn on Bluetooth - it wouldn't change the Process.Start/Exit behavior, but it does allow to successfully lunch the command to directly open Bluetooth Settings window, and to get the list of the machine's Bluetooth/Radio devices. Still, when turning off Bluetooth and restarting the machine, same problem would happen all over again.
Any ideas for a code-based workaround?
note - all this based only on my debugging research, nothing from this is documented
i look how BT-settings window is open via Action Center (win8.1, win 10):
the IApplicationActivationManager interface created and called ActivateApplication method with:
appUserModelId = L"windows.immersivecontrolpanel_cw5n1h2txyewy!microsoft.windows.immersivecontrolpanel"
arguments = L"page=SettingsPagePCSystemBluetooth"
code on c++ can be look like:
if (0 <= CoInitialize(0))
{
IApplicationActivationManager* pAppAct;
if (0 <= CoCreateInstance(__uuidof(ApplicationActivationManager), 0, CLSCTX_ALL, IID_PPV_ARGS(&pAppAct)))
{
ULONG dwProcessId;
pAppAct->ActivateApplication(
L"windows.immersivecontrolpanel_cw5n1h2txyewy!microsoft.windows.immersivecontrolpanel",
L"page=SettingsPagePCSystemBluetooth",
AO_NONE ,
&dwProcessId);
pAppAct->Release();
}
CoUninitialize();
}
the processId (if all ok) reference to "X:\Windows\ImmersiveControlPanel\SystemSettings.exe" -ServerName:microsoft.windows.immersivecontrolpanel
for c# - look IApplicationActivationManager::ActivateApplication in C#?
the "windows.immersivecontrolpanel_cw5n1h2txyewy!microsoft.windows.immersivecontrolpanel" and "page=SettingsPagePCSystemBluetooth" strings not documented anywhere and I not sure are it is "persist" - but currently it used to open manage Bluetooth page in system settings. as is.
in case we run control.exe bthprops.cpl - the process ( control.exe ) launched without any error - as result you and not got any errors when you call this code.
then control.exe bthprops.cpl exec new process rundll32.exe Shell32.dll,Control_RunDLL bthprops.cpl, and exit
the rundll32.dll call Control_RunDLLW(HWND_DESKTOP, (HINSTANCE)&__ImageBase, L"bthprops.cpl", SW_SHOWDEFAULT);
we can and direct call void WINAPI Control_RunDLLW(HWND hwndParent, HINSTANCE hInst, PCWSTR cplName, int nCmdShow ); this api exported from shell32.dll
internally Control_RunDLLW load "bthprops.cpl" (3-rd argument - cplName), find CPlApplet entry point, and send CPL_INIT message. the bthprops.cpl on this message check are bthserv is running via OpenService(L"BTHSERV", ) + QueryServiceStatus (in function BthpIsbluetoothServiceRunning) and if "BTHSERV" not running - it return zero (fail code)
I have a Compact .Net 2.0 Application (Targeting Windows Mobile 2003 and Above) that has a button to Minimize the Form. I use P/Invoke to Minimize the form/application
//[DllImport("coredll.dll")]
ShowWindow(this.Handle /* Handle for the Form */, SW_MINIMIZED /*6*/)
I have another simple program Splash.exe written in C++ that starts the compact.net application. The purpose of the Splash program is to check if the .Net Application is running and either Restore the .Net Form if running or Start the .Net application if not.
I use the following procedure to check if the .Net application is running:
HWND GetProcessIfAlive(TCHAR szExeName[MAX_PATH]) /* Exe name of the .Net App */
{
HANDLE hSnapShot = NULL;
PROCESSENTRY32 pEntry = {0};
// Get the snapshot of the system
hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, NULL);
pEntry.dwSize = sizeof(pEntry);
//Get first process
Process32First(hSnapShot, &pEntry);
//Iterate thru all processes
while(TRUE)
{
TCHAR *strTempExe = pEntry.szExeFile;
if(_tcscmp(strTempExe, szExeName) == 0)
{
DWORD ProcesID = pEntry.th32ProcessID;
return (HWND)OpenProcess (PROCESS_ALL_ACCESS, FALSE, ProcesID);
//return (HWND)ProcesID;
}
if(!Process32Next (hSnapShot, &pEntry)/* == FALSE*/)
{
return NULL;
}
}
return NULL;
}
After returning from this procedure, I try to restore the Form using
HWND hExistingHandle = GetProcessIfAlive(TEXT("CompactDotNetApp.exe"));
if(hExistingHandle != NULL)
{
if(ShowWindow(hExistingHandle, SW_RESTORE) == FALSE)
{
DWORD err = GetLastError();
}
}
I am getting error 1400 ERROR_INVALID_WINDOW_HANDLE
How can I fix the code the get the proper window Handle for the running Compact .Net Application?
Thanks in Advance.
You're trying to use ShowWindow on a process handle, not a Window handle, which is invalid. You would need the handle to your main app window, which you could get with FindWindow calls, but it's a kludge.
A better approach here is a 2-part solution.
First use a named mutex. Create the mutex when the managed app starts and release it when you exit. The C app can then just check to see if the mutex exists to determine if the app is running or not - no toolhelp work needed.
The second part of the solution is to have your managed app launch a thread (or a timer) to periodically check for a named system event. If it finds that the event is set, it then maximizes the main form and brings it fore.
Back in the C app, if the mutex is found to exist (the app is running) then you simply set the event and exit. That will trigger the thread in the manged app to bring itself to the fore.
I have a situation where I'm starting a process in my code in order to set up an IPC channel. The process I'm starting is an MFC application with no CLR support. The application from which I am starting this process is a C# module in a WPF application (thought I don't think that that is consequential to my problem). This works with a version of the application that does support CLR, and it works on every computer except the deployment target, a touch screen computer with Windows 7. But for some reason, when I try it with this exact scenario, the Process object never resolves a main window handle (Process.MainWindowHandle). Is there another (perhaps even pinvoke) method of doing this? Is this a security thing? I'm the one staring the process. The process's main window handle does exist. I don't see what could be wrong.
If it helps, here is my code.
_applicationProcess = new Process();
_applicationProcess.StartInfo.FileName = _strProcessPath;
_applicationProcess.StartInfo.Arguments = _strProcessArguments;
_applicationProcess.Start();
long nTicks = Environment.TickCount;
if (_applicationProcess.WaitForInputIdle(1 /*minute(s)*/ * 60000))
{
try
{
do
{
// Don't let total processing take more than 1 minute(s).
if (Environment.TickCount > nTicks + 1 /*minute(s)*/ * 60000)
throw new ApplicationException("MFCApplication.Startup failed! The main window handle is zero!");
_applicationProcess.Refresh();
}
while (_applicationProcess.MainWindowHandle.ToInt32() == 0);
_applicationHandle = new IntPtr(_applicationProcess.MainWindowHandle.ToInt32());
}
catch (Exception ex)
{
//Do some stuff...
throw;
}
}
else
{
// Do exception handling.
}
The ApplicationException is hit after a minute of trying to get a main window handle other than zero.
The value you get out of Process.MainWindowHandle is unfortunately a guess. There is no API function available to a program that lets it tell Windows "this is my main window". The rule it uses is documented, it is the first window that's created by a process when it gets started. That causes trouble if that first window is, say, a login window or a splash screen.
Not much you can do about this, you have to know more about how the program behaves to find that real main window. Enumerating windows with EnumThreadWindows() could help you find it, as long as the first window was created on the same thread as the main window. A more elaborate EnumWindows() will be necessary if that is not the case.
My habit is to call EnumWindows in a loop combined with GetWindowThreadProcessId to find the window handle.
C Code, adapt to your language
DWORD TargetHWND;
//...
while (EnumWindows(EnumWndProc, (LPARAM)(DWORD)pid)) {
Sleep(100);
}
//...
BOOL EnumWndProc(HWND hWnd, LPARAM lParam) {
DWORD pid = (DWORD)-1;
GetWindowThreadProcessId(hWnd, &pid);
if (pid == (DWORD)lParam) {
TargetHWND = hWnd;
return FALSE;
}
return TRUE;
}
In order to get MainWindowHandle by means of your process, please make sure your WPF application is shown on the taskbar i.e ShowInTaskbar="True" and set Application.Current.MainWindow property to the window that you'd like to set as your main window.
If I execute code below in my WPF main window without setting ShowInTaskbar="True" I always got 0 as the MainWindowHandle because my WPF window was full screen and not displayed on the taskbar.
Application.Current.MainWindow = this;
var Query = System.Diagnostics.Process.GetProcessesByName("ProcessName");
if (Query.Any())
{
Query.FirstOrDefault().Refresh();
MessageBox.Show(Query.FirstOrDefault().MainWindowHandle.ToInt32().ToString());
}
I don't know why it could be different, but after you create the process, try doing:
Process[] allProcesses = Process.GetProcessesByName("YourWindowTitle");
and see if any of the processes returned have a MainWindowHandle.
Good day everyone.
This problem was part of another one which it as been solved, i realized that what i thought it was the problem after all, it wasn't. Still thanks to that I've learned a couple things.
My application does loads of work with IE and from time to time, IE is redirected to a website with some bad Javascript code that ends up blocking IE interface. And consequently blocking my application too once everything on my application is running on the same Thread.
To counteract that problem, at startup my application runs a static method in another Thread that every 15 seconds does a simple check if IE is responding or not, and if IE isn't responding, he closes all its process's, liberating the lock on my application main Thread and then my application can resume its work.
To find if IE process's are responding i had a simple code like this:
bool terminate = false;
foreach (System.Diagnostics.Process exe in System.Diagnostics.Process.GetProcesses())
{
if (exe.ProcessName.StartsWith("iexplore"))
{
if (exe.Responding == false)
{
terminate = true;
break;
}
}
}
// Code to close all IE process's...
In order to the Process.Responding property finds if the process is responding, and according to information on MSDN, this property needs another property named MainWindowHandle to be available in order to complete the process of checking. And if MainWindowHandle isn't available Process.Responding always returns true even if the process isn't responding.
And for some reason which i don't know. In Windows XP MainWindowHandle isn't available there so Responding isn't accurate.
Thats why i need to know another way to find if a specific process is responding or not in Windows XP.
Any help is appreciated, thanks.
PS: If your looking for a website to freeze IE here goes: http://aboutmycollege.com/
EDIT: Following 0xA3 suggestion:
I went through all IE process's checking if they had the MainWindowHandle property, those who had that property i send they Responding property to a MessageBox and they report correctly when IE isn't responding on Windows 7 but not on XP.
I executed this code every 15 seconds:
foreach (System.Diagnostics.Process exe in System.Diagnostics.Process.GetProcesses())
{
if (exe.ProcessName.StartsWith("iexplore"))
{
if (exe.MainWindowHandle == IntPtr.Zero)
{
System.Windows.Forms.MessageBox.Show("Process doesn't have MainWindowHandle");
}
else
{
System.Windows.Forms.MessageBox.Show("Process Responding: " + exe.Responding.ToString());
}
}
}
In Windows 7 and Xp he reports the Process's of IE that don't have the MainWindowHandle property, and in Windows 7 he also reports correctly when IE isn't responding. But in XP all IE process's with MainWindowHandle are always responding even when they aren't.
IE is special because each tab has its own process plus there is an additional parent IE process. In fact, only the parent process will have a valid MainWindowHandle.
Did you check whether MainWindowHandle is null for all these processes? If it isn't I think your code should work as expected on XP as well.
Update
Since checking all IE instances didn't help, the next thing I would try is to modify the timeout that is used by Process.Responding. The property internally calls the SendMessageTimeout api function and then checks the return value whether a timeout occurred. If so, the process is assumed to be hanging. The timeout is a hard-coded value of 5 seconds.
You can call SendMessageTimeout yourself using P/Invoke and vary the timeout. Possibly a shorter value would give better results on Windows XP:
[DllImport("user32.dll", CharSet=CharSet.Auto)]
static extern IntPtr SendMessageTimeout(
HandleRef hWnd,
int msg,
IntPtr wParam,
IntPtr lParam,
int flags,
int timeout,
out IntPtr pdwResult);
const int SMTO_ABORTIFHUNG = 2;
bool IsResponding(Process process)
{
HandeRef handleRef = new HandleRef(process, process.MainWindowHandle);
int timeout = 2000;
IntPtr lpdwResult;
IntPtr lResult = SendMessageTimeout(
handleRef,
0,
IntPtr.Zero,
IntPtr.Zero,
SMTO_ABORTIFHUNG,
timeout,
out lpdwResult);
return lResult != IntPtr.Zero;
}