【问题标题】:unable to read another application's caption无法阅读其他应用程序的标题
【发布时间】:2011-06-03 23:48:45
【问题描述】:

我将如何在我的主程序中找到窗口句柄...

在 C# 中

我运行 notepad.exe 然后在其中输入一些内容,然后使用 SPY++ (0x111111) 找到主窗口句柄,然后

[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]

internal static extern int GetWindowText(IntPtr hWnd, [Out] StringBuilder lpString, int nMaxCount);
.
.
.
GetWindowText((IntPtr)(0x111111), str, 1024);

这段代码运行良好,并返回主窗口的标题。

: : 但是当我做同样的事情来查找 notepad.exe 的子项的标题时,它只是将 str 设置为空。 spy++ 告诉我孩子的标题是有价值的。

【问题讨论】:

  • 哪个孩子???范围可以从“文件”菜单选项到状态栏或介于两者之间的任何内容!
  • 我在文本区写了一些东西。然后使用 spy++ 在标题中找到带有文本的句柄,然后在我的代码中使用该句柄。
  • 您确实意识到句柄 (hWnd) 并不总是相同的吗?在应用程序中硬编码值是没有用的。

标签: c# dllimport user32


【解决方案1】:

GetWindowText function documentation 明确指出“GetWindowText 无法在另一个应用程序中检索控件的文本。...要在另一个进程中检索控件的文本,请直接发送 WM_GETTEXT 消息而不是调用 GetWindowText。”

您可以使用以下代码检索控件的文本:

[DllImport("user32.dll", EntryPoint = "SendMessage")]
public static extern IntPtr SendMessageGetText(IntPtr hWnd, uint msg, UIntPtr wParam, StringBuilder lParam);

const uint WM_GETTEXT = 13;
const int bufferSize = 1000; // adjust as necessary
StringBuilder sb = new StringBuilder(bufferSize);
SendMessageGetText(hWnd, WM_GETTEXT, new UIntPtr(bufferSize), sb);
string controlText = sb.ToString();

【讨论】:

  • wParam 参数是 IntPtr。返回值也一样,在这种情况下并不重要。
  • @Hans 谢谢;我更正了签名以匹配记录的类型 LRESULT (LONG_PTR)、UINT 和 WPARAM (UINT_PTR)。
  • 谢谢,它有效,请问我在阅读之前如何找到文本的长度,或者我如何阅读文本的尾部?
  • @amir:您正试图挖掘另一个程序的内部结构。这显然是未定义的领域。你应该重新考虑你的解决方案。
  • @amir:使用 WM_GETTEXTLENGTH (msdn.microsoft.com/en-us/library/ms632628(v=vs.85).aspx)。
【解决方案2】:

“最正确”的做法是:

public static string GetWindowText(IntPtr hwnd)
{
    if (hwnd == IntPtr.Zero)
        throw new ArgumentNullException("hwnd");
    int length = SendMessageGetTextLength(hwnd, WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero);
    if (length > 0 && length < int.MaxValue)
    {
        length++; // room for EOS terminator
        StringBuilder sb = new StringBuilder(length);
        SendMessageGetText(hwnd, WM_GETTEXT, (IntPtr)sb.Capacity, sb);
        return sb.ToString();
    }
    return String.Empty;
}

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

[DllImport("User32.dll", EntryPoint = "SendMessage")]
extern static int SendMessageGetTextLength(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
[DllImport("User32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
extern static IntPtr SendMessageGetText(IntPtr hWnd, int msg, IntPtr wParam, [Out] StringBuilder lParam);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
extern static IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, [In] string lpClassName, [In] string lpWindowName);

注意使用[In][Out] 属性来消除编组期间不必要的复制。

另外请注意,您不应该将 p/invoke 方法暴露给外界(不公开)。

【讨论】:

  • 能否请您提供一些关于如何在我的文本框中查找最新更改的提示?我有一个不断增长的文本框,我可以将某些内容链接到该文本框的事件列表或类似的内容吗?
  • 系统 EDIT 控件上没有可以由外部进程监控的更改通知。您可以轮询它,或者您可以编写一个全局挂钩来实现这样的事件。该钩子将是一个 CBT_CALLWNDPROCRET。这需要编写一个本地 DLL(即 C/C++),并且如果被挂钩的进程像记事本一样是 32 位和 64 位,那么这会很复杂。
猜你喜欢
  • 2014-01-21
  • 1970-01-01
  • 2012-05-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多