调用SendMessage(P / Invoke)会一直崩溃

我不得不写一个与第三方程序通信的应用程序( 美国在线 ,对不起。:()

做了大量的研究我发现了一些使用P / Invoke做到这一点的方法,并且在大多数情况下它可以正常工作,但它会在随后的试验中崩溃,特别是使用SendMessage 。 我正在概述下面的崩溃代码。

所有这些都是从旧的,旧的Visual Basic文件移植到.NET 。 这是古老的,我知道如果它不可行 – 我只是希望有一种比Visual Basic 4.0更好的方法来完成这项工作。

 [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool CloseHandle(IntPtr hObject); [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow); [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, IntPtr windowTitle); [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(HandleRef hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", EntryPoint="SendMessageW")] public static extern IntPtr SendMessageByString(HandleRef hWnd, UInt32 Msg, IntPtr wParam, StringBuilder lParam); [DllImport("user32.dll", CharSet = CharSet.Unicode , EntryPoint = "SendMessageW")] public static extern IntPtr SendMessageByString(HandleRef hWnd, UInt32 Msg, IntPtr wParam, String lParam); public IntPtr FindClientWindow() { IntPtr aol = IntPtr.Zero; IntPtr mdi = IntPtr.Zero; IntPtr child = IntPtr.Zero; IntPtr rich = IntPtr.Zero; IntPtr aollist = IntPtr.Zero; IntPtr aolicon = IntPtr.Zero; IntPtr aolstatic = IntPtr.Zero; aol = Invoke.FindWindow("AOL Frame25", null); mdi = Invoke.FindWindowEx(aol, IntPtr.Zero, "MDIClient", null); child = Invoke.FindWindowEx(mdi, IntPtr.Zero, "AOL Child", null); rich = Invoke.FindWindowEx(child, IntPtr.Zero, "RICHCNTL", null); aollist = Invoke.FindWindowEx(child, IntPtr.Zero, "_AOL_Listbox", null); aolicon = Invoke.FindWindowEx(child, IntPtr.Zero, "_AOL_Icon", null); aolstatic = Invoke.FindWindowEx(child, IntPtr.Zero, "_AOL_Static", null); if (rich != IntPtr.Zero && aollist != IntPtr.Zero && aolicon != IntPtr.Zero && aolstatic != IntPtr.Zero) return child; do { child = Invoke.FindWindowEx(mdi, child, "AOL Child", null); rich = Invoke.FindWindowEx(child, IntPtr.Zero, "RICHCNTL", null); aollist = Invoke.FindWindowEx(child, IntPtr.Zero, "_AOL_Listbox", null); aolicon = Invoke.FindWindowEx(child, IntPtr.Zero, "_AOL_Icon", null); aolstatic = Invoke.FindWindowEx(child, IntPtr.Zero, "_AOL_Static", null); if (rich != IntPtr.Zero && aollist != IntPtr.Zero && aolicon != IntPtr.Zero && aolstatic != IntPtr.Zero) return child; } while (child != IntPtr.Zero) ; return child; } IntPtr room = IntPtr.Zero; IntPtr child = IntPtr.Zero; IntPtr length = IntPtr.Zero; IntPtr roomHandle = IntPtr.Zero; child = FindClientWindow(); room = FindChildByClass(child, "RICHCNTLREADONLY"); HandleRef n = new HandleRef(IntPtr.Zero, room); length = SendMessage(n, 0x000E, IntPtr.Zero, IntPtr.Zero); // This is the line that keeps crashing on me. SendMessageByString(n, 0x000D, new IntPtr( length.ToInt32() + 1 ), str); public IntPtr FindChildByClass(IntPtr parent, string child) { return Invoke.FindWindowEx(parent, IntPtr.Zero, child, null); } 

你是使用宽字节SendMessage..ie广角字符,你尝试过正常的Sendmessage .. public static extern int SendMessage(IntPtr hWnd, UInt32 Msg, Int32 wParam, Int32 lParam);

我也注意到它就好像你试图根据richtextbox控件的句柄改变值,因此在另一个进程中寻找AOL的客户端窗口……这是正确的吗?

这可能是问题的根源,直接修改属于不属于您的窗口的控件(您的程序被管理,修改非托管进程的窗口)……这可以解释它崩溃的原因。 你能澄清什么是hex常数吗?

编辑:当您使用WM_GETTEXTLENGTH和WM_GETTEXT时,它们是Windows消息的一部分,用于从控件中检索文本长度和实际文本。 如果你看看这里 ,看看pinvoke.net对他们有什么看法……当你发出一个’SendMessage’,用WM_GETTEXTLENGTH和WM_GETTEXT,你告诉Windows – ‘嘿,告诉我相关句柄中文本的长度我已经在参数n给你了。 刚发生在我身上,值得一试…我会摆脱那些SendMessage pinvokes并使用这一个..

 [DllImport(“user32.dll”,CharSet = CharSet.Auto)]
 static extern IntPtr SendMessage(IntPtr hWnd,UInt32 Msg,IntPtr wParam,StringBuilder lParam);
 //如果使用'[Out] StringBuilder',请首先使用适当的长度初始化字符串构建器。

 child = FindClientWindow();
 room = FindChildByClass(child,“RICHCNTLREADONLY”);

 length = SendMessage(n,0x000E,IntPtr.Zero,IntPtr.Zero);

 StringBuilder sbBuf = new StringBuilder(length);

 SendMessageByString(room,0x000D,new IntPtr(length.ToInt32()+ 1),out sbBuf);  //这是一直让我崩溃的线。

试试看,回到这里…… 🙂

希望这会有所帮助,最好的问候,汤姆。

您是否设法解决“尝试读取或写入受保护的内存”。 错误? t0mm13b的答案似乎是分配一个StringBuilder其缓冲区是一个字符太小(以容纳尾随’\ 0’)。

这里的代码对我有用:

 [DllImport("user32.dll", EntryPoint = "SendMessage", SetLastError = true)] private static extern Int32 SendMessageByString(IntPtr wnd, UInt32 msg, Int32 WParam, StringBuilder output); const int WM_GETTEXTLENGTH = 0x000e; const int WM_GETTEXT = 0x000d; public static string GetText(IntPtr hWnd) { int len = SendMessageByString(hWnd, WM_GETTEXTLENGTH, 0, null); var sb = new StringBuilder(len + 1); // +1 is for the trailing '\0' SendMessageByString(hWnd, WM_GETTEXT, sb.Capacity, sb); return sb.ToString(); } 

我在Marshal.PtrToStringUni(bf)声明中遇到类似情况的崩溃,其中SendMessage使用WM_GETTEXTLENGTH参数返回文本长度的“错误大小”(控件类为“RICHEDIT50W”;多行文本)。

我曾尝试添加1,10,100(文本长度查询结果),即使(稍后)文本长度等于从第一次调用( WM_GETTEXTLENGTH )返回的内容,仍会出现错误。

我的解决方案是:我将结果乘以2然后我修剪它。

我确实使用了Marshal.AllocHGlobal(sz)然后使用Marshal.Release(bf) ,因此内存效率没有问题。 我的猜测是,对于多行文本, Marshal.AllocHGlobal(sz)在内存中没有足够的空间,即使是精确的文本大小(+1)。

也许文本中的返回字符(vbCr,vbLf)需要更多的内存:我没有发现任何可以解释这个问题,但是大小加倍对我有用。