我有一个方法集,它使用pinvoke在另一个程序的文本框上调用WM_GETTEXT - 它工作得相当好,但我经常回到它后面的全部垃圾文本。 (ORIGINAL文本始终完好无损。)
这是随机的,我不能按要求重现它,但它经常足以成为停止。
以下是获取信息的文字。
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);
答案 0 :(得分:1)
发送WM_GETTEXT时不应忽略返回值。来自MSDN:
The return value is the number of characters copied, not including the terminating null character.
如果另一个应用程序更改了WM_GETTEXTLENGTH和WM_GETTEXT之间的控件文本(更短的内容),那么这将解释您所看到的内容:WM_GETTEXT填充了20个字符的StringBuilder的第一个(比方说)5个字符,其余的是未定义的。它可能有空字符或者它可能有垃圾(取决于你是否正在调用ANSI版本的SendMessage,这将迫使操作系统代表你分配一个可能是垃圾填充的临时缓冲区),但不管怎样,你需要在使用字符串之前将它们剥离。
您需要读取SendMessageByString调用的返回值,并在使用之前将StringBuilder截断为该长度。
答案 1 :(得分:1)
在我看来,您的错误是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 wParam
必须包含最大数量要复制的字符,包括终止空字符。因此WM_GETTEXT
消息的正确参数必须为nLen + 1
而不是nLen
。
更大缓冲区的用法为nLen
我找到了最好的方法。我建议你将缓冲区分配至少2个字符作为nLen
返回的WM_GETTEXTLENGTH
值,并使用nLen + 2
作为WM_GETTEXT
的参数(具体到你有多大)缓冲区大小)。如果WM_GETTEXT
的返回值为nLen
或更少,则可以确保返回的字符串包含您要读取的全文。如果WM_GETTEXT
的结果为nLen + 1
,则会在发送WM_GETTEXTLENGTH
和WM_GETTEXT
消息之间更改文本,您应该重复从{{1}开始的所有步骤再一次知道新的文字大小。
答案 2 :(得分:1)
似乎有一些奇怪的事情,它看起来像你所针对的控件,当使用P / Invoke通过WM_GETTEXT返回垃圾...我建议以下,而不是返回整个缓冲区,返回当前行,这会让事情变得更加快捷......
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的棘手部分是,缓冲区的第零个位置必须包含char的长度 - 因此strBuffer.Append(Convert.ToChar(nLineLen))
的用法,以及指定的stringbuilder的Length
属性。
以下是上述P / Invoke所需的常量