C# 来自pinvoke WM_GETTEXT的垃圾角色
我有一个方法集,它使用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
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”和字符集