-1

I'm trying to get certain window handle. I was searching for solution for many hours and I understand that my question sounds similar to this one: FindWindow() doesn't find my window [C++] But that discussion didn't help.

I was trying to use both FindWindow() and FindWindowEx() like these two:

IntPtr SysPropWndHandler = FindWindow("#32770", "Параметри продуктивності");

IntPtr SysPropWndHandler = FindWindowEx(IntPtr.Zero, IntPtr.Zero, "#32770", "Параметри продуктивності");

Weird part is that when I run the program, it starts new process for certain system settings program from system32 folder and it can't find it's handle during same launch time (if that's correct to say so). I tried to pause it to give it time to create window and assign handle, but that doesn't help. But! If that system program is launched first and then I run my program - it finds it's handle right away. Two ways for that "external launch":

  1. I run system program manually before launching my program
  2. I run my program, which launches that system program, then I close my program, system program doesn't close then. After that I run my program again.

But what I'm actually trying to make my program do is this:

  1. launch system program (some productivity settings)
  2. hide window
  3. change some settings via WinApi (kind of checkbox clicking emulation)
  4. click ok
  5. close it

Since my code works, at least in some conditions, looks like it has nothing to do with encoding, which was mansioned in that similar question. Otherwise it wouldn't work at all.

I was trying to launch it hidden, but it didn't work. I tried the same code for notepad for debugging it - it works.

string prog_path = @"C:\Windows\System32\SystemPropertiesPerformance.exe";

Process process = new Process();
process.StartInfo.FileName = prog_path;
process.StartInfo.CreateNoWindow = true; // no need for that, but I tried with it and without it just in case it works
process.StartInfo.UseShellExecute = true;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.Start();

According to Microsoft documentation you need to set UseShellExecute to true in order to use StartInfo.WindowStyle = ProcessWindowStyle.Hidden (which I did), but program still can choose to ignore that. Looks like that's exactly what's happening there. But I tried to get exect window handle via Spy++ and try to hide it - it works, so I can manipulate it from there and do my thing. The only problem is to find it's handle...

How do I find that handle in this case?

P.S.

  • Windows 10 x64 Pro Ukrainian (for other languages that window title in the code won't work)
  • .NET Framework 4.7.2
  • Code is inside .NET Framework Class Library, which is launched from C# Console Application.
  • 1
    Try with `process.WaitForInputIdle();` after `process.Start();`. Then call `IntPtr handle = FindWindow(null, "[The Windows Title in your language]");`. I don't think you can't hide that window. Remember to `Dispose()` of the process. – Jimi Dec 31 '18 at 04:49
  • Also, I think (not sure here) that you should check whether that process is already active before running it. If it's already running and you run it again, you app will probably crash, unless you `try/catch` the `process.Start();`. You can use `Process.GetProcessesByName("SystemPropertiesPerformance").First();` to see if it's active. – Jimi Dec 31 '18 at 04:53
  • Is the window hidden or not after being launched by your program? – kennyzx Dec 31 '18 at 04:54
  • Where with `First()` I mean `FirstOrDefault()` of course :) – Jimi Dec 31 '18 at 05:27
  • Jimi, thank you so much. ) I didn't check other answers, but simple line of `process.WaitForInputIdle();` after `process.Start();` worked for me. I think you should add it as an answer instead of a comment. – Constantine Ketskalo Jan 03 '19 at 21:40
  • kennyzx, no, the window is not hidden after it's launched by my program. It just ignores that command, as I wrote in the question (in the end, before 'P.S.`). So if I want to hide it I'm forced to search for it's handle and then hide it. – Constantine Ketskalo Jan 03 '19 at 21:42

2 Answers2

0

For me this one works fine(on Windows 7):

using System;
using System.Diagnostics;
using System.Text;
using System.Runtime.InteropServices;

namespace findwindow
{
    class Program
    {
        [DllImport("user32.dll", SetLastError = true)]
        static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
        public static void Main(string[] args)
        {
            Process.Start(new ProcessStartInfo(){FileName=@"C:\Windows\System32\SystemPropertiesPerformance.exe"});
            System.Threading.Thread.Sleep(100);
            IntPtr hwnd = FindWindow("#32770", "Параметры быстродействия");
            var sb = new StringBuilder(50);
            GetWindowText(hwnd, sb, 49);
            Console.WriteLine("hwnd:"+hwnd+", title:"+sb);
            Console.ReadKey(true);
        }
    }
}

Outputs:

hwnd:5636204, title:Параметры быстродействия

Try with that code your title, and say if it works.

Also there is a different approach like in this answer.

BladeMight
  • 2,670
  • 2
  • 21
  • 35
  • Adding some sleep might be good idea, but it depends a lot on PC productivity, how long it takes to accomplish the task (how powerfull PC is and how loaded with other tasks it is). So I think it would be not universal idea, sutable for any PC and any situation. But thanks for the answer anyway. ) – Constantine Ketskalo Jan 03 '19 at 22:03
0

Another approach is to use the UI Automation technology that's built in Windows. For example, this sample Console app should work. And because it's event-based, it does not need to use timers which can be context-dependent:

public static void Main(string[] args)
{
    Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent, AutomationElement.RootElement, TreeScope.Children, (sender, e) =>
    {
        var element = sender as AutomationElement;
        if (element.Current.Name == "Параметры быстродействия")
        {
            Console.WriteLine("hwnd:" + element.Current.NativeWindowHandle);
        }
    });

    Process.Start("SystemPropertiesPerformance.exe");
    Console.ReadLine(); // wait ...
    Automation.RemoveAllEventHandlers(); // cleanup
}

It works fine on my Windows 10 x64 machine. If this doesn't work, make sure your program and SystemPropertiesPerformance.exe run at the same UAC level.

Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
  • Probably that would work, but the downside of this solution is that if user desides to launch same window - it will be hidden as well. Most likely user is not going to launch it anyway, but still can be considered as a bug, so I would tend to avoid that. And yes, I also thought about timers as not the best idea. Jimmi's suggestion in comments to question worked best for me, but thanks for answer anyway. If I'll need your approach another time - I know where to find it now. ;) – Constantine Ketskalo Jan 03 '19 at 22:10