如何在PowerShell环境中使用Win32 API FindWindow()在Windows 8.1 Update 2操作系统中查找桌面窗口(按窗口名称)?

如何在PowerShell环境中使用Win32 API FindWindow()在Windows 8.1 Update 2操作系统中查找桌面窗口(按窗口名称)?,windows,powershell,Windows,Powershell,我不记得在较旧的Windows操作系统中查找窗口时遇到任何问题,但是,我在使用PowerShell v4.0的Windows 8.1 Update 2操作系统中没有成功 这是我正在使用的PowerShell v4.0代码(非常简单): 最后一个命令始终返回0 将DllImport属性更改为 [DllImport("user32.dll", CharSet = CharSet.Unicode)] 不会改变任何事情0以相同的方式返回 有趣的是,注意到C#中的等效代码返回正确的HWND值 有人知道上

我不记得在较旧的Windows操作系统中查找窗口时遇到任何问题,但是,我在使用PowerShell v4.0的Windows 8.1 Update 2操作系统中没有成功

这是我正在使用的PowerShell v4.0代码(非常简单):

最后一个命令始终返回0

将DllImport属性更改为

[DllImport("user32.dll", CharSet = CharSet.Unicode)]
不会改变任何事情0以相同的方式返回

有趣的是,注意到C#中的等效代码返回正确的HWND


有人知道上面的PowerShell v4.0代码出了什么问题(以及如何修复)吗?

首先:如果你使用好的类,这不是一个答案,而是为了帮助其他人,例如这里的I code
CalcFrame
它是
calc.exe
主窗口的真实类

$fw::FindWindow("CalcFrame", $wname) # returns the right value for me if calc.exe is started.
第二:以下内容适合我;第一个参数应为null,但根据e站点,必须将IntPtr.Zero作为第一个参数传递


如果并且仅当还指定了
ClassName
(不能为
null
),该方法似乎不会失败,如本例所示:

$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

如果类名为
null
,则JPBlanc的方法工作正常,为该方法指定了不同的签名。

我查看了这个问题的C#实现,发现了两个WNDCLASS结构:WNDCLASS_D和WNDCLASS_I。前者是使用字符串作为类型的传统结构,遵循Win32 API。但是后者使用IntPtr.Zero值来表示那些接受空字符串值的WNDCLASS结构。因此,指定空字符串值将导致note found,因为空字符串值不能隐式转换为IntPtr.Zero。

由于@zyq的


问题是试图将
$null
传递给字符串类型的参数,结果是空字符串。这意味着
FindWindow
将搜索一个空的类名,这将永远不会成功

这曾经是一个众所周知的限制,也是引入的原因(有关更多信息,请参阅)

借用Valdemar的答案中的一些代码,以下应该是可行的:

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

# find window by title only
$Win32API::FindWindow([NullString]::Value, 'window title')

你试过FindWindowEx吗?这个用于查找子窗口。FindWindow()怎么了?很久以前就正确地使用它(用于顶级窗口)。包括在内,它可以在C#环境中正常工作。您也可以找到顶级窗口。在文档中:“如果hwndParent为NULL,则函数使用桌面窗口作为父窗口。函数在作为桌面子窗口的窗口之间搜索。”我建议这样做的原因是我找到了一些文章(与C++相关)在Windows 8.1下使用FINDOWINK也有问题,使用FINDINWOWEX作为一种解决方案。你能把链接链接到那些C++文章吗?如果你有这些链接,我将不胜感激。谢谢。是的,正如我在下面解释的,C#实现中使用了两个WNDCLASS结构。我在这个问题上查看了C#实现,发现了两个WNDCLASS结构:WNDCLASS#D和WNDCLASS#I。前者是使用字符串作为类型的传统结构,遵循Win32 API。但是后者使用IntPtr.Zero值来表示那些接受空字符串值的WNDCLASS结构。因此,指定空字符串值将导致note found,因为空字符串值不能隐式转换为IntPtr.Zero。如果这样说,没有问题。我把你的回答标记为正确,而不是我的。这是你想要的,但我不明白你为什么不把你的回答作为我的一个评论?它就在你的回答之前。你的回答不是用你自己的话来回答的。另外,我将我的回复作为附加答案发布,因为它告诉我们为什么代码必须使用IntPtr.Zero,而不仅仅是它必须使用它。但我非常感谢您的回复。非常感谢,将$null替换为[IntPtr]::零对我来说很有用
$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
[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)
$Win32API = Add-Type -Name Funcs -Namespace Win32 -PassThru -MemberDefinition @'
    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
'@

# find window by title only
$Win32API::FindWindow([NullString]::Value, 'window title')