来自 pinvoke WM_GETTEXT 的垃圾字符
我有一个方法集,使用 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这似乎发生了一些奇怪的事情,看起来您所瞄准的控件在通过 WM_GETTEXT 使用 P/Invoke 时返回垃圾...我建议执行以下操作,而不是返回整个缓冲区,而是返回当前行,这将使事情变得更快捷...
通过这种方式,我们获得:
nLineCount
nLineCount
-nIndex
nIndex
获取行长 -nLineLen
使用
nLineLen
,然后我们可以设置 StringBuilder 缓冲区,使用 EM_GETLINE 的棘手部分是,缓冲区的第 0 个位置必须包含 char 的长度 - 因此使用strBuffer.Append(Convert. ToChar(nLineLen))
,并指定字符串构建器的Length
属性。以下是上述 P/Invoke 所需的常量
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...
In that way, we obtain:
nLineCount
nLineCount
-nIndex
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 ofstrBuffer.Append(Convert.ToChar(nLineLen))
, and the stringbuilder'sLength
property specified.Here are the constants required for the above P/Invoke
发送 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:
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.
在我看来,您的错误在于错误使用了
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_GETTEXTLENGTH
和WM_GETTEXT
之间文本会发生变化消息,您应该再次重复以WM_GETTEXTLENGTH
开头的所有步骤,以了解新的文本大小。It seems to me that your error is in the wrong usage of one parameter of
WM_GETTEXT
message. You should usenLen + 1
instead ofnLen
as thewParam
.At the beginning you use
WM_GETTEXTLENGTH
to getnLen
, which will be the number of TCHARs copied, not including the terminating null character. Then you allocate the buffer of the sizenLen + 1
characters. There steps are absolutely correctly, but then you sendWM_GETTEXT
withnLen
as thewParam
which is wrong, because corresponds to http://msdn.microsoft.com/en-us/library/ms632627.aspxwParam
must contain the maximum number of characters to be copied, including the terminating null character. So the correct parameter ofWM_GETTEXT
message must benLen + 1
instead ofnLen
.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 thenLen
value returned byWM_GETTEXTLENGTH
and usenLen + 2
as the parameter ofWM_GETTEXT
(exactly how large are you buffer size). If the returned value of theWM_GETTEXT
arenLen
or less, then you can be sure that the returned string contain full text which you want to read. If the result ofWM_GETTEXT
will benLen + 1
, then the text are changed between sending ofWM_GETTEXTLENGTH
andWM_GETTEXT
messages and you should repeat all the steps starting withWM_GETTEXTLENGTH
one more time to know the new text size.