调用 SendMessage (P/Invoke) 不断崩溃

发布于 2024-08-20 02:25:09 字数 4482 浏览 5 评论 0原文

我必须编写一个与第三方程序通信的应用程序(AOL,我'抱歉。:()

做了很多研究,我发现了一些方法可以使用 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);
}

I am having to write an application that communicates with a third-party program (AOL, I'm sorry. :()

Doing a lot of research I found some ways to do this with P/Invoke, and for the most part it works okay, but it crashes upon subsequent trials, specifically with SendMessage. I'm outlining the crashing code below.

All of this was ported to .NET from old, old Visual Basic files. It's archaic as it can be, and I understand if it's not doable - I was just hoping there was a better way than Visual Basic 4.0 to get this done.

[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);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

惟欲睡 2024-08-27 02:25:09

您正在使用宽字节 SendMessage..ie 来表示宽字符,您是否尝试过普通的 Sendmessage..
public static extern int SendMessage(IntPtr hWnd, UInt32 Msg, Int32 wParam, Int32 lParam);

我还注意到,就像您试图根据 RichTextBox 控件的句柄更改值一样,因此在另一个进程中查找 AOL 的客户端窗口...这是正确的吗?

这可能是问题的根源,直接修改属于不属于您的窗口的控件(您的程序是托管的,修改非托管进程的窗口)......这可以解释它崩溃的原因。您能澄清一下十六进制常量的用途吗?

编辑:当您使用 WM_GETTEXTLENGTH 和 WM_GETTEXT 时,它们是 Windows 消息的一部分,用于从控件中检索文本长度和实际文本。如果您查看此处,看看 pinvoke.net 需要做什么说说它们..当您使用 WM_GETTEXTLENGTH 和 WM_GETTEXT 发出“SendMessage”时,您是在告诉 Windows - '嘿,请告诉我我在参数 n 中给您的关联句柄中文本的长度。我突然想到,值得尝试...我会摆脱那些 SendMessage pinvokes 并只使用这个...

[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, StringBuilder lParam);
//If you use '[Out] StringBuilder', initialize the string builder with proper length first.
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); // this is the line that keeps crashing on me.

尝试一下然后回到这里...:)

希望这会有所帮助,
此致,
汤姆.

You are using the Wide byte SendMessage..ie for Wide Characters, have you tried the normal Sendmessage..
public static extern int SendMessage(IntPtr hWnd, UInt32 Msg, Int32 wParam, Int32 lParam);

I also notice it's like as if you are trying to change the value based on the handle of the richtextbox's control hence the looking around for the AOL's client window in another process...is that correct?

That could be the source of the problem, directly modifying a control that belongs to a window that is not yours (your program is managed, modifying a unmanaged process's window)...that could explain why it crashed. Can you clarify what is the hex constants for?

Edit: When you use the WM_GETTEXTLENGTH and WM_GETTEXT, they are part of the Windows Messages to retrieve the text length and the actual text from the control. If you look here and see what pinvoke.net has to say about them..When you issue a 'SendMessage', with WM_GETTEXTLENGTH and WM_GETTEXT, you are telling Windows - 'Hey, get me the length of the text in that associated handle which I've given you in the parameter n. Just occurred to me, worth trying out...I would get rid of those SendMessage pinvokes and use just this one..

[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, StringBuilder lParam);
//If you use '[Out] StringBuilder', initialize the string builder with proper length first.
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); // this is the line that keeps crashing on me.

Try that and get back here ... :)

Hope this helps,
Best regards,
Tom.

不念旧人 2024-08-27 02:25:09

您是否设法解决“尝试读取或写入受保护的内存”问题。错误? 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();
}

Did you manage to solve the "Attempted to read or write protected memory." error? t0mm13b's answer seems to allocate a StringBuilder whose buffer is one character too small (to accommodate the trailing '\0').

Here's code that works for me:

[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();
}
‘画卷フ 2024-08-27 02:25:09

在类似的情况下,我从 Marshal.PtrToStringUni(bf) 语句中崩溃,其中 SendMessage 使用 WM_GETTEXTLENGTH 返回文本长度的“错误大小” code> 参数(控制类为“RICHEDIT50W”;多行文本)。

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

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

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

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

I was getting a crash from Marshal.PtrToStringUni(bf) statement in similar situation where SendMessage was returning a "wrong size" for a text length with WM_GETTEXTLENGTH argument (the control class was "RICHEDIT50W"; multi-line text).

I had tried adding 1, 10, 100 (to text length query result) and still would get an error even though (later on) the text length was equal what was returned from the first call (WM_GETTEXTLENGTH).

My solution was: I multiplied the result with 2 then I trimmed it.

I did use Marshal.AllocHGlobal(sz) and then Marshal.Release(bf), so there was no problem with memory efficiency. My guess is that for multi-line texts Marshal.AllocHGlobal(sz) wasn't making enough space in the memory even with exact text size (+1).

Maybe the return character within the text (vbCr, vbLf) requires more memory: I found nothing to explain this isue, but doubling the size worked for me.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文