来自 pinvoke WM_GETTEXT 的垃圾字符

发布于 2024-09-14 22:55:49 字数 4622 浏览 6 评论 0原文

我有一个方法集,使用 pinvoke 在另一个程序的文本框中调用 WM_GETTEXT - 它工作得相当好,但我经常只得到附加到其末尾的全部垃圾文本。 (原始文本始终完好无损。)

这是随机的,我无法按需复制它,但它的频率足以导致停止。

这是获取信息的文本。

System.Text.StringBuilder strBuffer = new System.Text.StringBuilder();

int nLen = 0;

bool nUpdated = false;

try
{
    this.isOpen = false;

    if (ptrHandle == null)
        return;

    if (ptrHandle == IntPtr.Zero)
        return;

    nLen =
        Converter.SendMessage(ptrHandle, Converter.WM_GETTEXTLENGTH, 0, 0);

    if (nLen <= 0)
        return;

    if (nPreviousLen != nLen)
        nUpdated = true;

    if (nUpdated)
    {
        System.Diagnostics.Debug.WriteLine("nLen:\t{0}", nLen);

        strBuffer = new System.Text.StringBuilder(null, nLen + 1);

        System.Diagnostics.Debug.WriteLine("strBuffer:\t{0}", strBuffer.ToString());

        int sLen = Converter.SendMessageByString(ptrHandle, Converter.WM_GETTEXT, nLen
            , strBuffer);

        System.Diagnostics.Debug.WriteLine("sLen:\t{0}", sLen);

        System.Diagnostics.Debug.WriteLine("\n\nstrBuffern\n\n{0}", strBuffer.ToString());

        strBuffer = new System.Text.StringBuilder(strBuffer.ToString().Left(sLen));

        System.Diagnostics.Debug.WriteLine("\n\nsLenBuffer\n\n{0}", strBuffer.ToString());

source = new Special.IO.TextReader( 
                    new System.IO.MemoryStream(  System.Text.Encoding.Default.GetBytes(strBuffer.ToString() ) ), nUpdated );
        }
    }
}


    /// <summary>
    /// Sends the specified message to a window or windows. The SendMessage function calls the window procedure for the specified window and does not return until the window procedure has processed the message.
    /// <br />
    /// To send a message and return immediately, use the SendMessageCallback or SendNotifyMessage function. To post a message to a thread's message queue and return immediately, use the PostMessage or PostThreadMessage function.
    /// </summary>
    /// <param name="hWnd">
    /// Handle to the window whose window procedure will receive the message. 
    /// If this parameter is HWND_BROADCAST, the message is sent to all top-level windows in the system, including disabled or invisible unowned windows, overlapped windows, and pop-up windows; but the message is not sent to child windows.
    /// </param>
    /// <param name="Msg">
    /// [in] Specifies the message to be sent.
    /// </param>
    /// <param name="wParam">
    /// [in] Specifies additional message-specific information.
    /// </param>
    /// <param name="lParam">
    /// [in] Specifies additional message-specific information.
    /// </param>
    /// <returns>
    /// The return value specifies the result of the message processing; it depends on the message sent.
    /// </returns>
    [DllImport("user32.dll", EntryPoint = "SendMessageA", CharSet = CharSet.Ansi, SetLastError = false)]
    internal static extern int SendMessageByString(IntPtr hWnd, uint Msg, int wParam, StringBuilder lParam);

    /// <summary>
    /// Sends the specified message to a window or windows. The SendMessage function calls the window procedure for the specified window and does not return until the window procedure has processed the message.
    /// <br />
    /// To send a message and return immediately, use the SendMessageCallback or SendNotifyMessage function. To post a message to a thread's message queue and return immediately, use the PostMessage or PostThreadMessage function.
    /// </summary>
    /// <param name="hWnd">
    /// Handle to the window whose window procedure will receive the message. 
    /// If this parameter is HWND_BROADCAST, the message is sent to all top-level windows in the system, including disabled or invisible unowned windows, overlapped windows, and pop-up windows; but the message is not sent to child windows.
    /// </param>
    /// <param name="Msg">
    /// [in] Specifies the message to be sent.
    /// </param>
    /// <param name="wParam">
    /// [in] Specifies additional message-specific information.
    /// </param>
    /// <param name="lParam">
    /// [in] Specifies additional message-specific information.
    /// </param>
    /// <returns>
    /// The return value specifies the result of the message processing; it depends on the message sent.
    /// </returns>
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    internal static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

I have a method set that uses pinvoke to call WM_GETTEXT on another program's textbox - and it works fairly well, but frequently I just get back total junk text appended to the end of it. (The ORIGINAL text is always intact.)

This is random, I cannot reproduce it on demand, but it is frequent enough to be stopship.

Here is the text to get the information.

System.Text.StringBuilder strBuffer = new System.Text.StringBuilder();

int nLen = 0;

bool nUpdated = false;

try
{
    this.isOpen = false;

    if (ptrHandle == null)
        return;

    if (ptrHandle == IntPtr.Zero)
        return;

    nLen =
        Converter.SendMessage(ptrHandle, Converter.WM_GETTEXTLENGTH, 0, 0);

    if (nLen <= 0)
        return;

    if (nPreviousLen != nLen)
        nUpdated = true;

    if (nUpdated)
    {
        System.Diagnostics.Debug.WriteLine("nLen:\t{0}", nLen);

        strBuffer = new System.Text.StringBuilder(null, nLen + 1);

        System.Diagnostics.Debug.WriteLine("strBuffer:\t{0}", strBuffer.ToString());

        int sLen = Converter.SendMessageByString(ptrHandle, Converter.WM_GETTEXT, nLen
            , strBuffer);

        System.Diagnostics.Debug.WriteLine("sLen:\t{0}", sLen);

        System.Diagnostics.Debug.WriteLine("\n\nstrBuffern\n\n{0}", strBuffer.ToString());

        strBuffer = new System.Text.StringBuilder(strBuffer.ToString().Left(sLen));

        System.Diagnostics.Debug.WriteLine("\n\nsLenBuffer\n\n{0}", strBuffer.ToString());

source = new Special.IO.TextReader( 
                    new System.IO.MemoryStream(  System.Text.Encoding.Default.GetBytes(strBuffer.ToString() ) ), nUpdated );
        }
    }
}


    /// <summary>
    /// Sends the specified message to a window or windows. The SendMessage function calls the window procedure for the specified window and does not return until the window procedure has processed the message.
    /// <br />
    /// To send a message and return immediately, use the SendMessageCallback or SendNotifyMessage function. To post a message to a thread's message queue and return immediately, use the PostMessage or PostThreadMessage function.
    /// </summary>
    /// <param name="hWnd">
    /// Handle to the window whose window procedure will receive the message. 
    /// If this parameter is HWND_BROADCAST, the message is sent to all top-level windows in the system, including disabled or invisible unowned windows, overlapped windows, and pop-up windows; but the message is not sent to child windows.
    /// </param>
    /// <param name="Msg">
    /// [in] Specifies the message to be sent.
    /// </param>
    /// <param name="wParam">
    /// [in] Specifies additional message-specific information.
    /// </param>
    /// <param name="lParam">
    /// [in] Specifies additional message-specific information.
    /// </param>
    /// <returns>
    /// The return value specifies the result of the message processing; it depends on the message sent.
    /// </returns>
    [DllImport("user32.dll", EntryPoint = "SendMessageA", CharSet = CharSet.Ansi, SetLastError = false)]
    internal static extern int SendMessageByString(IntPtr hWnd, uint Msg, int wParam, StringBuilder lParam);

    /// <summary>
    /// Sends the specified message to a window or windows. The SendMessage function calls the window procedure for the specified window and does not return until the window procedure has processed the message.
    /// <br />
    /// To send a message and return immediately, use the SendMessageCallback or SendNotifyMessage function. To post a message to a thread's message queue and return immediately, use the PostMessage or PostThreadMessage function.
    /// </summary>
    /// <param name="hWnd">
    /// Handle to the window whose window procedure will receive the message. 
    /// If this parameter is HWND_BROADCAST, the message is sent to all top-level windows in the system, including disabled or invisible unowned windows, overlapped windows, and pop-up windows; but the message is not sent to child windows.
    /// </param>
    /// <param name="Msg">
    /// [in] Specifies the message to be sent.
    /// </param>
    /// <param name="wParam">
    /// [in] Specifies additional message-specific information.
    /// </param>
    /// <param name="lParam">
    /// [in] Specifies additional message-specific information.
    /// </param>
    /// <returns>
    /// The return value specifies the result of the message processing; it depends on the message sent.
    /// </returns>
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    internal static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

对风讲故事 2024-09-21 22:55:49

这似乎发生了一些奇怪的事情,看起来您所瞄准的控件在通过 WM_GETTEXT 使用 P/Invoke 时返回垃圾...我建议执行以下操作,而不是返回整个缓冲区,而是返回当前行,这将使事情变得更快捷...

try{
    int nLineCount = Converter.SendMessage(ptrHandle, Converter.EM_GETLINECOUNT, 0, 0);
    int nIndex = Converter.SendMessage(ptrHandle, Converter.EM_LINEINDEX, nLineCount, 0);
    int nLineLen = Converter.SendMessage(ptrHandle, Converter.EM_LINELENGTH, nIndex, 0);
    //
    strBuffer = new System.Text.StringBuilder(nLineLen);
    strBuffer.Append(Convert.ToChar(nLineLen));
    strBuffer.Length = nLineLen;
    int nCharCnt = Converter.SendMessage(ptrHandle, Converter.EM_GETLINE, new IntPtr(nLineCount),     strBuffer).ToInt32();
    nLen = nCharCnt;
    if (nLen <= 0) return;
    if (nPreviousLen != nLen) nUpdated = true;
}finally{
    source = new TextReader(strBuffer.ToString(), nUpdated, isOpen ? true : false);
    this.isOpen = true;
    nPreviousLen = nLen;
}

通过这种方式,我们获得:

  • 控件中的行数 - nLineCount
  • 获取作为行开头的字符索引 nLineCount - nIndex
  • 最后,使用 nIndex 获取行长 - nLineLen

使用 nLineLen,然后我们可以设置 StringBuilder 缓冲区,使用 EM_GETLINE 的棘手部分是,缓冲区的第 0 个位置必须包含 char 的长度 - 因此使用 strBuffer.Append(Convert. ToChar(nLineLen)),并指定字符串构建器的Length 属性。

以下是上述 P/Invoke 所需的常量

  • const int EM_GETLINECOUNT = 0xBA;
  • 常量 int EM_LINEINDEX = 0xBB;
  • 常量 int EM_LINELENGTH = 0xC1;

There seems to be some-thing weird going on with this, it looks like the control you are targeting, when using P/Invoke via WM_GETTEXT is returning the junk... I suggest the following, instead of returning the whole buffer, return back the current line which would make things a bit snappier...

try{
    int nLineCount = Converter.SendMessage(ptrHandle, Converter.EM_GETLINECOUNT, 0, 0);
    int nIndex = Converter.SendMessage(ptrHandle, Converter.EM_LINEINDEX, nLineCount, 0);
    int nLineLen = Converter.SendMessage(ptrHandle, Converter.EM_LINELENGTH, nIndex, 0);
    //
    strBuffer = new System.Text.StringBuilder(nLineLen);
    strBuffer.Append(Convert.ToChar(nLineLen));
    strBuffer.Length = nLineLen;
    int nCharCnt = Converter.SendMessage(ptrHandle, Converter.EM_GETLINE, new IntPtr(nLineCount),     strBuffer).ToInt32();
    nLen = nCharCnt;
    if (nLen <= 0) return;
    if (nPreviousLen != nLen) nUpdated = true;
}finally{
    source = new TextReader(strBuffer.ToString(), nUpdated, isOpen ? true : false);
    this.isOpen = true;
    nPreviousLen = nLen;
}

In that way, we obtain:

  • the line count in the control - nLineCount
  • Obtain the character index that is the start of the line nLineCount - nIndex
  • Finally, obtain the line length using the nIndex - nLineLen

Using nLineLen, then we can set up the StringBuilder buffer, the tricky part in using EM_GETLINE is, the zero'th position of the buffer MUST contain the length in char's - hence the usage of strBuffer.Append(Convert.ToChar(nLineLen)), and the stringbuilder's Length property specified.

Here are the constants required for the above P/Invoke

  • const int EM_GETLINECOUNT = 0xBA;
  • const int EM_LINEINDEX = 0xBB;
  • const int EM_LINELENGTH = 0xC1;
岁吢 2024-09-21 22:55:49

发送 WM_GETTEXT 时不应忽略返回值。来自 MSDN:

返回值是复制的字符数,不包括终止空字符。

如果其他应用程序在 WM_GETTEXTLENGTH 和 WM_GETTEXT 之间更改了控件的文本(缩短),那么这将解释您所看到的内容:WM_GETTEXT 填充您的前(比方说)5 个字符20个字符的StringBuilder,其余未定义。它可能有空字符,也可能有垃圾(取决于您是否调用 ANSI 版本的 SendMessage,这将迫使操作系统代表您分配可能充满垃圾的临时缓冲区),但无论哪种方式,在使用绳子之前,您需要将它们剥掉。

您需要读取 SendMessageByString 调用的返回值,并在使用 StringBuilder 之前将其截断为该长度。

You shouldn't ignore the return value when you send WM_GETTEXT. From MSDN:

The return value is the number of characters copied, not including the terminating null character.

If the other app changes the control's text (to something shorter) between your WM_GETTEXTLENGTH and your WM_GETTEXT, then that would explain what you're seeing: WM_GETTEXT fills the first (let's say) 5 characters of your 20-character StringBuilder, and the rest is undefined. It might have null characters or it might have garbage (depends on things like whether you're calling the ANSI version of SendMessage, which would force the OS to allocate a probably-garbage-filled temporary buffer on your behalf), but either way, you need to strip them off before you use the string.

You need to read the return value of your SendMessageByString call, and truncate the StringBuilder to that length before using it.

眉黛浅 2024-09-21 22:55:49

在我看来,您的错误在于错误使用了 WM_GETTEXT 消息的一个参数。您应该使用 nLen + 1 而不是 nLen 作为 wParam

首先,您使用 WM_GETTEXTLENGTH 获取 nLen,这将是复制的 TCHAR 数,不包括终止空字符。然后分配大小为 nLen + 1 字符的缓冲区。这些步骤绝对正确,但是您发送 WM_GETTEXT 时使用 nLen 作为 wParam 这是错误的,因为对应于 http://msdn.microsoft.com/en-us/library/ms632627.aspx < code>wParam 必须包含要复制的最大字符数,包括终止空字符。所以WM_GETTEXT消息的正确参数必须是nLen + 1而不是nLen

我发现使用更大作为nLen的缓冲区是最好的方法。我建议您分配的缓冲区至少比 WM_GETTEXTLENGTH 返回的 nLen 值长 2 个字符,并使用 nLen + 2 作为参数WM_GETTEXT(缓冲区大小到底有多大)。如果WM_GETTEXT的返回值小于或等于nLen,则可以确定返回的字符串包含您要读取的全文。如果 WM_GETTEXT 的结果为 nLen + 1,则在发送 WM_GETTEXTLENGTHWM_GETTEXT 之间文本会发生变化消息,您应该再次重复以 WM_GETTEXTLENGTH 开头的所有步骤,以了解新的文本大小。

It seems to me that your error is in the wrong usage of one parameter of WM_GETTEXT message. You should use nLen + 1 instead of nLen as the wParam.

At the beginning you use WM_GETTEXTLENGTH to get nLen, which will be the number of TCHARs copied, not including the terminating null character. Then you allocate the buffer of the size nLen + 1 characters. There steps are absolutely correctly, but then you send WM_GETTEXT with nLen as the wParam which is wrong, because corresponds to http://msdn.microsoft.com/en-us/library/ms632627.aspx wParam must contain the maximum number of characters to be copied, including the terminating null character. So the correct parameter of WM_GETTEXT message must be nLen + 1 instead of nLen.

The usage of the buffer which are larger as nLen I find for the best way. I'll recommend you to allocate buffer at least 2 characters longer as the nLen value returned by WM_GETTEXTLENGTH and use nLen + 2 as the parameter of WM_GETTEXT (exactly how large are you buffer size). If the returned value of the WM_GETTEXT are nLen or less, then you can be sure that the returned string contain full text which you want to read. If the result of WM_GETTEXT will be nLen + 1, then the text are changed between sending of WM_GETTEXTLENGTH and WM_GETTEXT messages and you should repeat all the steps starting with WM_GETTEXTLENGTH one more time to know the new text size.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文