如何获取超时窗口的文本?

时间:2013-06-17 09:02:05

标签: c# c++ porting

我目前正在将C++应用移植到C#,但无法转换此特定功能。 此函数获取给定窗口句柄的文本/标题。

我发现重要的部分是对SendMessageTimeoutW的2次调用,这些调用得到了我假设的窗口文本,但我无法弄清楚其余部分。

我也找不到UTF-16函数SendMessageTimeoutW的任何P / Invoke签名,因此对SendMessageTimeout的调用是等效的,如this post所示?

public static unsafe string GetWindowText(IntPtr hWnd)
{

    // THIS PART MAY NOT BE NEEDED
    string str2 = null;
    WinHookEx* exPtr3;
    WinHookEx* exPtr = @new(4);
    try
    {
        exPtr3 = (exPtr == null) ? null : WinHookEx.{ctor}(exPtr);
    }
    fault
    {
        delete((void*) exPtr);
    }
    WinHookEx* exPtr2 = exPtr3;
    *((int*) exPtr2) = hWnd.ToPointer();
    HWND__* hwnd__Ptr = (HWND__*) hWnd.ToPointer();
    uint modopt(IsLong) num = 0;
    delete((void*) exPtr2);


    // 1st call to SendMessageTimeoutW
    if (SendMessageTimeoutW(hwnd__Ptr, 14, 0, 0, 2, 0x3e8, &num) == 0)
    {
        return null;
    }

    // whats happening here?
    num++;
    uint modopt(IsLong) num2 = num;
    char* chPtr = @new((num2 > 0x7fffffff) ? uint.MaxValue : ((uint) (num2 * 2)));
    chPtr[0] = '\0';

    // 2nd call to SendMessageTimeoutW
    if (SendMessageTimeoutW(hwnd__Ptr, 13, num, (int modopt(IsLong)) chPtr, 2, 0x3e8, &num) == 0)
    {
        return null;
    }
    str2 = new string(chPtr);
    delete((void*) chPtr);
    return str2;
}

我暂时将此功能移植到C#,但它始终返回空白字符串。 我甚至尝试使用new StringBuilder(256)启动字符串构建器,但它仍然不起作用。

我做错了什么?

public static unsafe string GetWindowText(IntPtr hWnd){

    // send WM_GETTEXTLENGTH 
    if (SendMessageTimeout(hWnd, 14, 0, 0, 2, 0x3e8, IntPtr.Zero) == 0){
        return null;
    }

    // send WM_GETTEXT
    StringBuilder sb = new StringBuilder();
    if (SendMessageTimeout(hWnd, 13, 0, sb, 2, 0x3e8, IntPtr.Zero) == 0){
        return null;
    }

    return sb.ToString();
}

2 个答案:

答案 0 :(得分:1)

对于WM_GETTEXTwParamSendMessageTimeout的第三个参数)是

  

要复制的最大字符数,包括终止空字符。

你传递零。

此外,您正在调用WM_GETTEXTLENGTH但未使用返回值:

  

返回值是以字符为单位的文本长度,不包括终止空字符。

使用它来指定StringBuilder的初始大小。我刚刚证实了这一点:

    static void Main(string[] args) {
        var p = Process.GetProcessById(3484);
        var h = p.MainWindowHandle;

        string s = GetWindowTextTimeout(h, 100 /*msec*/);

    }


    [DllImport("User32.dll", SetLastError = true)]
    public unsafe static extern int SendMessageTimeout(
      IntPtr hWnd,
      uint uMsg,
      uint wParam,
      StringBuilder lParam,
      uint fuFlags,
      uint uTimeout,
      void* lpdwResult);

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

    public static unsafe string GetWindowTextTimeout(IntPtr hWnd, uint timeout)
    {
        int length;
        if (SendMessageTimeout(hWnd, WM_GETTEXTLENGTH, 0, null, 2, timeout, &length) == 0) {
            return null;
        }
        if (length == 0) {
            return null;
        }

        StringBuilder sb = new StringBuilder(length + 1);  // leave room for null-terminator
        if (SendMessageTimeout(hWnd, WM_GETTEXT, (uint)sb.Capacity, sb, 2, timeout, null) == 0) {
            return null;
        }

        return sb.ToString();
    }

答案 1 :(得分:0)

你需要传递缓冲区的长度。

int size;
SendMessageTimeout((int)hWnd, WM_GETTEXTLENGTH, 0, 0, 2, 0x3e8, &size).ToInt32();

if (size > 0)
{
    StringBuilder sb = new StringBuilder(size + 1);

    SendMessageTimeout(hWnd, WM_GETTEXT, sb.Capacity, sb, 2, 0x3e8, IntPtr.Zero)
}