null написал: Друзья, подскажите пожалуйста можно ли как-то прочитать таблицу новостей QUIK через LUA ?
Можно через библиотеку w32.dll функциями FindWindow и FindWindowEx рекурсивно перебрать все дочерние открытые окна в Quik и найти окно новостей по имени класса «InfoNews». Оттуда уже из его дочернего окна списка вытащить новости.
null написал: Друзья, подскажите пожалуйста можно ли как-то прочитать таблицу новостей QUIK через LUA ?
Можно через библиотеку w32.dll функциями FindWindow и FindWindowEx рекурсивно перебрать все дочерние открытые окна в Quik и найти окно новостей по имени класса «InfoNews». Оттуда уже из его дочернего окна списка вытащить новости.
Спасибо получилось. Может кому пригодится список хендлов их очередность для чтения. Стрелочка показывает родителя.
null написал: Может кому пригодится список хендлов
«хендлы» — это указатели, список их значений смысла не имеет, они всегда разные... при повторном запуске Quik, повторном создании окна и т.д... привязываться можно только к строковым именам окон и их классов...
null написал: Может кому пригодится список хендлов
«хендлы» — это указатели, список их значений смысла не имеет, они всегда разные... при повторном запуске Quik, повторном создании окна и т.д... привязываться можно только к строковым именам окон и их классов...
Кстати может знаете как из MultiList получить список строк? Чтобы прочесть новость, в мультилисте надо даблкликнуть по новости, тогда она только появится RichEdit20A. Что-то мне костыльная затея по считыванию числа новостей из заголовка и вычисление высоты строки делением высоты контейнера на количество новостей, и эмуляцию координат дабл клика не очень нравится(и не факт, что будет работать, слишком дробная высота строки получается). Может у списка есть какие-то служебные сообщения.
null написал: Кстати может знаете как из MultiList получить список строк?
Это, по-видимому, какой-то самописный класс со своими закрытыми сообщениями. Возможно, к нему есть возможность сделать запрос, но нужно знать коды этих самых сообщений. Можно конечно повозиться с Spy++ и попытаться отследить коды сообщений, но лучше чтобы сама тех. поддежка Quik их предоставила.
null написал: Кстати может знаете как из MultiList получить список строк?
Это, по-видимому, какой-то самописный класс со своими закрытыми сообщениями. Возможно, к нему есть возможность сделать запрос, но нужно знать коды этих самых сообщений. Можно конечно повозиться с Spy++ и попытаться отследить коды сообщений, но лучше чтобы сама тех. поддежка Quik их предоставила.
SPY++ не ловит. Пробовал. Да это какой-то самописный класс. Поддержка наверное не даст коды сообщений.
Если такую простыню кидаете, хотя бы под спойлейр её засунули...
Spy++ всё ловит нормально, он либо ловит, либо нет... проверьте, что «Unknown» тип сообщений стоит в настройках. Вот к примеру, перехватил такие сообщения:
Код
<000129> 003C0EF6 P message:0x0118 [Unknown] wParam:0000FFFA lParam:0022B5A4
<000398> 000F035C S message:0x0090 [Unknown] wParam:00000000 lParam:00000000
<000399> 000F035C R message:0x0090 [Unknown] lResult:00000000
Но... это не поможет. Я понаблюдал, при добавлении новых новостей в список, нет никаких сообщений, только WM_PAINT и пр. То-есть, этот самописный контрол работает через прорисовку данных из какой-то внутренней структуры Quik, и никакого отдельного интерфейса к окну MultiList видимо нет, вся логика внутри самого окна. Поэтому, нечего там перехватывать.
Соответственно, пока вижу такой способ. Нужно просто взять размер окна этого MultiList, и затем попиксельно пройтись по нему WM_LBUTTONDOWN прокручивая страницу WM_KEYDOWN(VK_NEXT), одновременно перехватывая обновление окна RichEdit20A. И таким образом, собрать все новости.
Если такую простыню кидаете, хотя бы под спойлейр её засунули...
Spy++ всё ловит нормально, он либо ловит, либо нет... проверьте, что «Unknown» тип сообщений стоит в настройках. Вот к примеру, перехватил такие сообщения:
Но... это не поможет. Я понаблюдал, при добавлении новых новостей в список, нет никаких сообщений, только WM_PAINT и пр. То-есть, этот самописный контрол работает через прорисовку данных из какой-то внутренней структуры Quik, и никакого отдельного интерфейса к окну MultiList видимо нет, вся логика внутри самого окна. Поэтому, нечего там перехватывать.
Соответственно, пока вижу такой способ. Нужно просто взять размер окна этого MultiList, и затем попиксельно пройтись по нему WM_LBUTTONDOWN прокручивая страницу WM_KEYDOWN(VK_NEXT), одновременно перехватывая обновление окна RichEdit20A. И таким образом, собрать все новости.
Я видел эти юзер сообщения, они ничего не дают. Размер скрол ареа получить у меня не получилось, а функции получения размера показывают только видимый размер. Так бы легко вычислил размер строки, проскролил, и сэмулировал координаты дабл клика по каждой строке.
Код
const LPRECT rect = new tagRECT();
GetClientRect(hMulti, rect);
const PWINDOWINFO info = new tagWINDOWINFO();
GetWindowInfo(hMulti, info);
Suntor написал: GetScrollInfo попробуйте, может выцепит размер?...
Как раз его пробовал на MultiView, 0 возвращает.
Не поленился, проверил... всё отлично возвращает.
GetScrollInfo для SB_VERT возвращает в SCROLLINFO::nMax вертикальный размер всей клиентской области в пикселах. GetScrollBarInfo для OBJID_VSCROLL возвращает в SCROLLBARINFO::dxyLineButton высоту ползунка в пикселах, и она для окна MultiList удачно совпадает с высотой строки в пикселах.
Делим одно на другое, и получаем количество строчек в окне новостей. Ну а дальше, уже дело техники...
GetScrollInfo для SB_VERT возвращает в SCROLLINFO::nMax вертикальный размер всей клиентской области в пикселах. GetScrollBarInfo для OBJID_VSCROLL возвращает в SCROLLBARINFO::dxyLineButton высоту ползунка в пикселах, и она для окна MultiList удачно совпадает с высотой строки в пикселах.
У меня по нулям возвращает...
Код
const LPSCROLLINFO lpsi = new tagSCROLLINFO();
GetScrollInfo(hMulti, SB_VERT, lpsi);
const PSCROLLBARINFO sbi = new tagSCROLLBARINFO();
GetScrollBarInfo(hMulti, OBJID_VSCROLL, sbi);
Делим одно на другое, и получаем количество строчек в окне новостей.
Хотелось бы так считать, но Новостей 402, а при делении получаем 337.
У меня совпало... высота 17 пикселей...
Ну тогда, по-другому можно попробовать. Нужно нажать один раз кнопку полосы прокрутки вниз, чтобы на одну новость прокрутило. И повторно вызвать GetScrollInfo. В SCROLLINFO::nPos тогда уже будет точное значение высоты строки. Вручную попробуйте сначала, на одну новость прокрутить. Запустить тот же код. И посмотреть значение nPos, проверьте расчёт количества nMax/nPos.
Suntor написал: Ну тогда, по-другому можно попробовать. Нужно нажать один раз кнопку полосы прокрутки вниз, чтобы на одну новость прокрутило. И повторно вызвать GetScrollInfo. В SCROLLINFO::nPos тогда уже будет точное значение высоты строки. Вручную попробуйте сначала, на одну новость прокрутить. Запустить тот же код. И посмотреть значение nPos, проверьте расчёт количества nMax/nPos.
Даже не так... сам подход неправильный. Будет ошибка округления набегать в любом случае, nPos нужно считать делением всей высоты на количество, а не наоборот.
Короче, нужно из заголовка окна из текста вытаскивать количество. Из nMax вытаскивать высоту клиентской области. Делить второе на первое, и округлять до целого.
То-есть, формула высоты прокрутки строки: h = (int)((double)nMax/(double)n+0.5); У меня, к примеру, получается: (17930/1083 + 0.5) = (16.56+0.5) = 17 Как раз совпадает с тем, что возвращает в nPos при прокрутке на одну строчку.
Да и вообще, не нужны эти заморочки. Можно проще сделать. Нужно попиксельно пройтись по видимой части клиентской области нажимая мышкой, затем прокрутить страницу PGDOWN, и снова и т.д. до конца. При этом нужно следить за размером nMax, если он изменился в ходе обхода, то прервать процесс и начать заново, так как новая новость пришла. Дальше, держать в памяти количество собранных новостей и nMax, и добавлять уже новые по мере прихода.
Вот сделал более менее рабочий вариант. Но есть глюки, если первая новость помечена не прочитанной, то читает ее только со второго раза. Конец новостей определяет криво, с каждым разом отодвигает ее дальше. Хотелось бы еще считать кроме текста новости заголовок Новости, время, агенство.
null написал: Нашел способ как найти высоту строки. Но столкнулся с другой проблемой сообщения дабл клика приходят в окно, но новость не обновляется
Код
//Вычисляем высоту линии
SetScrollPos(hMulti, SB_VERT, 0 , true );
SendMessage(hMulti, WM_VSCROLL, SB_LINEDOWN, 0 );
const size_t height = GetScrollPos(hMulti, SB_VERT);
Собственно, я об этом и написал перед этим... прокрутить на одну строчку вниз, и посмотреть nPos в GetScrollInfo, ну или как вы получаете тоже значение через GetScrollPos.
Цитата
null написал: Вот сделал более менее рабочий вариант.
Мягко говоря... совсем «код новичка» так сказать. Ну по порядку:
Зачем?... в эту ф-цию обратного вызова и так идут все хендлы детей и их детей и т.д.... не нужно в ней ещё поддетей детей проверять... здесь нужно взять имя класса через GetClassName и сравнить его с "InfoNews".
Вот честно не нравится мне это... совсем... Мне кажется это нужно по-другому вообще сделать. Нужно сначала SetFocus() на MultiList сделать, а потом вообще клавиатурой пройтись по всему списку нажимая кнопки VK_DOWN и VK_RETURN. Так с ходу не могу сказать, нужно экспериментировать... но мне кажется, что это лучше, чем вычисление координат и тыкание мышкой...
SCROLLINFO * si = new SCROLLINFO();
wchar_t * title = new wchar_t[tlen + 1 ];
RECT * rect = new RECT();
wchar_t * buff = new wchar_t[current_LEN + 1 ];
Ну и наконец — самое главное — «гвоздь „программы”» в прямом и переносном смысле: где delete??? Вы везде ставите new, в том числе и в цикл его засунули, но при этом не делаете delete. У вас в программе сплошные утечки памяти идут. Да и не нужны new для тех же структур WinAPI, зачем?... все эти структуры локально на стеке выделить и проинициализировать:
Код
SCROLLINFO si = {sizeof(SCROLLINFO)};
si.fMask = SIF_ALL;
if (!GetScrollInfo(hMulti, SB_VERT, &si)) /*ошибка*/;
...
RECT rect;
if (!GetClientRect(hMulti, &rect)) /*ошибка*/;
и вызовы самих ф-ций на ошибки тоже нужно проверять... ну и т.д.
Зачем?... в эту ф-цию обратного вызова и так идут все хендлы детей и их детей и т.д.... не нужно в ней ещё поддетей детей проверять... здесь нужно взять имя класса через GetClassName и сравнить его с "InfoNews".
Вот честно не нравится мне это... совсем... Мне кажется это нужно по-другому вообще сделать. Нужно сначала SetFocus() на MultiList сделать, а потом вообще клавиатурой пройтись по всему списку нажимая кнопки VK_DOWN и VK_RETURN.Ну и наконец — самое главное — «гвоздь „программы”» в прямом и переносном смысле: где delete??? Вы везде ставите new, в том числе и в цикл его засунули, но при этом не делаете delete. У вас в программе сплошные утечки памяти идут. Да и не нужны new для тех же структур WinAPI, зачем?... все эти структуры локально на стеке выделить и проинициализировать:
Код
SCROLLINFO si = {sizeof(SCROLLINFO)};
si.fMask = SIF_ALL;
if ( ! GetScrollInfo(hMulti, SB_VERT, & si)) / * ошибка * /;
.. .
RECT rect;
if ( ! GetClientRect(hMulti, & rect)) / * ошибка * /;
и вызовы самих ф-ций на ошибки тоже нужно проверять... ну и т.д.
Зачем? был какой-то глюк со свернутым окном, просто не находил InfoNews. Это еще не программа, это лишь прототип в консоли для проверки, всегда освобождаю. Насчет VK_DOWN и VK_RETURN попробую.
Я не знаю, можно ли через LUA разбирать текстовые файлы. Лет 5 назад надо было парсить появление новостей в квике. В директории квика есть файл с новостями. Я вызывал системный класс, ловящий изменение файла в системе. Получив событие просто текстово парсил файл. Какой именно сейчас не вспомню, он увесистый, если в настройках выставить хранить новости несколько дней то ббудет несколько мегабайт. Я не разбирался как там раскодировать дату сообщений, но заголовок и текст довольно легко достаются. Помоему это куда проще чем то что вы описываете выше.
Покопался в архивах, раньше файл назывался news.log, возможно название изменилось. Файл начинается с INFONEWS, строки оканчиваются нулём. Вот немного полезной инфы про отслеживания за изменениями файлов в директории https://habr.com/post/164775/
Юрий написал: Я не знаю, можно ли через LUA разбирать текстовые файлы. Лет 5 назад надо было парсить появление новостей в квике. В директории квика есть файл с новостями. Я вызывал системный класс, ловящий изменение файла в системе. Получив событие просто текстово парсил файл. Какой именно сейчас не вспомню, он увесистый, если в настройках выставить хранить новости несколько дней то ббудет несколько мегабайт. Я не разбирался как там раскодировать дату сообщений, но заголовок и текст довольно легко достаются. Помоему это куда проще чем то что вы описываете выше.
Тоже думал над этим, спасибо. Бегло прошелся хекс редактором, показалось там не полный текст новости. Попробую на досуге еще раз поковырять файл. Дата вероятно там упаковывается в DWORD.
Юрий написал: Покопался в архивах, раньше файл назывался news.log, возможно название изменилось. Файл начинается с INFONEWS, строки оканчиваются нулём.
Идея хорошая... смущает только одна вещь. Посмотрел у себя этот файл, и такое впечатление, что там только заголовки новостей и те новости, которые читались. То-есть, если в окне новостей щёлкнуть на новость и её текст появится в RichEdit20A, то он сохраняется в news.log. А если новость не читали, то её нет в news.log.
Вот самый стабильный вариант. Осталось решить одну проблему - первые пару новых не прочитанных новостей читаются пустым, при повторном запуске читаются нормально. Хотя не особо проблема. Принцип работы с заголовка парсится количество новостей(в окне). Дальше перебираем каждую строку до количества новостей. Осталось спарсить дату, агенство, заголовок. Есть идеи?
Юрий написал: Покопался в архивах, раньше файл назывался news.log, возможно название изменилось. Файл начинается с INFONEWS, строки оканчиваются нулём.
Идея хорошая... смущает только одна вещь. Посмотрел у себя этот файл, и такое впечатление, что там только заголовки новостей и те новости, которые читались. То-есть, если в окне новостей щёлкнуть на новость и её текст появится в RichEdit20A, то он сохраняется в news.log. А если новость не читали, то её нет в news.loЯ
Я точно не помню, посмотрите в настройках квика, нет ли там опции читать все новости с сервера и сохранять. Я тело новости не парсил, мне нужны были только заголовки.
1. Не злоупотребляйте new/delete операциями. Это очень тяжёлые ресурсоёмкие операции, которые приводят к вызову системных ф-ций выделения памяти из кучи и пр. При этом исполняется код объёмом под 150 КБ... поэтому, всегда нужно стараться использовать обычные переменные на стеке, память под которые выделяется всего одной инструкцией процессора записи в регистр указателя стека... Я уже выше писал об этом про структуры. Но тоже самое касается и массивов с константным размером. Например ваш код:
2. Максимальный размер имени класса окна в WNDCLASSEX равен 256, а не 128.
3. Ещё раз посмотрел весь код, и та же мысль... ну не так. Вообще не нужно количество новостей из заголовка вытаскивать. И мышка не нужна. И координаты не нужны. Нужно: • нажать Ctrl+End (окно прокрутится вниз, и выделится самая последняя новость), а затем Enter (новость появится в RichEdit20A); • далее в цикле идти вверх, нажимать UpArrow+Enter, читая новости и считая их количество и следя за сдвигом ползунка; • в самом верху, как только позиция ползунка обнулилась, выходим из цикла; В итоге, все новости прочитаны и их количество подсчитано. Да ещё в придачу, если в момент нашего цикла пришла новая новость, то ничего страшного, к моменту прихода нашего цикла вверх, она также обработается и подсчитается.
4. Про разбор даты, агентства и заголовка. Посмотрел формат там у всех агентств разный. Поэтому, если и делать разбор, то нужно сначала пробежать по новости, и поискать там ключевые слова: «ПРАЙМ», «Interfax» и пр.. То-есть, определить новостное агентство. И уже затем, для каждого из них, разбирать отдельно. У Интерфакса, за вычетом пустых строк, идёт в первой строчке дата, во второй ключ. слова, в следующей заголовок, и далее тело. Для ПРАЙМ всё слитно, вначале дата, и далее сразу тело новости без заголовка. Ну и т.д. Тут конечно можно поизвращаться, но что-то красивое и универсальное не сделать, скорее всего...
Юрий написал: Я точно не помню, посмотрите в настройках квика, нет ли там опции читать все новости с сервера и сохранять. Я тело новости не парсил, мне нужны были только заголовки.
Есть флажок «Запрашивать тело новости вместе с заголовком». Но надо проверять, то или не то. Может и запрашивает, но в файл news.log не будет сохранять.