C# 来自pinvoke WM_GETTEXT的垃圾角色

C# 来自pinvoke WM_GETTEXT的垃圾角色,c#,pinvoke,C#,Pinvoke,我有一个方法集,它使用pinvoke在另一个程序的文本框上调用WM_GETTEXT——它工作得相当好,但通常我只会在它的末尾返回全部垃圾文本。(原始文本始终保持完整。) 这是随机的,我不能按需复制,但它足够频繁,可以停止 以下是获取信息的文本 System.Text.StringBuilder strBuffer = new System.Text.StringBuilder(); int nLen = 0; bool nUpdated = false; try { this.is

我有一个方法集,它使用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);
System.Text.StringBuilder strBuffer=new System.Text.StringBuilder();
int-nLen=0;
bool nUpdated=false;
尝试
{
this.isOpen=false;
if(ptrHandle==null)
返回;
if(ptrHandle==IntPtr.Zero)
返回;
恩伦=
SendMessage(ptrHandle,Converter.WM_GETTEXTLENGTH,0,0);

如果(nLen您在从MSDN发送WM_GETTEXT.时不应忽略返回值:

如果另一个应用程序在WM_GETTEXTLENGTH和WM_GETTEXT之间更改控件的文本(变短),那么这就可以解释您看到的情况:WM_GETTEXT填充了20个字符的StringBuilder的前5个字符,其余的未定义。它可能有空字符,也可能有垃圾(取决于您是否调用ANSI版本的SendMessage,这将迫使操作系统为您分配一个可能充满垃圾的临时缓冲区),但无论哪种方式,您都需要在使用该字符串之前将其去掉


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

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

开始时,您使用
WM_GETTEXTLENGTH
获取
nLen
,这将是复制的TCHARs数,不包括终止的空字符。然后分配大小为
nLen+1
字符的缓冲区。这些步骤绝对正确,但随后您使用
nLen
作为
wParam
是错误的,因为对应于
wParam
的必须包含要复制的最大字符数,包括终止的空字符。因此
WM_GETTEXT
消息的正确参数必须是
nLen+1
,而不是
nLen


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

这似乎有点奇怪,看起来您的目标控件,当通过WM_GETTEXT使用p/Invoke时,返回垃圾…我建议以下内容,而不是返回whole buffer,返回当前行,这将使事情变得更加快速

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;
}
试试看{
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(NLINLEN);
strBuffer.Append(Convert.ToChar(nLineLen));
strBuffer.Length=nLineLen;
int-nCharCnt=Converter.SendMessage(ptrHandle,Converter.EM_GETLINE,new-IntPtr(nLineCount),strBuffer).ToInt32();
nLen=nCharCnt;

如果(nLen您是否需要处理WM_GETTEXTLENGTH步骤?您不能在事后分析StringBuilder以查看它是否包含任何数据吗?您不必声明具有特定大小的StringBuilder。请参阅关于编组StringBuilder的这一部分:但是SendMessageByString中的nLen+1似乎是必需的。如果我之前没有得到长度?是的,我必须做GETTEXTLENGTH。长度是sendmessage方法所必需的。您能将其设置为任意大的值吗?比如256或其他什么,还是必须精确?(我试图帮助您避免使用get-length消息,因为在这两条消息之间,内存可能会变得古怪。)我想每次收到的文本大小都会不同。你能解释一下避免它会有什么作用吗?我以为这已经解决了它-但不,它仍然做同样的事情。如果你发布所有代码,包括这个修复和P/Invoke声明,那会很有帮助。我已经按照你的要求发布了。你是在64 bi上运行这个吗操作系统?如果是这样,问题可能是SendMessageByString的P/Invoke声明。根据,wParam和返回值都应该是IntPtr,而不是int。另外,有没有原因让您如此努力地使用ANSI版本的SendMessage?如果您在基于NT的操作系统上运行此声明(即,任何比Windows 95或98更新的操作系统),强制操作系统做额外的工作,将控件的Unicode文本转换为ANSI,然后将ANSI文本改回Unicode(因为所有.NET字符串都是Unicode)。这种转换(以及操作系统必须分配的临时缓冲区)可能是垃圾的一部分原因。请尝试将SendMessageByString的入口点更改为“SendMessage”和字符集