How to find a desktop window (by window name) in Windows 8.1 Update 2 OS, using the Win32 API FindWindow() in PowerShell environment?

How to find a desktop window (by window name) in Windows 8.1 Update 2 OS, using the Win32 API FindWindow() in PowerShell environment?

I don’t remember having any problem finding a window in older Windows OS’s, but, I’m not succeeding in Windows 8.1 Update 2 OS, using PowerShell v4.0.
This is the PowerShell v4.0 code I’m using (pretty much trivial):
$sig=@’
[DllImport(“user32.dll”)]
public static extern IntPtr FindWindow(String sClassName, String sAppName);
‘@

$fw = Add-Type -Namespace Win32 -Name Funcs -MemberDefinition $sig -PassThru
$wname=’Form1’ # any existing window name

$fw::FindWindow($null -as [String], $wname) # returns 0, always!

The last command returns 0, always.
Changing the DllImport attribute to
[DllImport(“user32.dll”, CharSet = CharSet.Unicode)]

does not change anything; 0 is returned the same way.
Interesting to notice that the equivalent code in C#, returns the correct HWND value.
Does anyone know what’s wrong (and how to fix) the PowerShell v4.0 code above?

Solutions/Answers:

Solution 1:

First : not an answer but to help other people working on it, if you use the good class, for example here I code CalcFrame wich is the real class of the main window of calc.exe it works.

$fw::FindWindow("CalcFrame", $wname) # returns the right value for me if calc.exe is started.

Second : The following works for me ; accordind to Microsoft documentation the first parameter should be null, but accordin to PInvoke site you must pass IntPtr.Zero as the first parameter.

$sig = @"
  [DllImport("user32.dll", CharSet = CharSet.Unicode)]
  public static extern IntPtr FindWindow(IntPtr sClassName, String sAppName);

  [DllImport("kernel32.dll")]
  public static extern uint GetLastError();
"@

$fw = Add-Type -Namespace Win32 -Name Funcs -MemberDefinition $sig -PassThru
$wname='Calculatrice' # any existing window name

$fw::FindWindow([IntPtr]::Zero, $wname ) # returns the Window Handle
$a = $fw::GetLastError()
$a

Solution 2:

It seems that the method doesn’t fail if, and only if, the ClassName is also specified (cannot be null) like in this example:

Related:  Exec powershell.exe hangs msbuild
$sig=@'
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
'@    

$w32 = Add-Type -Namespace Win32 -Name Funcs -MemberDefinition $sig -PassThru
$w32::FindWindow('ConsoleWindowClass', 'Windows PowerShell') # Windows PowerShell Console

If the ClassName is null, then the JPBlanc’s method works correctly, which specifies a different signature for the method.

Solution 3:

I had a look at the C# implementation for this issue, and found 2 WNDCLASS structures: WNDCLASS_D AND WNDCLASS_I. The former is the traditional structure that uses strings as types, following the Win32 API. But the latter, uses IntPtr.Zero values for those WNDCLASS structure that take null string values. For this reason, specifying null string values will result in noting found because a null string value is not implicitly convertible to IntPtr.Zero.

Solution 4:

The following code works fine for me, thanks to @zyq’s advice

[System.Windows.Forms.MessageBox]::Show('Test', 'PowerShell Dialog', 
        [Windows.Forms.MessageBoxButtons]::OK, 
        [Windows.Forms.MessageBoxIcon]::Information, 
        [Windows.Forms.MessageBoxDefaultButton]::Button1,
        [Windows.Forms.MessageBoxOptions]::ServiceNotification
        )

$Win32API = Add-Type -Name Funcs -Namespace Win32 -PassThru -MemberDefinition @'
    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr FindWindow(string lpClassName, IntPtr lpWindowName);

    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr FindWindow(IntPtr lpClassName, string lpWindowName);
'@

$Win32API::FindWindow('#32770',       'PowerShell Dialog')
$Win32API::FindWindow([IntPtr]::Zero, 'PowerShell Dialog')
$Win32API::FindWindow('#32770',       [IntPtr]::Zero)
$Win32API::FindWindow('Notepad',      [IntPtr]::Zero)

References