Find control of specific class in another application - c#

I am scrapping content from another Windows application.
The application has a Listbox and two TRichEdit controls (and other controls without interest).
When using SendKeys.SendWait("{DOWN}") to the Listbox, the content in the two TRichEdit boxes changes. Thats where I want to scrap the content. That works.
RichEdit1 : No problem - I get the content using SendMessageW()
RichEdit2: Big problem. It changes Windows Handle each time I use SendKeys.SendWait on the LIstBox, so I can't access it.
The solution is to find the new Windows Handle for RichEdit2. I think that I can get a list of Handles for RichEdit control and select the one with Handle different from RichEdit1.
Question:
How can I get a list of Handles of a specific class (RichEdit) from a different windows forms application?
Or
Does anyone has a better solution?
A code snippet in C# will be appreciated.
Thanks in advance.

For the question on how to get the RichEdit window handles:
You can PInvoke FindWindowEx setting the child window parameter to NULL to check all child windows and the class name set to the class names of RichEdit control from here:
v1.0 = RICHEDIT
v2.0 & v3.0 = RichEdit20A or RichEdit20W
v4.1 = RICHEDIT50W
v5.0 = RichEdit50W
v6.0 = RichEdit60W
Still, MSDN states that:
The function searches among windows that are child windows of the desktop.
So basically you get a search depth of one. If your controls are nested deeper, then you may need to combine this with EnumChildWindows to perfrom a full depth search.
EDIT
This is a snippet on how to enumerate the windows and find matching windows for a given class using the described method, hope you can fine tune it.
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Collections.Concurrent;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace UIAutomation
{
class Program
{
public delegate bool EnumWindowsProc(IntPtr hwnd, IntPtr lParam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
public static bool EnumChildWindowsCallback(IntPtr hWnd, IntPtr lParam)
{
StringBuilder className = new StringBuilder(256);
GetClassName(hWnd, className, className.Capacity);
var windowInformation = new WindowInformation(hWnd, lParam, className.ToString());
_windowLookupMap[hWnd] = windowInformation;
if (lParam != IntPtr.Zero)
{
_windowLookupMap[lParam]._children.Add(windowInformation);
}
EnumChildWindows(hWnd, EnumChildWindowsCallback, hWnd);
return true;
}
class WindowInformation
{
public IntPtr _parent;
public IntPtr _hWnd;
public string _className;
public List<WindowInformation> _children = new List<WindowInformation>();
public WindowInformation(IntPtr hWnd, IntPtr parent, string className)
{
_hWnd = hWnd;
_parent = parent;
_className = className;
}
}
static Dictionary<IntPtr, WindowInformation> _windowLookupMap = new Dictionary<IntPtr, WindowInformation>();
static void FindWindowsByClass(string className, WindowInformation root, ref List<WindowInformation> matchingWindows)
{
if (root._className == className)
{
matchingWindows.Add(root);
}
foreach (var child in root._children)
{
FindWindowsByClass(className, child, ref matchingWindows);
}
}
static void Main(string[] args)
{
var processes = Process.GetProcessesByName("notepad");
StringBuilder className = new StringBuilder(256);
GetClassName(processes[0].MainWindowHandle, className, className.Capacity);
_windowLookupMap[processes[0].MainWindowHandle] = new WindowInformation(processes[0].MainWindowHandle, IntPtr.Zero, className.ToString());
EnumChildWindows(processes[0].MainWindowHandle, EnumChildWindowsCallback, processes[0].MainWindowHandle);
List<WindowInformation> matchingWindows = new List<WindowInformation>();
FindWindowsByClass("Edit", _windowLookupMap.Single(window => window.Value._parent == IntPtr.Zero).Value, ref matchingWindows);
Console.WriteLine("Found {0} matching window handles", matchingWindows.Count);
}
}
}

Thanks for the answer above. It's very detailed. I ended up using a more simple approach:
private IntPtr FindHandle()
{
while (true)
{
IntPtr handle = FindWindowEx(this.ApplicationHandle,IntPtr.Zero,"TRichEdit", null);
if (handle == null)
{
throw new Exception("No handle found");
}
if (handle != this.Handle_01)
{
return handle;
}
}
}

Related

How to get the "Application Name" from hWnd for Windows 10 Store Apps (e.g. Edge)

I'm trying to get an understandable "Process Name" for Windows 10 apps. Currently, all of them use ApplicationFrameHost, so I thought I could use either the ModelId or the PackageName, but it seems Windows 10 Store Apps (I tried with Mail, Store and Edge) won't work with the Package query API
Using kernel32.dll, GetApplicationUserModelId returns APPMODEL_ERROR_NO_APPLICATION and GetPackageId returns APPMODEL_ERROR_NO_PACKAGE.
How can I get an identifier for a Windows 10 Store App, so that I can uniquely identify, say, Edge but also any other Windows 10 Store Apps?
Update
I'm getting the process ID from the hWnd (the window handle), so I think my problem is actually how to get the "real" process ID from a window handle. From there, using those methods would probably work.
UWP apps are wrapped into an other app/process. If this has focus, then try and find the child UWP process.
You will need some P/Invoke methods. Take a look at this class, which provide all the code you need to do the job:
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
namespace Stackoverflow
{
internal struct WINDOWINFO
{
public uint ownerpid;
public uint childpid;
}
public class UwpUtils
{
#region User32
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", SetLastError = true)]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
// When you don't want the ProcessId, use this overload and pass IntPtr.Zero for the second parameter
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
/// <summary>
/// Delegate for the EnumChildWindows method
/// </summary>
/// <param name="hWnd">Window handle</param>
/// <param name="parameter">Caller-defined variable; we use it for a pointer to our list</param>
/// <returns>True to continue enumerating, false to bail.</returns>
public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);
[DllImport("user32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr hWndParent, EnumWindowProc lpEnumFunc, IntPtr lParam);
#endregion
#region Kernel32
public const UInt32 PROCESS_QUERY_INFORMATION = 0x400;
public const UInt32 PROCESS_VM_READ = 0x010;
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool QueryFullProcessImageName([In]IntPtr hProcess, [In]int dwFlags, [Out]StringBuilder lpExeName, ref int lpdwSize);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(
UInt32 dwDesiredAccess,
[MarshalAs(UnmanagedType.Bool)]
Boolean bInheritHandle,
Int32 dwProcessId
);
#endregion
public static string GetProcessName(IntPtr hWnd)
{
string processName = null;
hWnd = GetForegroundWindow();
if (hWnd == IntPtr.Zero)
return null;
uint pID;
GetWindowThreadProcessId(hWnd, out pID);
IntPtr proc;
if ((proc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, (int)pID)) == IntPtr.Zero)
return null;
int capacity = 2000;
StringBuilder sb = new StringBuilder(capacity);
QueryFullProcessImageName(proc, 0, sb, ref capacity);
processName = sb.ToString(0, capacity);
// UWP apps are wrapped in another app called, if this has focus then try and find the child UWP process
if (Path.GetFileName(processName).Equals("ApplicationFrameHost.exe"))
{
processName = UWP_AppName(hWnd, pID);
}
return processName;
}
#region Get UWP Application Name
/// <summary>
/// Find child process for uwp apps, edge, mail, etc.
/// </summary>
/// <param name="hWnd">hWnd</param>
/// <param name="pID">pID</param>
/// <returns>The application name of the UWP.</returns>
private static string UWP_AppName(IntPtr hWnd, uint pID)
{
WINDOWINFO windowinfo = new WINDOWINFO();
windowinfo.ownerpid = pID;
windowinfo.childpid = windowinfo.ownerpid;
IntPtr pWindowinfo = Marshal.AllocHGlobal(Marshal.SizeOf(windowinfo));
Marshal.StructureToPtr(windowinfo, pWindowinfo, false);
EnumWindowProc lpEnumFunc = new EnumWindowProc(EnumChildWindowsCallback);
EnumChildWindows(hWnd, lpEnumFunc, pWindowinfo);
windowinfo = (WINDOWINFO)Marshal.PtrToStructure(pWindowinfo, typeof(WINDOWINFO));
IntPtr proc;
if ((proc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, (int)windowinfo.childpid)) == IntPtr.Zero)
return null;
int capacity = 2000;
StringBuilder sb = new StringBuilder(capacity);
QueryFullProcessImageName(proc, 0, sb, ref capacity);
Marshal.FreeHGlobal(pWindowinfo);
return sb.ToString(0, capacity);
}
/// <summary>
/// Callback for enumerating the child windows.
/// </summary>
/// <param name="hWnd">hWnd</param>
/// <param name="lParam">lParam</param>
/// <returns>always <c>true</c>.</returns>
private static bool EnumChildWindowsCallback(IntPtr hWnd, IntPtr lParam)
{
WINDOWINFO info = (WINDOWINFO)Marshal.PtrToStructure(lParam, typeof(WINDOWINFO));
uint pID;
GetWindowThreadProcessId(hWnd, out pID);
if (pID != info.ownerpid)
info.childpid = pID;
Marshal.StructureToPtr(info, lParam, true);
return true;
}
#endregion
}
}
Now, get a handle to the current foreground window using another P/Invoke method
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
Use the return value and call the GetProcessName method from the code above. You should receive the correct name/path to the process.
Here is a simple Form to test the code:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using StackOverflow;
namespace Stackoverflow.Test
{
public partial class TestForm : Form
{
WinEventDelegate dele = null;
delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
[DllImport("user32.dll")]
static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
private const uint WINEVENT_OUTOFCONTEXT = 0;
private const uint EVENT_SYSTEM_FOREGROUND = 3;
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
public TestForm()
{
InitializeComponent();
dele = new WinEventDelegate(WinEventProc);
IntPtr m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, dele, 0, 0, WINEVENT_OUTOFCONTEXT);
}
public void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
textBox1.AppendText(GetActiveWindowTitle() + "\n");
}
private string GetActiveWindowTitle()
{
return UwpUtils.GetProcessName(GetForegroundWindow());
}
}
}
You can download the full code, including the example/test on GitHub.
You can use GetPackageId() and then PackageFullNameFromId().
E.g.:
HANDLE hProcess = OpenProcess(
PROCESS_QUERY_LIMITED_INFORMATION,
false,
pe32.th32ProcessID);
UINT32 bufferLength = 0;
LONG result = GetPackageId(hProcess, &bufferLength, nullptr);
BYTE* buffer = (PBYTE) malloc(bufferLength);
result = GetPackageId(hProcess, &bufferLength, buffer);
PACKAGE_ID* packageId = reinterpret_cast<PACKAGE_ID*>(buffer);
wprintf(L"Name: %s\n", packageId->name);
GetPackageFullName/FamilyName/Id(hprocess,...) etc return APPMODEL_ERROR_NO_PACKAGE if the process has no package identity. Ditto GetApplicationUserModelId(hprocess...) returns APPMODEL_ERROR_NO_APPLICATION because likewise the process has no application identity.
Sounds like you have an HWND for a process that does work on behalf of the application, but is not the application. This is quite common - RuntimeBroker and other processes run as 'Desktop apps' (i.e. process w/o package or application identity) as brokers to do things for application processes which they can't do for themselves.
To your original question, "I'm getting the process ID from the hWnd (the window handle), so I think my problem is actually how to get the "real" process ID from a window handle" this is a fundamentally flawed approach. You have a pid from an HWND, but if the process is a broker it can do work on behalf of multiple applications - the broker process has no identity; it knows *per request/WinRT API call/etc who its caller is and scopes its work to that identity. You can't discover that at the process level.
So first of all there is a thing called AppUserModelID, it's ID of window that is used by taskbar to group windows. Because all WinRT windows are from same process but they aren't grouped, it means that each app has own UserModelID.
To get UserModelID from HWND you can use method from this answer.
#include "Propsys.h"
#include <propkey.h>
#pragma comment (lib, "Shell32.lib")
//.........
IPropertyStore* propStore;
auto weatherWnd = FindWindow(L"ApplicationFrameWindow", L"Weather");
SHGetPropertyStoreForWindow(weatherWnd, IID_IPropertyStore, (void**)&propStore);
PROPVARIANT prop;
propStore->GetValue(PKEY_AppUserModel_ID, &prop);
And prop will contain value LPWSTR = 0x00838f68 L"Microsoft.BingWeather_8wekyb3d8bbwe!App". This is full entry point name in format <FullPackageFamilyName>!<EntryPoint>. Entry point for launched apps usually called App. Entry points are defined in app manifest.
Also interesting thing - child window that is owned by app is not destroyed, but is moved away from app frame host into desktop window. I don't know why it happens, but you must be careful because FindWindow(nullptr, L"Weather") returned child app window and not appframehost window.
P.S. AppUserModelID is just a string and it's format is not documented, so this method is not exactly the most reliable one.
P.P.S. Also I noticed that you want to have icon and name, you can use PackageManager for that, it requires you to reference winmd assembly, how to do this look here
Below is a similar one for getting the actual process name,
Name of process for active window in Windows 8/10
With Spy++ utility, confirmed that Windows.Core.UI.CoreWindow is a child window of Weather and it is the one that we are interested in. (Verified on Win10 10563)

FindWindowEx on child dialog window

I'm trying to get the handle of a child dialog window. I've tried using FindWindowEx, but it didn't work. Instead, FindWindow did work.
I did an experiment with visual studio's options window, with the following code:
IntPtr vsHandle = Process.GetProcessById(vsProcessId).MainWindowHandle; // consistent with spy++'s parent handle of options window
IntPtr optionsHandle = FindWindowEx(vsHandle, IntPtr.Zero, "#32770", "Options"); // returns 0
IntPtr optionsHandle2 = FindWindow("#32770", "Options"); // returns correct handle
From my understanding, FindWindowEx should've worked, it is a child window.
I'm running windows xp, and have also tried using FindWindowEx(vsHandle, IntPtr.Zero, "#32770", null). Didn't work. Seems like the only way to get it is using FindWindow which isn't good enough as two parent instances with the same dialog can be open.
This is the declaration:
[DllImport("user32.dll")]
Private static extern IntPtr FindWindow(string className, string windowTitle);
[DllImport("user32.dll")]
Private static extern IntPtr FindWindowEx(IntPtr parentHWnd, IntPtr childAfterHWnd, string className, string windowTitle);
Thanks in advance.
I found a solution to this. The reason FindWindowEx didn't work was because it only works on child windows that have WS_CHILD style, and apparently dialog windows do not have this style. It is why EnumChildWindows won't work either (I've tried).
So the ugly solution is EnumWindows combined with GetParent to compare the handle and the text.
struct SearchData
{
public string WindowText;
public IntPtr ParentHandle;
public IntPtr ResultHandle;
}
delegate bool EnumWindowsCallback(IntPtr currentWindowHandle, ref SearchData searchData);
[DllImport("user32.dll")] static extern bool EnumWindows(EnumWindowsCallback callback, ref SearchData searchData);
[DllImport("user32.dll")] static extern IntPtr GetParent(IntPtr childHandle);
[DllImport("user32.dll")] static extern void GetWindowText(IntPtr handle, StringBuilder resultWindowText, int maxTextCapacity);
static bool Callback(IntPtr currentWindowHandle, ref SearchData searchData)
{
bool continueEnumeration = true;
IntPtr currentWindowParentHandle = GetParent(currentWindowHandle);
if (currentWindowParentHandle == searchData.ParentHandle)
{
var windowText = new StringBuilder(1024);
GetWindowText(currentWindowHandle, windowText, windowText.Capacity);
if (windowText.ToString() == searchData.WindowText)
{
searchData.ResultHandle = currentWindowHandle;
continueEnumeration = false;
}
}
return continueEnumeration;
}
IntPtr GetChildWindowHandle(string windowText, IntPtr parentHandle)
{
var searchData = new SearchData{ParentHandle=parentHandle, WindowText=windowText};
EnumWindows(Callback, ref searchData);
return searchData.ResultHandle;
}

How to access a window?

I'm trying to access a specific window using its handle (that is System.IntPtr value):
// Getting the process of Visual Studio program
var process = Process.GetProcessesByName("devenv")[0];
// Showing the handle we've got, we've no problem
MessageBox.Show(this, process.MainWindowHandle.ToString());
// Attempting to get the main window object by its handle
var wnd = NativeWindow.FromHandle(process.MainWindowHandle);
// always fails
if (wnd == null)
MessageBox.Show("Failed");
else
MessageBox.Show(wnd.ToString(), "Yeeeeees!!");
I have tried also to access another demo .net winforms application's main window, that I have made for this purpose, (i.e. I run the demo application, and attempted to access its main window by this application), and failed, too, although both the demo and this application are .NET applications. However, this successes:
var process2 = Process.GetCurrentProcess();
MessageBox.Show(this, process2.MainWindowHandle.ToString());
var wnd2 = NativeWindow.FromHandle(process2.MainWindowHandle);
if (wnd2 == null)
MessageBox.Show("Failed");
else
MessageBox.Show(wnd2.ToString(), "Yes");
I think this works because it is invoked from the same application. So, how can I access some another program's window object by its handle?
I thought it can work using C\C++ by using header file <windows.h> and then using a P\invoke.
If I can't, is there another way to access a window (i.e. rather than using handles)?
===================
EDIT
I want to deal with the entire window object and its own controls
Then, as Raymond suggested, why don't you try with Automation? Add a console project with references to UIAutomationClient and UIAutomationTypes
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Automation;
using System.Diagnostics;
using System.Threading;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
var pInfo = new ProcessStartInfo("notepad");
var p = Process.Start(pInfo);
p.WaitForInputIdle();
AutomationElement installerEditorForm = AutomationElement.FromHandle(p.MainWindowHandle);
// menus
AutomationElementCollection menuBars = installerEditorForm.FindAll(TreeScope.Children, new PropertyCondition(
AutomationElement.ControlTypeProperty, ControlType.MenuBar));
var mainMenuItem = menuBars[0];
AutomationElementCollection menus = mainMenuItem.FindAll(TreeScope.Children, new PropertyCondition(
AutomationElement.ControlTypeProperty, ControlType.MenuItem));
var fileMenuItem = menus[0];
ExpandCollapsePattern fileMenuItemOpenPattern = (ExpandCollapsePattern)fileMenuItem.GetCurrentPattern(
ExpandCollapsePattern.Pattern);
fileMenuItemOpenPattern.Expand();
AutomationElement fileMenuItemNew = fileMenuItem.FindFirst(TreeScope.Children,
new AndCondition(
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.MenuItem),
new PropertyCondition(AutomationElement.NameProperty, "New")));
Console.Read();
}
}
}
reference
The documentation for NativeWindow.FromHandle explains why that function always returns null for you:
The handle must already be owned by another NativeWindow in the
current process; otherwise, null is returned.
But the window that you are targeting is in a different process. So you simply cannot use NativeWindow here. You will have to make do with the window handle as an IntPtr.
In your edit you state:
I want to deal with the entire window object and its own controls
That changes nothing. You can't use NativeWindow. You will have to deal with the raw Win32 API.
What do you want to access? You can get the title and text of windows in Windows. But you cant get a NativeWindow object of another application. You need to use the windows API to interact with other applications. I once hijacked an object in another app, but did so by knowing its class and discovering a hack to find its Idispatch pointer, you can look into it here. Below is how to get the title of the foreground window, hope this helps.
using System.Runtime.InteropServices;
using System.Text;
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
private string GetActiveWindowTitle()
{
const int nChars = 256;
IntPtr handle = IntPtr.Zero;
StringBuilder Buff = new StringBuilder(nChars);
handle = GetForegroundWindow();
if (GetWindowText(handle, Buff, nChars) > 0)
{
return Buff.ToString();
}
return null;
}
Thought I might add, if you're trying to subclass a window of another application, you should take a look at my link above. I believe the only way to do it is using DLL injection and windows hooks, exemplified in my example here.
Didn't get what you're really trying to do but maybe if you try...
public class ApiUtils
{
[DllImport("user32")]
public static extern int SetForegroundWindow(IntPtr hwnd);
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommand nCmdShow);
[DllImport("user32.dll")]
public static extern int GetForegroundWindow();
public static void ActiveWindow(IntPtr hwnd)
{
if ((IntPtr)GetForegroundWindow() != hwnd)
{
ShowWindow(hwnd, ShowWindowCommand.ShowMaximized);
}
}
}
now calling it...
Process p = Process.Start(new ProcessStartInfo() { FileName = "someApp.exe"});
ApiUtils.ShowWindow(p.MainWindowHandle, WindowShowStyle.ShowNormal);
If not sorry, didn't get the question really well.

How to get the name of an External window in C# Application?

i've developed a simple application (.dll) in LABVIEW and i implorted that dll to a C# windows application(Winforms) . Like
[DllImport(#".\sample.dll")]
public static extern void MyFunc(char[] a, StringBuilder b ,Int32 c);
so when i call the function MyFunc a window will be popped up( the Lab View window( Front panel of my labview application
i need to get the window name (ExpectedFuncName) in my C# application. i.e i need to get the name of the external window which is opend by my C# application. Can we use FileVersionInfo or assembly loader to get the name?
Is there any idea to do this?
Thanks in advance.
If you have the window handle, this is relatively easy:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
static extern int GetWindowTextLength(IntPtr hWnd);
...
int len;
// Window caption
if ((len = GetWindowTextLength(WindowHandle)) > 0) {
sb = new StringBuilder(len + 1);
if (GetWindowText(WindowHandle, sb, sb.Capacity) == 0)
throw new Exception(String.Format("unable to obtain window caption, error code {0}", Marshal.GetLastWin32Error()));
Caption = sb.ToString();
}
Here, 'WindowHandle' is the handle of the created window.
In the case you do not have a window handle (I see you don't), you have to enumerate every desktop top-level window, filter them by the creating process (I see the window is created by you application by calling MyFunc, so you know the process ID [*]), and then use some heuristic to determine the required information.
Here is the C# import of the functions you shall use in the case you do not have the handle:
[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
Basically EnumWindows calls EnumWindowsProc for each window found in the current desktop. So you can get the window caption.
List<string> WindowLabels = new List<string>();
string GetWindowCaption(IntPtr hWnd) { ... }
bool MyEnumWindowsProc(IntPtr hWnd, IntPtr lParam) {
int pid;
GetWindowThreadProcessId(hWnd, out pid);
if (pid == Process.GetCurrentProcess().Id) {
// Window created by this process -- Starts heuristic
string caption = GetWindowCaption(hWnd);
if (caption != "MyKnownMainWindowCaption") {
WindowLabels.Add(caption);
}
}
return (true);
}
void DetectWindowCaptions() {
EnumWindows(MyEnumWindowsProc, IntPtr.Zero);
foreach (string s in WindowLabels) {
Console.WriteLine(s);
}
}
[*] In the case the window is not created by your application (i.e but from another background process), you shall filter the values returned by GetWindowThreadProcessId using another process ID, but this requires another question...
If you activate LabVIEW scripting (LabVIEW 2010), or install it (LV 8.6, 2009) there is a front-panel property called 'FP.nativewindow'. This returns a handle to the front panel window.
Use the following snippet to get the property:

Get number of apps in TaskBar

I've been wondering how to do this for ages. I'm creating a little app, and I need to figure out how many apps or windows are displayed in the TaskBar.
I've yet to find any info on this at all, I'd appreciate any help at all.
Thank you :)
Here is an article that shows how to get the windows, that are shown when you are using the ALT+TAB key combination.
Basically, you will get the same windows that are shown in the taskbar (unless it is a tool window that is not displayed), but then again, you can always check against WS_EX_TOOLWINDOW (not shown) and WS_EX_APPWINDOW (shown).
You may have a look at my previous answer here; the main difference here is that you just have to count the windows that match the given requirements.
As other's have said you need to enumerate through the windows using the Win32 EnumWindows function, and get your count that way.
You can also enumerate through processes using Process.GetProcesses(); However windows like explorer windows which are not a separate process will not show up in that list.
int appCount = 0;
public bool EnumerateWindows(IntPtr hwnd, IntPtr lParam)
{
if (IsWindowVisible(hwnd))
{
StringBuilder sb = new StringBuilder();
string text = "";
GetWindowText(hwnd, sb, 1024);
text = sb.ToString();
if (text != string.Empty && text != "Program Manager")
{
appCount++;
}
}
return true;
}
private int GetAppCount()
{
appCount = 0;
EnumWindows(EnumerateWindows, new IntPtr(0));
return appCount;
}
internal delegate bool EnumThreadWindowsCallback(IntPtr hwnd, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool EnumWindows(EnumThreadWindowsCallback callback, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern bool IsWindowVisible(IntPtr hwnd);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern int GetWindowText(IntPtr hWnd, [Out, MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpString, int nMaxCount);
As far as I know there is no managed way of accessing the taskbar. Here is a link that describes how to access the taskbar by the Windows API. However, I a quick scan did not show any "number of items" or something similar. Still it might point you in the right direction.

Categories

Resources