C# Pinvoke 在第一次列表计数为 0 后找不到控件的 Hwnd

Posted

技术标签:

【中文标题】C# Pinvoke 在第一次列表计数为 0 后找不到控件的 Hwnd【英文标题】:C# Pinvoke can't find the Hwnd of Controls after List count was 0 at first time 【发布时间】:2014-07-20 11:31:34 【问题描述】:

我正在尝试单击另一个应用程序中的按钮(从我的带有 Process.Start 的程序开始)

问题:我需要等到加载屏幕消失并且 GUI 弹出...

我的想法是读取所有 (Hwnd)Controls,直到找到 GUI 中的特定控件(按钮:“Kill Client”)(=GUI Opened)。

但这只有在我手动等待 GUI 并按下“搜索控件”按钮时才有效。

如果我在加载屏幕处于活动状态时按下“搜索按钮”,我会得到一个 Hwnd = 0(List 计数也是 0...),如果我在打开 GUI 时再次按下它,它是又是 0(List 也算...)!!!

这是我的代码:

 public class WndSearcher
 
    [DllImport("user32")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);

    public static List<IntPtr> GetChildWindows(IntPtr parent)
    
        List<IntPtr> result = new List<IntPtr>();
        GCHandle listHandle = GCHandle.Alloc(result);
        try
        
            EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
            EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
        
        finally
        
            if (listHandle.IsAllocated)
                listHandle.Free();
        
        return result;
    

    private static bool EnumWindow(IntPtr handle, IntPtr pointer)
    
        GCHandle gch = GCHandle.FromIntPtr(pointer);
        List<IntPtr> list = gch.Target as List<IntPtr>;
        if (list == null)
        
            throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
        
        list.Add(handle);
        return true;
    

我的按钮:

List<IntPtr> AllControlHandles = WndSearcher.GetChildWindows(selectedCharacter.Botprocess.MainWindowHandle);
IntPtr ControlHandle = AllControlHandles.Find(x => PInvoke.GetWindowTextRaw(x) == "Kill Client" ? true : false);
MessageBox.Show(ControlHandle.ToString());

PInvoke(类)的一部分:

const int WM_GETTEXT = 0x000D;
const int WM_GETTEXTLENGTH = 0x000E;

[DllImport("user32.dll", CharSet = CharSet.Auto)]
    static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, [Out] StringBuilder lParam);

public static string GetWindowTextRaw(IntPtr hwnd)
    
        // Allocate correct string length first
        int length = (int)SendMessage(hwnd, WM_GETTEXTLENGTH, IntPtr.Zero, null);
        StringBuilder sb = new StringBuilder(length + 1);
        SendMessage(hwnd, WM_GETTEXT, (IntPtr)sb.Capacity, sb);
        return sb.ToString();
    

【问题讨论】:

Thread.Sleep() 是一种明显的等待方式。 你可以使用一个 while 循环(当 hwnd = 0,睡眠几毫秒),然后你想要的代码。 @Scott 我尝试过这种方式,但如果列表是第一次 Count=0 那么它将是每次 0 ...(这就是我尝试使用按钮但同样的问题的原因这里(如果我在加载屏幕处于活动状态时搜索控件)。 @Hans 我知道,但我想要另一种方式 :) 您应该为此使用自动化 【参考方案1】:

目前还没有找到解决办法。

所以我决定将 AutoHotKey 与 C# 结合使用。

在 C# 中,我启动我的 AutoHotKey 脚本并等待脚本完成。 (然后外部Programm就完全启动了)

起始参数:1.Processid 2.NewExternalProgramName

这是我的 AutoHotKey 脚本:

counter := 0
Loop, %0%  ; For each parameter:


    param := %A_Index%

    if (counter = 0) ; do sth with parameter 1
        winwait, ahk_pid %param% ; Not logged in ;wait until the text "Not logged in" can be read (Program started completely)

    if (counter = 1) ; do sth with parameter 2
        WinSetTitle, %param%

    counter += 1

【讨论】:

以上是关于C# Pinvoke 在第一次列表计数为 0 后找不到控件的 Hwnd的主要内容,如果未能解决你的问题,请参考以下文章

C# 编组、不平衡堆栈和正确获取 PInvoke 签名

通过 PInvoke 从 C# 到 C++ 的简单结构中的垃圾数据

PInvoke 与指针 - C++ 到 C#

如何使用 pinvoke 将二进制数据缓冲区从 C 传递到 C#

C# Marshal / Pinvoke CBitmap?

pinvoke:不确定如何使用 dllimport 和导入的库