【问题标题】:Can't get text from another application (window)无法从其他应用程序(窗口)获取文本
【发布时间】:2016-08-18 01:04:02
【问题描述】:

我正在尝试从另一个应用程序中提取文本。这个应用程序现在可能很简单,我只想让它工作(现在)。

我使用的代码:

public static class ModApi
{
    [DllImport("user32.dll", EntryPoint = "SendMessageTimeout", SetLastError =     true, CharSet = CharSet.Unicode)]
    public static extern uint SendMessageTimeoutText(IntPtr hWnd, int Msg, int countOfChars, StringBuilder text, uint flags, uint uTimeoutj, uint result);

    public static string GetText(IntPtr hwnd)
    {
        var text = new StringBuilder(1024);

        if (SendMessageTimeoutText(hwnd, 0xd, 1024, text, 0x2, 5000, 0) != 0)
        {
            return text.ToString();
        }

        MessageBox.Show(text.ToString());
        return "";
    }
}

我使用以下代码调用此代码:

IntPtr MytestHandle = new IntPtr(0x00788600);
HandleRef hrefHWndTarget = new HandleRef(null, MytestHandle);

0x00788600 是我正在运行的应用程序之一的示例(我 100% 确定这是主窗口句柄)。

我需要在“其他”应用程序中保存一个文本框中的文本,但是当我使用我的代码时,它每次都返回一个空字符串

建议?

【问题讨论】:

  • 所有这些硬编码的幻数究竟应该做什么?是什么让您认为您用作 HWND 的数字实际上是一个有效的窗口句柄? (是的,我读到 我 100% 确定,但因为你这么说并不代表它是真的 - IsWindow 说什么?。)

标签: c# text extract


【解决方案1】:

我看不出您的代码有任何错误。我建议检查一下你的句柄是否正确。

但是,为了获得TextBox 的文本,您将不得不使用实际控件的句柄。 MainWindowHandle 只会返回表单标题。

我创建了一个虚拟应用程序“WindowsFormsApplication1”,其中包含一些控件,并使用以下代码获取所有文本:

[Flags]
internal enum SendMessageTimeoutFlags : uint
{
    SMTO_NORMAL = 0x0,
    SMTO_BLOCK = 0x1,
    SMTO_ABORTIFHUNG = 0x2,
    SMTO_NOTIMEOUTIFNOTHUNG = 0x8,
    SMTO_ERRORONEXIT = 0x20
}

// Specific import for WM_GETTEXTLENGTH
[DllImport("user32.dll", EntryPoint = "SendMessageTimeout", CharSet = CharSet.Auto)]
internal static extern int SendMessageTimeout(
    IntPtr hwnd,
    uint Msg,              // Use WM_GETTEXTLENGTH
    int wParam,
    int lParam,
    SendMessageTimeoutFlags flags,
    uint uTimeout,
    out int lpdwResult);

// Specific import for WM_GETTEXT
[DllImport("user32.dll", EntryPoint = "SendMessageTimeout", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern uint SendMessageTimeoutText(
    IntPtr hWnd,
    uint Msg,              // Use WM_GETTEXT
    int countOfChars,
    StringBuilder text,
    SendMessageTimeoutFlags flags,
    uint uTImeoutj,
    out IntPtr result);

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

// callback to enumerate child windows
private delegate bool EnumWindowProc(IntPtr hwnd, IntPtr parameter);

private static bool EnumChildWindowsCallback(IntPtr handle, IntPtr pointer)
{
    // this method will be called foreach child window
    // create a GCHandle from pointer
    var gcHandle = GCHandle.FromIntPtr(pointer);

    // cast pointer as list
    var list = gcHandle.Target as List<IntPtr>;

    if (list == null)
        throw new InvalidCastException("Invalid cast of GCHandle as List<IntPtr>");

    // Adds the handle to the list.
    list.Add(handle);

    return true;
}

private static IEnumerable<IntPtr> GetChildWindows(IntPtr parent)
{
    // Create list to store child window handles.
    var result = new List<IntPtr>();

    // Allocate list handle to pass to EnumChildWindows.
    var listHandle = GCHandle.Alloc(result);

    try
    {
        // enumerates though the children
        EnumChildWindows(parent, EnumChildWindowsCallback, GCHandle.ToIntPtr(listHandle));
    }
    finally
    {
        // free unmanaged list handle
        if (listHandle.IsAllocated)
            listHandle.Free();
    }

    return result;
}

internal static string GetText(IntPtr hwnd)
{
    const uint WM_GETTEXTLENGTH = 0x000E;
    const uint WM_GETTEXT = 0x000D;
    int length;
    IntPtr p;

    var result = SendMessageTimeout(hwnd, WM_GETTEXTLENGTH, 0, 0, SendMessageTimeoutFlags.SMTO_ABORTIFHUNG, 5, out length);

    if (result != 1 || length <= 0)
        return string.Empty;

    var sb = new StringBuilder(length + 1);

    return SendMessageTimeoutText(hwnd, WM_GETTEXT, sb.Capacity, sb, SendMessageTimeoutFlags.SMTO_ABORTIFHUNG, 5, out p) != 0 ?
            sb.ToString() : 
            string.Empty;
}

public static void Main(string[] args)
{
    var p = Process.GetProcessesByName("WindowsFormsApplication1").First();            

    Console.WriteLine(GetText(p.MainWindowHandle));    // main window handle of form, returns "Form1"
    Console.WriteLine(GetText(new IntPtr(0x70BA0)));   // actual textbox handle, used Winspector, returns "quertz"

    // iterate through dynamic handles of children
    foreach (var hwnd in GetChildWindows(p.MainWindowHandle))
        Console.WriteLine($"{hwnd}:{GetText(hwnd)}");

    Console.ReadLine();
}     

【讨论】:

  • 你太棒了!非常感谢,在与 Winspector(您作为评论写的)杂耍之后,一切正常!非常感谢:)
  • 这个答案可以节省时间。我不知道为什么它得分低
【解决方案2】:

要节省大量代码,您可以使用 Autoit 库。

安装名为AutoItX.Dotnet的nuget包

using AutoIt;

class Program
{
    static void Main(string[] args)
    {
        var text = AutoItX.ControlGetText("Untitled - Notepad", "", "[CLASSNN:Edit1]");

        //In your case, since you are dealing with handles, you can use:
        var windowHandle = new IntPtr(0x00788600);
        var controlHandle = new IntPtr(0x00000000);
        var text2 = AutoItX.ControlGetText(windowHandle, controlHandle);
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-09
    • 1970-01-01
    • 2020-08-30
    • 1970-01-01
    • 2021-04-19
    • 2018-07-17
    • 1970-01-01
    相关资源
    最近更新 更多