Вызов SendMessage (P / Invoke) продолжает падать - PullRequest
4 голосов
/ 02 февраля 2010

Мне нужно написать приложение, которое связывается со сторонней программой ( AOL , извините.: ()

Проведя много исследований, я нашел несколько способов сделать это с P / Invoke , и по большей части это работает хорошо, но оно падает при последующих испытаниях, особенно SendMessage. Я обрисовываю код сбоя ниже.

Все это было перенесено на .NET из старых, старых файлов Visual Basic. Это архаично, насколько это возможно, и я понимаю, что если это невозможно, я просто надеялся, что для этого есть лучший способ, чем 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);
}

Ответы [ 3 ]

2 голосов
/ 02 февраля 2010

Вы используете WideMate 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 может сказать о них .. Когда вы запускаете 'SendMessage', с WM_GETTEXTLENGTH и WM_GETTEXT, вы говорите Windows - 'Эй, найдите мне длину текст в связанном дескрипторе, который я дал вам в параметре n. Просто пришло мне в голову, стоит попробовать ... Я бы избавился от этих pinvokes SendMessage и использовать только этот ..

[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.

Попробуйте и вернитесь сюда ...:)

Надеюсь, это поможет, С наилучшими пожеланиями, Том.

0 голосов
/ 07 октября 2012

Я получал сбой от оператора 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) требуется больше памяти: я не нашел ничего, чтобы объяснить это, но удвоение размера мне помогло.

0 голосов
/ 21 декабря 2011

Удалось ли вам решить «Попытка чтения или записи защищенной памяти». ошибка? Ответ 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();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...