Форум по Delphi программированию

Delphi Sources



Вернуться   Форум по Delphi программированию > Все о Delphi > [ "Начинающим" ]
Ник
Пароль
Регистрация <<         Правила форума         >> FAQ Пользователи Календарь Поиск Сообщения за сегодня Все разделы прочитаны

Ответ
 
Опции темы Поиск в этой теме Опции просмотра
  #16  
Старый 24.04.2009, 17:37
Nyctos Kasignete Nyctos Kasignete вне форума
Активный
 
Регистрация: 29.03.2009
Сообщения: 300
Репутация: 94
По умолчанию

Я вернулась чуть раньше =)
И пример притащила... Всё оказывается несколько сложнее, чем я изначально полагала! Так вот просто забрать содержимое ListView из чужого окна путем посылки ему сообщения LVM_GETITEMTEXT не получится. Нужно свершать дополнительные манипуляции по выделению памяти под текстовый буфер в чужом процессе.
Сразу говорю, что я воспользовалась этим примером (и авторство себе не присваиваю). Но я заточила тот пример под наши конкретные нужды и написала процедуру, считывающую всё содержимое ListView в объект StringGrid, который передается процедуре как параметр по ссылке. Это может быть как реально существующий на форме StringGrid, так и динамически создаваемый (runtime) с помощью конструктора Create. Просто, думаю, со StringGrid очень удобно работать в дальнейшем, анализируя содержимое.
Вот как выглядит процедура:
Код:
procedure TForm1.GetListViewData(LVHandle: HWND; ColumnCount: Integer;
                                 var DataGrid: TStringGrid);
var
  hProcess: THandle;
  dwProcessID: DWORD;
  dwBytesWriten: DWORD;
  nItemCount: Integer;
  i, j, nTextLength: Integer;
  plviRemoteLVItem: PLVItem;
  lviRemoteLVItem: LV_ITEM;
  pszText: PChar;
  svText: ShortString;
begin
  if LVHandle = 0 then Exit;
  dwProcessID := 0;
  nItemCount := ListView_GetItemCount(LVHandle);
  GetWindowThreadProcessId(LVHandle, @dwProcessID);
  if dwProcessID = 0 then Exit;
  hProcess := OpenProcess(PROCESS_ALL_ACCESS, True, dwProcessID);
  if hProcess = 0 then Exit;
  pszText := VirtualAllocEx(hProcess, nil, 255,
                            MEM_COMMIT or MEM_TOP_DOWN, PAGE_READWRITE);
  if GetLastError <> 0 then Exit;
  plviRemoteLVItem := VirtualAllocEx(hProcess, nil, SizeOf(LV_ITEM),
                                     MEM_COMMIT or MEM_TOP_DOWN, PAGE_READWRITE);
  if GetLastError <> 0 then Exit;

  { единицу прибавляю в том предположении, 
    что есть фиксированные строка и столбец }
  DataGrid.RowCount := nItemCount + 1;
  DataGrid.ColCount := ColumnCount + 1;

  ZeroMemory(@lviRemoteLVItem, SizeOf(LV_ITEM));
  lviRemoteLVItem.mask := LVIF_TEXT;
  lviRemoteLVItem.pszText := pszText;
  lviRemoteLVItem.cchTextMax := 255;

  for i := 0 to nItemCount - 1 do
  begin
    lviRemoteLVItem.iSubItem := 0;
    if not WriteProcessMemory(hProcess, plviRemoteLVItem, @lviRemoteLVItem,
                 SizeOf(LV_ITEM), dwBytesWriten) then Exit;
    nTextLength := SendMessage(LVHandle, LVM_GETITEMTEXT,
                               i, Integer(plviRemoteLVItem));
    ZeroMemory(@svText, 255);
    ReadProcessMemory(hProcess, lviRemoteLVItem.pszText,
                      @svText[1], nTextLength, dwBytesWriten);
    DataGrid.Cells[1, i + 1] := StrPas(PChar(@svText[1]));
    for j := 1 to ColumnCount - 1 do
    begin
      lviRemoteLVItem.iSubItem := j;
      if not WriteProcessMemory(hProcess, plviRemoteLVItem, @lviRemoteLVItem,
                                SizeOf(LV_ITEM), dwBytesWriten) then Exit;
      nTextLength := SendMessage(LVHandle, LVM_GETITEMTEXT,
                                 i, Integer(plviRemoteLVItem));
      ZeroMemory(@svText, 255);
      ReadProcessMemory(hProcess, lviRemoteLVItem.pszText,
                        @svText[1], nTextLength, dwBytesWriten);
      DataGrid.Cells[j + 1, i + 1] := StrPas(PChar(@svText[1]));
    end;
  end;
  VirtualFreeEx(hProcess, pszText, 0, MEM_RELEASE);
  VirtualFreeEx(hProcess, plviRemoteLVItem, 0, MEM_RELEASE);
  CloseHandle(hProcess);
end;
Ещё чуть-чуть поясню. Процедура получает следующие параметры:
  1. Handle окна ListView (того самого, который имеет класс SysListView32). Этот дескриптор находите так же, как и раньше.
  2. Количество столбцов в этом ListView.
  3. Последний параметр, как уже сказано, — объект типа TStringGrid, который либо лежит на форме вашего собственного приложения, либо создается динамически.
Вот пример использования этой процедуры:
Код:
procedure TForm1.Button1Click(Sender: TObject);
var
  StrGrid: TStringGrid;
begin
  StrGrid := TStringGrid.Create(Self);
  try
    GetListViewData(ListViewHandle, 4, StrGrid);
    { теперь таблица StrGrid содержит всё, что было в ListView.
      Тут можно выполнять какие-то манипуляции с данными, 
      обращаясь к ним, например, так: StrGrid.Cells[3, 5] }
    // Анализ и обработка...
  finally
    StrGrid.Free;
  end;
end;
Ответить с цитированием
  #17  
Старый 24.04.2009, 20:55
одинадцатый одинадцатый вне форума
Новичок
 
Регистрация: 16.04.2009
Сообщения: 95
Репутация: 10
По умолчанию

К сожалению ни чего не получилось(

Через Trace into посмотрел программа еще вот здесь:
Код:
 
...
hProcess := OpenProcess(PROCESS_ALL_ACCESS, True, dwProcessID);
  if hProcess = 0 then Exit;
  pszText := VirtualAllocEx(hProcess, nil, 255,
                            MEM_COMMIT or MEM_TOP_DOWN, PAGE_READWRITE);
  if GetLastError <> 0 then Exit;
...
переменной pszText присваивает "". В итоге стринггрид пустой получается, хотя кол-во ичеек верное становиться.
В чем дело господа знатоки?
Ответить с цитированием
  #18  
Старый 24.04.2009, 21:44
Nyctos Kasignete Nyctos Kasignete вне форума
Активный
 
Регистрация: 29.03.2009
Сообщения: 300
Репутация: 94
По умолчанию

Ну что-то аж не знаю! Проверяю на всех окнах класса SysListView32, какие только есть у Windows, и всё работает... Уже получаю и список процессов в Диспетчере задач, и список файлов в Explorer'е, и всё, что есть в консоли mmc, — всё вытаскивается. Не понятно, почему у вас не работает.
Вы тоже проверьте при возможности на других объектах ListView, хотя бы из тех, что я перечислила...

И попробуйте еще для ясности убрать из процедуры GetListViewData вот эти строчки:
Код:
DataGrid.RowCount := nItemCount + 1;
DataGrid.ColCount := ColumnCount + 1;
И предварительно (заранее) вне этой процедуры задайте для объекта StringGrid нужное количество строк и колонок (с учетом фиксированных). Посмотрим, как будет работать без этого...

P.S. Да, и еще. Раз уж вы делаете трассировку, посмотрите заодно, как программа проходится по тем циклам for, что в процедуре GetListViewData. Уж не вылетает ли она вот на этих Exit:
Код:
if not WriteProcessMemory(hProcess, plviRemoteLVItem, @lviRemoteLVItem,
                          SizeOf(LV_ITEM), dwBytesWriten) then Exit;

Последний раз редактировалось Nyctos Kasignete, 24.04.2009 в 21:51.
Ответить с цитированием
  #19  
Старый 24.04.2009, 22:47
одинадцатый одинадцатый вне форума
Новичок
 
Регистрация: 16.04.2009
Сообщения: 95
Репутация: 10
По умолчанию

Странно но с диспетчером задач действительно работает)
Но с моей прогой - нет.
в циклах ни где не выкидывает - проходит до конца.
Ответить с цитированием
  #20  
Старый 24.04.2009, 23:25
Nyctos Kasignete Nyctos Kasignete вне форума
Активный
 
Регистрация: 29.03.2009
Сообщения: 300
Репутация: 94
По умолчанию

Эх. =(
Возможно, для полноты картины стоит проверить еще, что возвращает функция ReadProcessMemory. Если чтение памяти завершается неудачей, функция возвращает False.
То есть, можно попробовать в циклах заменить строчку
Код:
ReadProcessMemory(hProcess, lviRemoteLVItem.pszText,
                  @svText[1], nTextLength, dwBytesWriten);
на нечто вот такое:
Код:
if not ReadProcessMemory(hProcess, lviRemoteLVItem.pszText,
                         @svText[1], nTextLength, dwBytesWriten) then Exit;
В общем, так же, как в случае с WriteProcessMemory. Ну и дальше соответственно тоже проверить, не будет ли при этом происходить выброс из процедуры. Хотя это маловероятно, если уже запись прошла удачно...

И ведь мне проверять-то не на чем! O_o
Ответить с цитированием
  #21  
Старый 25.04.2009, 01:16
одинадцатый одинадцатый вне форума
Новичок
 
Регистрация: 16.04.2009
Сообщения: 95
Репутация: 10
По умолчанию

Если у вас есть возможность 5 метров скачать вот здесь лежит та прога:
http://www.alpari.ru/files/mt4setup.exe
в меню Вид нажать на терминал и вот как раз из появившегося окна мне надо вытянуть данные.

Ну все равно большое спасибо, вы мне очень помогли.
Ответить с цитированием
  #22  
Старый 26.04.2009, 09:14
одинадцатый одинадцатый вне форума
Новичок
 
Регистрация: 16.04.2009
Сообщения: 95
Репутация: 10
По умолчанию

О! Получилось, только не до конца...
Почему-то в стринггрид не записываются фиксированные ячейки(там только перавя строка) а еще внизу стринггрида есть закрашенная строка(внизу же не может быть фиксированной строки) вот так выглядит:
http://www.delphisources.ru/forum/at...1&d=1240722716
ее тоже не выводит.
И при трассировке иногда вообще ни чего не выводит, появляется ошибка, а иногда в ст выводит но при навелении на переменную psztext выглядит пустой. Почему?
строки
DataGrid.RowCount := nItemCount + 1;
DataGrid.ColCount := ColumnCount + 1;
убирал - не помогло.
Изображения
Тип файла: jpg 2412412.JPG (14.1 Кбайт, 28 просмотров)
Ответить с цитированием
  #23  
Старый 26.04.2009, 10:49
Nyctos Kasignete Nyctos Kasignete вне форума
Активный
 
Регистрация: 29.03.2009
Сообщения: 300
Репутация: 94
По умолчанию

Цитата:
Сообщение от одинадцатый
Почему-то в стринггрид не записываются фиксированные ячейки(там только перавя строка)
То, что вы называете "фиксированными ячейками" на самом деле не отностися к самому ListView. Надписи вверху ("Ордер", "Время", "Тип" и другие) принадлежат другому окну класса SysHeader32. Оно является дочерним для окна ListView. Надписи из Header Control вытаскиваются почти так же, как из ячеек ListView. Только там вместо сообщения LVM_GETITEMTEXT предусмотрено сообщение HDM_GETITEM и соответствующая макрофункция Header_GetItem.

Цитата:
Сообщение от одинадцатый
при навелении на переменную psztext выглядит пустой. Почему?
Сама ещё не очень разобралась с функцией VirtualAllocEx, если честно. Помните ведь, что код не совсем мой? =) Но, возможно, это происходит потому, что память-то выделяется в адресном пространстве другого процесса. А вообще, следить за содержимым pszText по-моему нет смысла... Ведь заполнение структуры lviRemoteLVItem происходит только после отправки сообщения LVM_GETITEMTEXT (в циклах), а чтение из памяти происходит позже — при вызове функции ReadProcessMemory сразу в буфер svText. А pszText служит лишь адресом, откуда начинать читать. Так что, думаю, смотреть нужно на содержимое вот этого: PChar(@svText[1]), а не pszText.

Цитата:
Сообщение от одинадцатый
Получилось, только не до конца...
Это значит, что у вас что-то прочиталось из окна "Терминал"?? У меня оно совсем как мертвое. Да-да, я, кстати, установила эту программу у себя... У меня из окна Терминал вообще не читается содержимое ListView. Более того, когда я явно запрашиваю количество строк в ListView с помощью функции ListView_GetItemCount (для этого нужен только handle и больше ничего), то функция возвращает ноль, как будто там все ListView пустые! (Да, там в окне Терминал не один-единственный объект ListView, а несколько — свой на каждой вкладке...). Однако из других окон (например, есть такое "Обзор рынка") всё считывается спокойно.
Цитата:
Сообщение от одинадцатый
а еще внизу стринггрида есть закрашенная строка
Возможно, что программисты, писавшие эту программу как-то накрутили объект ListView в окне "Терминал", потому что такие строчки на всю протяженность окна (как будто объединенные ячейки в таблице) вроде как в стандартных ListView я раньше не встречала... Да и вообще там строчки разных цветов (чередуются), это какое-то нестандартное окно ListView... Может поэтому оно нечитабельно?
Ответить с цитированием
  #24  
Старый 26.04.2009, 11:56
Аватар для Страдалецъ
Страдалецъ Страдалецъ вне форума
Гуру
 
Регистрация: 09.03.2009
Адрес: На курорте, из окна вижу теплое Баренцево море. Бррр.
Сообщения: 4,723
Репутация: 52347
По умолчанию

Цитата:
Возможно, что программисты, писавшие эту программу как-то накрутили объект ListView в окне "Терминал", потому что такие строчки на всю протяженность окна (как будто объединенные ячейки в таблице) вроде как в стандартных ListView я раньше не встречала... Да и вообще там строчки разных цветов (чередуются), это какое-то нестандартное окно ListView... Может поэтому оно нечитабельно?
Скорее всего это не ListView а cxGrid из каких-то ранних компонент Девэкспресс -уж очень похоже, вот только внешний вид вкладок немножко отличается.
__________________
Жизнь такова какова она есть и больше никакова.
Помогаю за спасибо.
Ответить с цитированием
  #25  
Старый 26.04.2009, 12:13
Nyctos Kasignete Nyctos Kasignete вне форума
Активный
 
Регистрация: 29.03.2009
Сообщения: 300
Репутация: 94
По умолчанию

Цитата:
Сообщение от Страдалецъ
Скорее всего это не ListView а cxGrid из каких-то ранних компонент Девэкспресс -уж очень похоже, вот только внешний вид вкладок немножко отличается.
Класс окна определяется однозначно как SysListView32.
Ответить с цитированием
  #26  
Старый 26.04.2009, 15:17
одинадцатый одинадцатый вне форума
Новичок
 
Регистрация: 16.04.2009
Сообщения: 95
Репутация: 10
По умолчанию

Ладно, закрашенная строка внизу мне пока не нужна.
Цитата:
Однако из других окон (например, есть такое "Обзор рынка") всё считывается спокойно.
Да у меня так же, но ведь если "фиксированные" ячейки не считываются, и допустим нижняя, закрашенная строка какая-то накрученная то там и считыватся нечему. Щас рынок закрыт, я в понедельник попробую транзакцию какую-нибудь открыть - тогда будет что считывать.
Теперь коли скачали мт4 тогда не подскажете ли: как мне передать в эту программу нажатие клавишы F9 и в появившемся окне в некоторых окнах что-то выбрать из списка, а что-то ввести, и нажать чекбокс внизу? И как все это сделать чтоб при передачи этих сообщений в программу она не разворачивалась?
Ответить с цитированием
  #27  
Старый 26.04.2009, 15:31
Nyctos Kasignete Nyctos Kasignete вне форума
Активный
 
Регистрация: 29.03.2009
Сообщения: 300
Репутация: 94
По умолчанию

Подождите, а что делает F9? Нажимаю-нажимаю, и ничего не происходит...

Тааак. Раскопала в справке к программе, что F9 вызывает команду Новый ордер. Соответствующая кнопка у меня неактивна, поэтому ничего не происходит. Короче, разбираться с этой программой не хочу. Скажу в общем.
Нажатие клавиши F9 можно послать окну, если известен его handle. Искать дескриптор вы умеете. Отослать F9 можно так:
Код:
PostMessage(wndHandle, WM_KEYDOWN, VK_F9, 0)
wndHandle — это дескриптор окна.
Ввести текст в какой-нибудь Edit:
Код:
SendMessage(editHandle, WM_SETTEXT, 0, Integer(PChar('новый текст здесь')));
editHandle — дескриптор Edit'а.
Нажать на CheckBox:
Код:
PostMessage(CheckBoxHandle, BM_CLICK, 0, 0);

Последний раз редактировалось Nyctos Kasignete, 26.04.2009 в 16:40.
Ответить с цитированием
  #28  
Старый 26.04.2009, 19:43
одинадцатый одинадцатый вне форума
Новичок
 
Регистрация: 16.04.2009
Сообщения: 95
Репутация: 10
По умолчанию

а как проверить чему равен checked у checkbox?
и скажите где и как вы всю эту информацию ищите, чтоб я вас больше не доставал)
Такой вот еще вопрос: можно же как-то имея адрес ехе файла узнать запущен ли он и если запущен получить дескриптор процесса, нити, и.. что там еще надо для вывода в мемо дескрипторов всех дочерних окон?
А еще как выбрать в комбобокс один из элементов, и как прочесть тексто того который сейчас показан?
и как узнать чему равен enabled и visible разных элементов, в частности лабел и едит?
И эмитирование нажатия F9 происходит только если терминал развернут, можно ли как нибудь это обойти? Может ему сначала следует пеердать фокус без разворачивания?
И можно нажать на кнопку новый ордер, она находится возле меню(у вас затемненная), как на нее нажать?
Я так понимаю она расположена на каком-то меню быстрых кнопок которе по моему имеет класс ToolbarWindows32.

Последний раз редактировалось одинадцатый, 26.04.2009 в 20:26.
Ответить с цитированием
  #29  
Старый 26.04.2009, 22:28
Nyctos Kasignete Nyctos Kasignete вне форума
Активный
 
Регистрация: 29.03.2009
Сообщения: 300
Репутация: 94
По умолчанию

Ааааааа!! Сколько вопросов! У меня сейчас взорвется голова!
Но поехали потихоньку...
Цитата:
Сообщение от одинадцатый
а как проверить чему равен checked у checkbox?
Код:
case SendMessage(CheckBoxHandle, BM_GETCHECK, 0, 0) of
  BST_CHECKED: ShowMessage('Флажок стоит');
  BST_UNCHECKED: ShowMessage('Флажок снят');
else ShowMessage('Флажок затенен');
end;
Цитата:
Сообщение от одинадцатый
скажите где и как вы всю эту информацию ищите
В Windows SDK и в книжечках.
Цитата:
Сообщение от одинадцатый
можно же как-то имея адрес ехе файла узнать запущен ли он
Можно. Раз, два, три и т.д. Перебираете все окна функцией EnumWindows и сравниваете для каждого путь к его файлу с тем путем, который имеется у вас. Если совпадают, то файл запущен.
Цитата:
Сообщение от одинадцатый
как выбрать в комбобокс один из элементов, и как прочесть тексто того который сейчас показан?
Код:
var
  CurTxt: PChar;
  CurSel: Integer;
  ElementToSelect: string;
begin
  ElementToSelect := 'qwerty'; // Впишите здесь текст элемента, который должен быть выбран
  GetMem(CurTxt, 255);
  try
    CurSel := SendMessage(ComboBoxHandle, CB_GETCURSEL, 0, 0); // индекс текущего элемента
    SendMessage(ComboBoxHandle, CB_GETLBTEXT, CurSel, Integer(CurTxt));
    ShowMessage('Текущий элемент: ' + StrPas(CurTxt));
    SendMessage(ComboBoxHandle, CB_SELECTSTRING, -1, Integer(@ElementToSelect[1]));
  finally
    FreeMem(CurTxt, 255);
  end;
end;
Если это не понятно, то ComboBoxHandle — это дескриптор ComboBox'а.
Цитата:
Сообщение от одинадцатый
как узнать чему равен enabled и visible разных элементов, в частности лабел и едит?
Есть функции IsWindowEnabled, IsWindowVisible, а также EnableWindow. Они требуют только знания дескриптора того окна, которое вы проверяете, и возвращают значения True или False. Функция EnableWindow меняет состояние и возвращает True, если окно было первоначально disabled. По поводу Label. По-моему, это вообще не окно, не может принимать фокуса и не имеет дескриптора, поэтому сильно сомневаюсь, что Win32 API тут хороший помощник. Но я могу ошибаться.

Про остальное подумаю тоже потом. Устала =) И еще не очень поняла, про развернутость окна... Это что значит — если окно развернуто на весь экран, то сообщение WM_KEYDOWN принимается, а если не развернуто, то нет?

Цитата:
Сообщение от одинадцатый
Я так понимаю она расположена на каком-то меню быстрых кнопок которе по моему имеет класс ToolbarWindows32
Посмотрите самостоятельно, имеет ли это "меню" дочерние окна:
Код:
if GetWindow(HandleOfToolbar, GW_CHILD) <> 0 then
  ShowMessage('Есть дети')
else ShowMessage('Нет детей');
HandleOfToolbar — дескриптор Toolbar'а с кнопками.
Если "дети" есть, то добраться до их дескрипторов можно с помощью функции EnumChildWindows. Хотя, вполне возможно, в Win32 API есть и специальные функции для работы с такими Toolbar.

Всё, не могу больше тыкать по кнопкам. Спокойной ночи.

Последний раз редактировалось Nyctos Kasignete, 26.04.2009 в 22:52.
Ответить с цитированием
  #30  
Старый 27.04.2009, 17:41
одинадцатый одинадцатый вне форума
Новичок
 
Регистрация: 16.04.2009
Сообщения: 95
Репутация: 10
По умолчанию

Цитата:
Это что значит — если окно развернуто на весь экран, то сообщение WM_KEYDOWN принимается, а если не развернуто, то нет?
Да только не обязательно на весь экран. Просто если программа свернута в таскбар(полоску с пуском) - то F9 не обрабатывается. А если программа развернута(не обязательно во весь экран) - то обрабатывается.
Такая еще проблема: когда я жму кнопку методом
PostMessage(handle, BM_CLICK, 0, 0);
программа выдает ошибку не верные параметры для сделки, а если жму на эту кнопку явно, мышкой - все нормально, почему так?
Может стоит сделать не BM_CLICK, а что то типа BM_MOUSEDOWN а потом BM_MOUSEUP?
А можно как-нибудь выбрать элимент из комбобокс не по тексту, а по индексу?

А главная проблемма в том что не смотря на имеющиеся поля(открытые позиции) в таблице на вкладке Торговля, на окне Терминал, в мой стринггрид эта информация все равно не выводиться.((((((((((((((
Все остальное не имеет значения если этих данных нет.
Умоляю помогите.
Чтоб у вас появилась эта вкладка, и загорелась кнопка Новый ордер вам надо зарегестрировать демо счет.
ПРЕДУПРЕЖДАЮ: ВАС ЭТО НИ К ЧЕМУ НЕ ОБЯЗЫВАЕТ!
Регистрируется так: меню Файл->Открыть счет, там от фонаря вводите данные и жмете везде далее.
Чтоб в стринггриде на вкладке торговля появились данные надо открыть позиции(ОПЯТЬ ЖЕ ПРЕДУПРЕЖДАЮ - ВИРТУАЛЬНО, вы ни каких денег ни кому должны не будете) Открываются так: F9 или новый ордер, и там сразу нажать на Buy или Sell.
Все, данные появились, и они почему то не выводятся.
Может это от того что эта вкладка не видна во время запуска программы и записывается в память куда то в другое место?
А можно с вами как-нибудь в другом месте связаться? Сильно доставать не буду) Мое мыло: D-work@list.ru.

Последний раз редактировалось одинадцатый, 27.04.2009 в 19:27.
Ответить с цитированием
Ответ


Delphi Sources

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB-коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход


Часовой пояс GMT +3, время: 20:53.


 

Сайт

Форум

FAQ

RSS лента

Прочее

 

Copyright © Форум "Delphi Sources" by BrokenByte Software, 2004-2023

ВКонтакте   Facebook   Twitter