I cannot seem to find a way to determine whether a Process has a user interface e.g. a window, which is visible to the user?
Environment.UserInteractive is not useful for external processes
process.MainWindowHandle != IntPtr.Zero appears to always return false in my tests?
I would like to differentiate between say Notepad and conhost
Find out the process ID from your Process instance.
Enumerate the top-level windows with EnumWindows.
Call GetWindowThreadProcessId and see if it matches the target PID.
Call IsWindowVisible and/or IsIconic to test if that window is visible to the user.
The MSDN article about System.Diagnostics.Process.MainWindowHandle states the following
If you have just started a process and want to use its main window handle, consider using the WaitForInputIdle method to allow the process to finish starting, ensuring that the main window handle has been created. Otherwise, an exception will be thrown.
What they are implying is that the Window might take several seconds to render after you've made the call for the MainWindowHandle, returning IntPtr.Zero even though you can clearly see a Window is shown.
See https://msdn.microsoft.com/en-us/library/system.diagnostics.process.mainwindowhandle(v=vs.110).aspx for reference
Following #David Heffernan, this is what I did:
HWND FindTopWindow(DWORD pid)
{
std::pair<HWND, DWORD> params = { 0, pid };
// Enumerate the windows using a lambda to process each window
BOOL bResult = EnumWindows([](HWND hwnd, LPARAM lParam) -> BOOL
{
auto pParams = (std::pair<HWND, DWORD>*)(lParam);
DWORD processId;
if (GetWindowThreadProcessId(hwnd, &processId) && processId == pParams->second)
{
if (IsWindowVisible(hwnd)) {
// Stop enumerating
SetLastError(-1);
pParams->first = hwnd;
return FALSE;
}
return TRUE;
}
// Continue enumerating
return TRUE;
}, (LPARAM)¶ms);
if (!bResult && GetLastError() == -1 && params.first)
{
return params.first;
}
return 0;
}
Related
I have a console program, not that complicated but at the same time's not a hello world one. I have just two projects, first one has several classes. It is not multithreaded, and is all about calling restful APIs etc.
The thing is this: I am trying to make my application check if it runs twice (it is very important). I thought that it would be somehow feasible, but turns out to be extremely complicated.
Unfortunately, the only example I found, doesn't shed any light on how to use this technology. Below the code, which, is quite complicated for me, and I don't know how to plug it in my Main. Hence, the question.
Below the code (is not mine, I found it here: https://www.codeproject.com/Articles/3014/Single-Process-Instance-Object)
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Reflection;
namespace SpecialServices
{
//SingleProgamInstance uses a mutex synchronization
//object to ensure that only one copy of process is running
//at a particular time. It also allows for UI identification
// of the intial process by bringing that window to the foreground.
public class SingleProgramInstance : IDisposable
{
//Win32 API calls necesary to raise an unowned processs main window
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool ShowWindowAsync(IntPtr hWnd,int nCmdShow);
[DllImport("user32.dll")]
private static extern bool IsIconic(IntPtr hWnd);
private const int SW_RESTORE = 9;
//private members
private Mutex _processSync;
private bool _owned = false;
public SingleProgramInstance()
{
//Initialize a named mutex and attempt to
// get ownership immediately
_processSync = new Mutex(
true, // desire intial ownership
Assembly.GetExecutingAssembly().GetName().Name,
out _owned);
}
public SingleProgramInstance(string identifier)
{
//Initialize a named mutex and attempt to
// get ownership immediately.
//Use an addtional identifier to lower
// our chances of another process creating
// a mutex with the same name.
_processSync = new Mutex(
true, // desire intial ownership
Assembly.GetExecutingAssembly().GetName().Name + identifier,
out _owned);
}
~SingleProgramInstance()
{
//Release mutex (if necessary)
//This should have been accomplished using Dispose()
Release();
}
public bool IsSingleInstance
{
//If we don't own the mutex than
// we are not the first instance.
get {return _owned;}
}
public void RaiseOtherProcess()
{
Process proc = Process.GetCurrentProcess();
// Using Process.ProcessName does not function properly when
// the actual name exceeds 15 characters. Using the assembly
// name takes care of this quirk and is more accruate than
// other work arounds.
string assemblyName =
Assembly.GetExecutingAssembly().GetName().Name;
foreach (Process otherProc in
Process.GetProcessesByName(assemblyName))
{
//ignore "this" process
if (proc.Id != otherProc.Id)
{
// Found a "same named process".
// Assume it is the one we want brought to the foreground.
// Use the Win32 API to bring it to the foreground.
IntPtr hWnd = otherProc.MainWindowHandle;
if (IsIconic(hWnd))
{
ShowWindowAsync(hWnd,SW_RESTORE);
}
SetForegroundWindow(hWnd);
break;
}
}
}
private void Release()
{
if (_owned)
{
//If we own the mutex than release it so that
// other "same" processes can now start.
_processSync.ReleaseMutex();
_owned = false;
}
}
#region Implementation of IDisposable
public void Dispose()
{
//release mutex (if necessary) and notify
// the garbage collector to ignore the destructor
Release();
GC.SuppressFinalize(this);
}
#endregion
}
}
Any help using the above would be GREATLY appreciated; thank you so much.
how to use the sample code.
take the sample code (its one file) and add it to your project (as a separate .cs file)
now at the startup of your program main add
using(var spi = new SpecialServices.SingleProgramInstance("x5k6yz"))
{
if (!spi.IsSingleInstance){
Console.WriteLine("another copy is running");
return;
}
}
caveat, I have not tried the code from the sample, I assume it works.
EDIT. Ok tested, it works fine
Try this:
private void CloseDuplicateApplications()
{
string ProgramTitle = System.Diagnostics.Process.GetCurrentProcess().MainWindowTitle;
System.Diagnostics.Process[] Processes = System.Diagnostics.Process.GetProcesses();
for (int i = 0; i < Processes.Length; i++)
{
if (Processes[i].MainWindowTitle == ProgramTitle)
{
Processes[i].CloseMainWindow();
}
}
}
Currently the procedure simply closes any duplicate programs, however if you need to kill the duplicate program, replace this line:
Processes[i].CloseMainWindow();
With:
Processes[i].Kill();
I've been working for a few days on this and would like if nothing more some confirmation or a few hints as to where to go next on this task.
I've been tasked with hiding certain notifications within Chrome that Chrome doesn't provide a native option to hide - even in kiosk or incognito mode. The approach I have been using is Microsoft's Automation API to get access to these objects, which is relatively easy because I can find them by classname or by some of the text contained within the object - which is necessary. The challenge now is that I need to hide the element and/or it's container without closing Chrome :)
Easy enough to get the main handle to Chrome:
private IntPtr GetChromeHandle()
{
IntPtr chWnd = IntPtr.Zero;
Process[] procsChrome = Process.GetProcessesByName("chrome");
foreach (Process chrome in procsChrome)
{
// the chrome process must have a window
if (chrome.MainWindowHandle == IntPtr.Zero)
{
continue;
}
else
{
chWnd = chrome.MainWindowHandle;
}
}
return chWnd;
}
I can get one of these elements pretty easily from there by doing the following:
PropertyCondition pcFullScreen = new PropertyCondition(AutomationElement.NameProperty, "F11", PropertyConditionFlags.IgnoreCase);
AutomationElement fsTest = chromeWindow.FindFirst(TreeScope.Descendants, pcFullScreen);
The challenge here is how to close that element or navigate to a high level that I can close it?
Alternate approach tried looks like this:
PropertyCondition pcTest = new PropertyCondition(AutomationElement.ClassNameProperty, "Intermediate D3D Window");
AutomationElement newTestElm = chromeWindow.FindFirst(TreeScope.Descendants, pcTest);
Problem here is that while I can "hide/close" by using the handle of the class, I can't seem to narrow it down to an instance of the class that contains the text I'm looking for. Any suggestions would be greatly appreciated.
Per a comment, tried to access via WindowPattern and get "null" based on this code:
private WindowPattern GetWindowPattern(AutomationElement targetControl)
{
WindowPattern windowPattern = null;
try
{
windowPattern =
targetControl.GetCurrentPattern(WindowPattern.Pattern)
as WindowPattern;
}
catch (InvalidOperationException)
{
// object doesn't support the WindowPattern control pattern
return null;
}
// Make sure the element is usable.
if (false == windowPattern.WaitForInputIdle(10000))
{
// Object not responding in a timely manner
return null;
}
return windowPattern;
}
I'm trying to automate a sub window (Dialog with ClassName is #32770) by using UI Automation C# API.
The target sub window shows up in Spy++ and Inspect.exe, but doesn't show in VisualUIAVerifyNative. It also could not be accessed by either AutomationElement.FindFirst or TreeWalker navigation.
I checked the tree in VisualUIAVerifyNative, found it was quite different from the tree in Spy++ and Inspect.exe.
Interesting things:
My C# code seems behave exactly like VisualUIAVerifyNative.
I'm able to use AutomationElement.FromHandle to bind the HWND of the Dialog
Do you know why this happens?
Here's the detail of The Dialog from Insepct.exe
How found: Selected from tree...
Name: "V6"
ControlType: UIA_PaneControlTypeId (0xC371)
LocalizedControlType: "Dialog"
BoundingRectangle: {l:-47997 t:-47997 r:-46107 b:-47553}
IsEnabled: true
HasKeyboardFocus: false
ProcessId: 15496
RuntimeId: [2A.140BD8]
FrameworkId: "Win32"
ClassName: "#32770"
NativeWindowHandle: 0x140BD8
IsControlElement: false
IsContentElement: false
ProviderDescription: "[pid:10556,hwnd:0x140BD8 Main:Microsoft: Container Proxy (unmanaged:uiautomationcore.dll); Hwnd(parent link):Microsoft: HWND Proxy (unmanaged:uiautomationcore.dll)]"
LegacyIAccessible.ChildId: 0
LegacyIAccessible.Name: "V6"
LegacyIAccessible.Role: Dialog (0x12)
LegacyIAccessible.State: (0x100000)
IsAnnotationPatternAvailable: false
IsDragPatternAvailable: false
IsDockPatternAvailable: false
IsDropTargetPatternAvailable: false
IsExpandCollapsePatternAvailable: false
IsGridItemPatternAvailable: false
IsGridPatternAvailable: false
IsInvokePatternAvailable: false
IsItemContainerPatternAvailable: false
IsLegacyIAccessiblePatternAvailable: true
IsMultipleViewPatternAvailable: false
IsObjectModelPatternAvailable: false
IsRangeValuePatternAvailable: false
IsScrollItemPatternAvailable: false
IsScrollPatternAvailable: false
IsSelectionItemPatternAvailable: false
IsSelectionPatternAvailable: false
IsSpreadsheetItemPatternAvailable: false
IsSpreadsheetPatternAvailable: false
IsStylesPatternAvailable: false
IsSynchronizedInputPatternAvailable: false
IsTableItemPatternAvailable: false
IsTablePatternAvailable: false
IsTextChildPatternAvailable: false
IsTextEditPatternAvailable: false
IsTextPatternAvailable: false
IsTextPattern2Available: false
IsTogglePatternAvailable: false
IsTransformPatternAvailable: false
IsTransform2PatternAvailable: false
IsValuePatternAvailable: false
IsVirtualizedItemPatternAvailable: false
IsWindowPatternAvailable: false
...
Just found this https://stackoverflow.com/a/14187618/1633272.
I tried the TreeWalker.RawViewWalker, it worked. However TreeWalker.ControlViewWalker didn't.
It's a known difference (.NET specific bug) between native COM API and managed API. Here is an example how to use UIA through COM objects for most popular languages including C#.
Today, I solved a similar issue I had last year which I didn't have time to revisit until now.
My problem was, the modal dialog of an application can't be found with UIAutomation interface (I tried native COM calls in C, and other methods with C# with all possible methods of UIA, to no success), and inspect.exe showed it quite easily, very similar to yours, maybe same.
So, when I tried finding the same dialog with FindWindowEx method, it worked quite well. One problem that surprised me was, the dialog was shown under it's application's main window in Inspect's tree hierarchy, but FindWindowEx was only successful when I searched for the dialog as desktop child - which occurs for some cases I believe, for example if you don't properly set the ShowDialog() receiver's parent, it is attached to Desktop.
This might answer some other questions I had about inspect.exe's behaviour. Speculating about it, I think it might be using winapi calls to find windows, and generating a tree hierarchy that's slightly different than the actual, thus leading us poor users baffled for a long time. Bad design.
And here is some code, if you still want to try it after a year, or to help someone else. To find a window under desktop:
First define this:
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);
Then call:
IntPtr w = FindWindowEx(IntPtr.Zero, IntPtr.Zero, (string)null, "V6 - or your dialog's name");
or to find it as child of a top level window (find it's handle from IUIAutomation.CurrentNativeWindowHandle, let's say set to var handle):
IntPtr w = FindWindowEx(handle, IntPtr.Zero, (string)null, "V6 - or your dialog's name");
Then you can try the w with IUIAutomation.ElementFromHandle().
Although question asked is older but I had similar issue and solved it as below.
As IsControlElement property of the dialog is false so you could have access this dialog using RawViewWalker. Here is the sample.
IEnumerable<AutomationElement> controlElements = GetChildren(parentAutomationElement);
AutomationElement automationElement = null;
foreach (var element in controlElements)
{
if (element.Current.ClassName == "#32770")
{
automationElement = element;
break;
}
}`
private List<AutomationElement> GetChildren(AutomationElement parent)
{
if (parent == null)
{
// null parameter
throw new ArgumentException();
}
IEnumerable<AutomationElement> collection = parent.FindInRawView();
if (collection != null)
{
List<AutomationElement> result = new List<AutomationElement>(collection.Cast<AutomationElement>());
return result;
}
else
{
// some error occured
return null;
}
}
public static IEnumerable<AutomationElement> FindInRawView(this AutomationElement root)
{
TreeWalker rawViewWalker = TreeWalker.RawViewWalker;
Queue<AutomationElement> queue = new Queue<AutomationElement>();
queue.Enqueue(root);
while (queue.Count > 0)
{
var element = queue.Dequeue();
yield return element;
var sibling = rawViewWalker.GetNextSibling(element);
if (sibling != null)
{
queue.Enqueue(sibling);
}
var child = rawViewWalker.GetFirstChild(element);
if (child != null)
{
queue.Enqueue(child);
}
}
}
Make sure to put the FindInRawView Extension Method in a static class, else it will show error.
I am trying to call Advapi32 GetThreadWaitChain (WCT) function from my c# code, I've struggled previously on similar issue:
WCT GetThreadWaitChain call allways return false
However now I am trying to make this call as async call with callback, as documented here:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms681421(v=vs.85).aspx
That's my finction:
internal void CollectWaitAsyncInformation(ClrThread thread)
{
//Currenlty not working
var handle = Advapi32.OpenThreadWaitChainSession(WCT_SESSION_OPEN_FLAGS.WCT_ASYNC_OPEN_FLAG, AppCallback);
uint threadID = thread.OSThreadId;
WAITCHAIN_NODE_INFO[] NodeInfoArray = new WAITCHAIN_NODE_INFO[WctApiConst.WCT_MAX_NODE_COUNT];
int isCycle = 0;
int Count = WctApiConst.WCT_MAX_NODE_COUNT;
_eventHandler = Kernel32.CreateEvent(IntPtr.Zero, true, true, "MyEvent");
//This is where the applciation hangs
bool waitChainResult = Advapi32.GetThreadWaitChain(handle,
_eventHandler, 0 ,
threadID, ref Count, NodeInfoArray, out isCycle);
CheckCount(ref Count);
if (!waitChainResult)
{
var lastErrorCode = WinApi.Advapi32.GetLastError();
if (lastErrorCode == (uint)SYSTEM_ERROR_CODES.ERROR_IO_PENDING)
{
// Wait for callback to run...
WinApi.Kernel32.WaitForSingleObject(_eventHandler, int.MaxValue);
}
Kernel32.WaitForSingleObject(_eventHandler, uint.MaxValue);
}
}
link:
https://github.com/Pavel-Durov/Multithreading-Debugging-Asignments/blob/master/Assignments/Assignments.Core/Handlers/WCT/WctApiHandler.cs
My process hangs as I am calling Advapi32.GetThreadWaitChain function without any error or such.
Last time the problem was with the WAITCHAIN_NODE_INFO struct layout, however here I am using the same struct so I don't know if it is an issue, but if I pass IntPtr.Zero instead on NodeInfo array I get false as a result but GetLastError returns me 0.
Any help will be appreciated :).
Here is the link for my whole GitHub project:
https://github.com/Pavel-Durov/Multithreading-Debugging-Asignments
I am trying to prevent the user from pinning my .NET app to the taskbar. I've found some code on the Old New Thing that does just that. However, it is in C++.
#include <shellapi.h>
#include <propsys.h>
#include <propkey.h>
HRESULT MarkWindowAsUnpinnable(HWND hwnd)
{
IPropertyStore *pps;
HRESULT hr = SHGetPropertyStoreForWindow(hwnd, IID_PPV_ARGS(&pps));
if (SUCCEEDED(hr)) {
PROPVARIANT var;
var.vt = VT_BOOL;
var.boolVal = VARIANT_TRUE;
hr = pps->SetValue(PKEY_AppUserModel_PreventPinning, var);
pps->Release();
}
return hr;
}
BOOL
OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
{
MarkWindowAsUnpinnable(hwnd);
return TRUE;
}
I am having very little luck converting it to c#. Can someone help?
You can download the Windows API Code Pack which has the necessary p/invoke calls you need to translate the code in your post to C#.
Either use the library in whole or find the specific calls and definitions you require (search it for SHGetPropertyStoreForWindow and then its other dependencies).
In the question, the Old New Thing post also talks about how you can set some registry settings on per application basis that will so prevent the pinning an application to the taskbar.
All you have to do is add the value of "NoStartPage" to a key for your application under the Root\Applications. The value can be blank and of any type, if Windows just sees it is there it will not show the ability to pin the app, when the user right clicks on it in the taskbar.
Here is the documentation from Microsoft on this feature: Use Registry to prevent pinning of an application
The one caveat to this is that in Windows 7, due to UAC, you have to run as administrator to update the registry. I did this via the app.manifest.
The code to find the right and update the correct registry keys is below (hopefully it is not too verbose):
public static void Main(string[] args)
{
// Get Root
var root = Registry.ClassesRoot;
// Get the Applications key
var applicationsSubKey = root.OpenSubKey("Applications", true);
if (applicationsSubKey != null)
{
bool updateNoStartPageKey = false;
// Check to see if your application already has a key created in the Applications key
var appNameSubKey = applicationsSubKey.OpenSubKey("MyAppName.exe", true);
if (appNameSubKey != null)
{
// Check to see if the NoStartPage value has already been created
if (!appNameSubKey.GetValueNames().Contains("NoStartPage"))
{
updateNoStartPageKey = true;
}
}
else
{
// create key for your application in the Applications key under Root
appNameSubKey = applicationsSubKey.CreateSubKey("MyAppName.exe", RegistryKeyPermissionCheck.Default);
if (appNameSubKey != null)
{
updateNoStartPageKey = true;
}
}
if (updateNoStartPageKey)
{
// Create/update the value for NoStartPage so Windows will prevent the app from being pinned.
appNameSubKey.SetValue("NoStartPage", string.Empty, RegistryValueKind.String);
}
}
}
Using the WindowsAPICodePack (via NuGet) you need code resembling:
// Ensure the handle is available
new WindowInteropHelper(window).EnsureHandle();
// Prevent the window from being pinned to the task bars
var preventPinningProperty = new PropertyKey(
new Guid("9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3"), 9);
WindowProperties.SetWindowProperty(window, preventPinningProperty, "1");