Евгений Корепов

Пользователи
  • Публикаций

    589
  • Зарегистрирован

  • Посещение

  • Победитель дней

    64

Весь контент Евгений Корепов

  1. Евгений Корепов

    Фреймворк для стандартного ListView

    Стало гораздо-гораздо лучше. Больше 70% не грузит ядро. Может математику вычислений попробовать на другое ядро повесить, чтоб совсем хорошо было? И очень интересно на исходники взглянуть. Это возможно?
  2. Евгений Корепов

    Фреймворк для стандартного ListView

    Жрет проц не меньше первой версии. Еще заметил - если дать приложению побыть запущенным несколько минут, то изменение размеров становится почти не возможным, торможение увеличивается на порядок.
  3. Евгений Корепов

    обновление визуальных компонентов

    Подобные задачи я решаю обычно следующим способом: 1. Разделяю приложение на две части: интерфейс и класс обеспечивающий непосредственно работу с внешним сервером. 2. Класс обеспечивает доступ к высокоуровневым методам управления со стороны интерфейса (т.е. интерфейс должен сказать классу - включи лампочку в туалете, а класс уже обеспечивает всю низкоуровневую работу, проверку состояния, отправку последовательности команд и т.д.). Класс можно сделать в контексте основного потока главной формы. 3. У класса должен быть потомок-поток, который обеспечивает непосредственно работу с TCP, обрабатывает полученные результаты и отдает классу-родителю ответы. Общение между классом и потоком лучше организовать по следующей схеме: 1. Из класса в поток - потоко-безопасная очередь TThreadQueue передающая Record к примеру такого вида TCommandRecord = Record CommandName : String; // или Integer Address : String; Command : TArray<Byte>; Parameter : TArray<Byte>; Error : Boolean; ErrorMessage : String; Result : TArray<Byte>; // или String UID : Integer; end; Это для примера. В реальности вам придется что то убрать, что то добавить. 2. Из потока в класс можно сделать или очередь TThreadQueue, или Event. Для вашего случая, обеспечения потока команд я бы следующую систему - у класса сделать список командных очередей TList. Перед началом отсылки команд, создаем очередь команд TQueue<TCommandRecord>, заполняем очередь из некоего шаблона (первая команда тип устройсва, вторая команда установка, третья команда старт и т.д.) и отдаем эту очередь в работу. Система отправляет из очереди первую команду в поток (снабженную идентификаторм очереди), поток отправляет ее в сеть, получает ответ, заполняет TCommandRecord (сохраняя идентификатор очереди) и отдает TCommandRecord в класс. Класс, по идентификатору находит нужную очередь, проверяет отсутствие ошибок и оправляет следующую команду из этой очереди.. Когда очередь команд пуста, удаляем ее из списка очередей. Вот как то так.
  4. Евгений Корепов

    обновление визуальных компонентов

    А опишите вашу концепцию движения данных. Что вообще за комплекс приложений, какие команды, какие ответы? Мы, посредством мозгового штурма, дадим вам варианты решений.
  5. Евгений Корепов

    обновление визуальных компонентов

    Вот тут лучше перестраховаться и взять за правило принцип "Любое обращение к адресному пространству другого потока выполнять потокобезопасными способами". Потому как даже чтение может привести к непредсказуемым результатам - читаете вы данные, строку к примеру из другого потока, прочитали половину, а тот поток в это время перезаписал содержимое ячеек памяти, и вы после этого читаете оставшуюся половину. Вместо ожидаемых данных получаете черте что. Это грубый пример конечно.
  6. Не понял, что значит выводит "одной строкой"? Причем тут Memo? И где танец с бубном? ))) Если вы хотите загружать файл напрямую с помощью Navigate, то делайте это: Делаем ресурс В деплоймент вы сразу увидите этот файл (он никуда не встраивается, а кладется как есть) И вам остается только лишь сделать WebBrowser1.Navigate('file://путь_до_вашего_файла'); Вы это хотели получить?
  7. Евгений Корепов

    Полоса на форме справа (Android)

    Очистка проекта? Если не поможет, то видимо переустановка среды. У меня вот с 10.3 была беда - https://quality.embarcadero.com/browse/RSP-22965 Application not started on Android 4.4, Android 5, Android 6. Помогло только полное удаление всех версий дельфи с компа, удаление из реестра всех упоминаний о дельфи, и удаление всего мусора дельфи из всех папок (AppData и прочих).
  8. Евгений Корепов

    Полоса на форме справа (Android)

    Хотя попробовал ваш исходник без изменений запустить - полосы у меня все равно нет.
  9. Евгений Корепов

    Полоса на форме справа (Android)

    Распаковал ваш исходник, вот так у вас выглядят библиотеки изначально:
  10. Евгений Корепов

    Полоса на форме справа (Android)

    Проверьте библиотеки, в вашем проекте они были какие то левые и не полностью. Кликните на Librares правой кнопкой и далее Rever system files to Default
  11. Евгений Корепов

    Полоса на форме справа (Android)

    LineRight.7z
  12. Евгений Корепов

    Полоса на форме справа (Android)

    Проверил на 10.3.1 - нет полосы. Вы уверены что это эффект приложения?
  13. Евгений Корепов

    обновление визуальных компонентов

    Для Андроид и Ios вам лучше использовать push. Сервер отправляет пуши (индивидуально, группе, или всем), а приложения их получают, парсят полученный JSON и раскидывают по контролам.
  14. Вы можете на этапе создания формы загрузить из ресурсов нужный вам HTML: procedure TFormMain.InitResource(); var ResStream: TResourceStream; SL : TStringList; begin ResStream := TResourceStream.Create(hInstance, 'HTMLSpinner', RT_RCDATA); SL:=TStringList.Create; SL.LoadFromStream(ResStream, TEncoding.UTF8); FHTMLSpinner:=SL.Text; ResStream.Free; SL.Free; end; Где FHTMLSpinner, типа String, и из этой строки уже грузить в браузер по мере надобности: FWB.LoadFromStrings(FHTMLSpinner, 'localhost');
  15. Евгений Корепов

    Фреймворк для стандартного ListView

    Очень сильно жрет CPU при изменении размеров формы - 100% CPU
  16. Евгений Корепов

    PUSH и Android8

    Вроде все работает. Разрешений не нужно никаких вообще. До этого ошибочно требовало доступ к учетной записи - пофиксили. Код вроде остался прежним, вот кусок из живого проекта: procedure TFormMain.InitPushService; begin TTask.Run( procedure Var ADeviceID, ADeviceToken : String; begin FPushService := TPushServiceManager.Instance.GetServiceByName(TPushService.TServiceNames.GCM); FPushService.AppProps[TPushService.TAppPropNames.GCMAppID] := ConstGCMAppID; if Assigned(FPushService) then begin FPushServiceConnection := TPushServiceConnection.Create(FPushService); FPushServiceConnection.OnChange := OnPushServiceConnectionChange; FPushServiceConnection.OnReceiveNotification := OnReceivePushNotificationEvent; FPushServiceConnection.Active := True; ADeviceID := FPushService.DeviceIDValue[TPushService.TDeviceIDNames.DeviceID]; ADeviceToken := FPushService.DeviceTokenValue[TPushService.TDeviceTokenNames.DeviceToken]; FSettings.Flags.PushServiceInited:=True; TThread.Synchronize(TThread.CurrentThread, procedure () begin FNetwork.DeviceID:=ADeviceID; FNetwork.DeviceToken:=ADeviceToken; FNetwork.SendFCMRegistration(); // ?????????????????????????????? Log('DeviceID: ' + FNetwork.DeviceID); Log('DeviceToken: ' + FNetwork.DeviceToken); end); end; end ); end; procedure TFormMain.OnPushServiceConnectionChange(Sender: TObject; AChange: TPushService.TChanges); begin TThread.Synchronize(TThread.CurrentThread, procedure () begin if TPushService.TChange.DeviceToken in AChange then begin if Assigned(FNetwork) then begin FNetwork.DeviceID := FPushService.DeviceIDValue[TPushService.TDeviceIDNames.DeviceID]; FNetwork.DeviceToken := FPushService.DeviceTokenValue[TPushService.TDeviceTokenNames.DeviceToken]; Log('DeviceID: ' + FNetwork.DeviceID); Log('DeviceToken: ' + FNetwork.DeviceToken); FNetwork.SendFCMRegistration(); end; end; end ); end; procedure TFormMain.OnReceivePushNotificationEvent(Sender: TObject; const ANotification: TPushServiceNotification); var AMessageSection : String; begin if Assigned(ANotification.Json) then begin Log('Push Message Json'); Log(ANotification.Json.ToString); if ANotification.Json.TryGetValue('message_section', AMessageSection) then begin if AMessageSection.Equals('support') then SetActiveTab(ConstSectionSupport); if AMessageSection.Equals('news') then SetActiveTab(ConstSectionNews); if AMessageSection.Equals('info') then SetActiveTab(ConstSectionInfo); if AMessageSection.Equals('services') then SetActiveTab(ConstSectionServices); end else if FSettings.CurrentSection <> ConstSectionSupport then SetActiveTab(ConstSectionSupport) else WebBrowserCallJS(TCallJS.SupportLoadContent); PlaySoundEffects(1); end; end; procedure TFormMain.CheckStartupNotifications(); var CurNotification : TPushServiceNotification; begin if Length(FPushService.StartupNotifications) > 0 then for CurNotification in FPushService.StartupNotifications do if Assigned(CurNotification) then OnReceivePushNotificationEvent(Self, CurNotification); NotificationCenter.CancelAll; end; Метод FNetwork.SendFCMRegistration() отсылает регистрацию на мой сервер. В методе проверяется получение регистрации и факт отсылки (он может вызываться несколько раз у меня)
  17. Потому что перед этой строкой вы делаете Item := ListView3.Items.Add; Item.Text := i.ToString; Item.Detail := 'Detail: '+i.ToString; Вы добавили Item, но не дали возможность его отрисовать. Т.е. ListView3 добавил себе итем, но еще ничего о нем не знает. Параметры Item вам нужно выставлять в событии ListView3UpdatedObjects - тут уже будет известна высота.
  18. Евгений Корепов

    Runtime permissions in Delphi 10.3 Rio

    Для полноценной работы вам нужно добавить параметры в вызов (иначе вы не узнаете дал ли пользователь разрешение или нет) PermissionsService.RequestPermissions([FPermissionWrite, FPermissionRead], nil); Вот так: PermissionsService.RequestPermissions([FPermissionWrite, FPermissionRead], PermissionRequestResult, ExplainReason); PermissionRequestResult - это обработка ответа пользователя procedure TForm.PermissionRequestResult(Sender: TObject; const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>); begin if (Length(AGrantResults) = 2) and (AGrantResults[0] = TPermissionStatus.Granted) and (AGrantResults[1] = TPermissionStatus.Granted) then begin // Ура! Пользователь дал разрешение на оба наших запроса. Выставялем глобальные флаги (к примеру) которые сигнализируют что можно читать/писать карту памти end else TDialogService.ShowMessage('Не возможно продолжить работу, требуемые разрешения не получены') end; И ExplainReason - если пользователь сдуру не дал разрешение, то вам нужно объяснить ему что без этого приложение работать не будет. procedure TForm.ExplainReason(Sender: TObject; const APermissions: TArray<string>; const APostRationaleProc: TProc); begin TDialogService.ShowMessage('Приложению нужен доступ к карте памяти для таких то целей, иначе приложение не сможет работать. Зайдите в настроки Андроид и дайте разрешение на доступ', procedure(const AResult: TModalResult) begin APostRationaleProc; end) end;
  19. Евгений Корепов

    Webbrowser в Windows

    Столкнулся вот тоже с этой фигней. Как правильно выставить флаг? По идее я должен выяснить версию IE в системе и подогнать значение под нее? Пока врубил 11001 - работает норм, по крайней мере у клиентов у которых стоит IE11
  20. Евгений Корепов

    [Android] обмен сообщениями между потоками и формой

    Вот вам еще пища для размышления и экспериментов. По моему мнению самый простой способ передачи из потока в главную форму: unit Unit1; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, Generics.Collections, System.Generics.Collections, FMX.Controls.Presentation, FMX.ScrollBox, FMX.Memo, FMX.StdCtrls, System.Rtti, FMX.Grid.Style, FMX.Media, FMX.Grid; type TMyMessage = record StringMessage : String; //Я вынужден был добавить еще строку, иначе в Rio косяк https://quality.embarcadero.com/browse/RSP-21806 PackedNumber : Integer; X : Integer; Y : Integer; Z : Integer; end; TOnReceiveDataEvent = procedure(const AMyMessage : TMyMessage) of object; TMyQueue = TThreadedQueue<TMyMessage>; TMyThread = class(TThread) protected FMyQueueIn : TMyQueue; FOnReceiveData : TOnReceiveDataEvent; FPackedNumber : Integer; procedure Execute; override; public constructor Create(AMyQueueIn : TMyQueue); property OnReceiveData: TOnReceiveDataEvent read FOnReceiveData write FOnReceiveData; end; TForm1 = class(TForm) Memo: TMemo; ToolBar1: TToolBar; Button1: TButton; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure Button1Click(Sender: TObject); private { Private declarations } FMyQueueOut: TMyQueue; FMyThread : TMyThread; procedure OnReceiveData(const AMyMessage : TMyMessage); public { Public declarations } end; var Form1: TForm1; implementation {$R *.fmx} constructor TMyThread.Create(AMyQueueIn : TMyQueue); begin FMyQueueIn:=AMyQueueIn; FPackedNumber:=0; inherited Create(True); end; procedure TMyThread.Execute; var AMyMessage : TMyMessage; S : String; begin while (not Terminated) do begin S:=''; if FMyQueueIn.PopItem(AMyMessage) = TWaitResult.wrSignaled then S:='Из главного потока получено: ' + AMyMessage.StringMessage; Inc(FPackedNumber); with AMyMessage do begin StringMessage:=S; PackedNumber:=FPackedNumber; X:=Random(1000); Y:=Random(1000); Z:=Random(1000); end; // Отправляем Record в главную форму (главный поток) if Assigned(FOnReceiveData) then // Тут на выбор три метода доставки :-) // TThread.Synchronize(Self, // TThread.ForceQueue(Self, TThread.Queue(Self, procedure begin FOnReceiveData(AMyMessage); end ); sleep(random(800)); end; end; procedure TForm1.FormCreate(Sender: TObject); begin FMyQueueOut:=TMyQueue.Create(30, 1000, 10); FMyThread:=TMyThread.Create(FMyQueueOut); FMyThread.OnReceiveData:=OnReceiveData; FMyThread.Start; end; procedure TForm1.FormDestroy(Sender: TObject); begin FMyThread.Terminate; FMyThread.WaitFor; FMyThread.Free; FMyQueueOut.Free; end; procedure TForm1.OnReceiveData(const AMyMessage : TMyMessage); Var S : String; begin S:='Пакет№ ' + AMyMessage.PackedNumber.ToString + ' X=' + AMyMessage.X.ToString + ' Y=' + AMyMessage.Y.ToString + ' Z=' + AMyMessage.Z.ToString + ' ' + AMyMessage.StringMessage; Memo.BeginUpdate; Memo.Lines.Add(S); Memo.GoToTextEnd; Memo.EndUpdate; end; procedure TForm1.Button1Click(Sender: TObject); Var AMyMessage : TMyMessage; begin AMyMessage.StringMessage:='Сообщение для потока № ' + Random(100).ToString; FMyQueueOut.PushItem(AMyMessage); end; end.
  21. Евгений Корепов

    PUSH и Android8

    Этот замкнутый круг разорвали в Rio, переходите на последнюю версию среды и пуши будут работать.
  22. Евгений Корепов

    как отжать клавишу Shift?

    А в каком компоненте вы выделяете текст? Если в стандартных компонентах редактирования текста, то может вот так проще? Edit1.SelStart:=5; Edit1.SelLength:=10; Memo1.SelStart:=5; Memo1.SelLength:=5;
  23. Евгений Корепов

    Отображение картинок в ListView

    У меня на Rio работает как то не стабильно. Изредка вылазят исключения при закрытии приложения. То в TMonitor, то даже в TDictionary ((( Сделал стабильную версию - работает быстро и без глюков, но с использованием внешнего списка с IAsyncResult. Добавил процедуру ClearListViewAndCancelAsynchronousRequests() где выполняется Cancel и очищается ListView. Теперь можно клацать по кнопке сколько угодно. unit Unit1; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.ListView.Types, FMX.ListView.Appearances, FMX.ListView.Adapters.Base, System.Net.HttpClient, System.Generics.Collections, FMX.Controls.Presentation, FMX.StdCtrls, FMX.Layouts, FMX.ListView; const ListViewItemImageEmpy = -1; ListViewItemImageLoading = 0; ListViewItemImageLoaded = 1; type TForm1 = class(TForm) ListView1: TListView; Layout1: TLayout; Button1: TButton; procedure Button1Click(Sender: TObject); procedure ListView1UpdatingObjects(const Sender: TObject; const AItem: TListViewItem; var AHandled: Boolean); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private { Private declarations } FListViewUpdating : Boolean; FHTTPClient : THTTPClient; FAsyncResultList : TList<IAsyncResult>; procedure LoadImage(const AItem: TListViewItem; const AListItemImage : TListItemImage); procedure ClearListViewAndCancelAsynchronousRequests(); public { Public declarations } end; var Form1: TForm1; implementation {$R *.fmx} procedure TForm1.FormCreate(Sender: TObject); begin listview1.ItemIndex:=0; listview1.ItemAppearance.ItemAppearance:='Custom'; listview1.ItemAppearanceObjects.ItemObjects.Accessory.Visible:=false; FHTTPClient:=THTTPClient.Create; FAsyncResultList:=TList<IAsyncResult>.Create; FListViewUpdating:=False; end; procedure TForm1.FormDestroy(Sender: TObject); begin ClearListViewAndCancelAsynchronousRequests(); FListViewUpdating:=True; if Assigned(FHTTPClient) then FHTTPClient.Free; end; procedure TForm1.ClearListViewAndCancelAsynchronousRequests(); Var I : Integer; begin for I := 0 to FAsyncResultList.Count - 1 do if FAsyncResultList.Items[I] <> Nil then if Not FAsyncResultList.Items[I].IsCompleted then FAsyncResultList.Items[I].Cancel; FListViewUpdating:=True; for I := ListView1.Items.Count - 1 downto 0 do ListView1.Items.Delete(I); FListViewUpdating:=False; end; procedure TForm1.Button1Click(Sender: TObject); var i:integer; item:TListViewItem; ARandom : Integer; begin ClearListViewAndCancelAsynchronousRequests(); //Формирование нового списка for i := 1 to 10000 do begin FAsyncResultList.Add(Nil); FListViewUpdating:=True; Item:=listview1.Items.Add; item.Height:=45; Randomize; ARandom:=Random(6); case ARandom of 0 : item.data['ImageURL']:='http://fire-monkey.ru/uploads/monthly_2017_06/me.thumb.jpg.966ddc17d5602ee14feb43479c1f6963.jpg'; 1 : item.data['ImageURL']:='http://fire-monkey.ru/uploads/monthly_2018_05/B-IpGQmVgTM.thumb.jpg.2ebeb0bd766ab7cf19f10195d6ea2be9.jpg'; 2 : item.data['ImageURL']:='http://fire-monkey.ru/uploads/monthly_2016_04/10.png.b9ab371e8fd38172fee96bcf75fb6699.thumb.png.b0685259b03bfff540903913845532a5.png'; 3 : item.data['ImageURL']:='https://secure.gravatar.com/avatar/9942c50b1641a921c52d4b389bd718d6?d=http://fire-monkey.ru/uploads/monthly_2017_12/K_member_87.png'; 4 : item.data['ImageURL']:='http://fire-monkey.ru/uploads/monthly_2016_11/photo-1529.png.7267be10b59f950b7c5bb3f34a60901e.thumb.png.22027ae85266216220310ed694d57628.png'; 5 : item.data['ImageURL']:='http://fire-monkey.ru/uploads/profile/photo-thumb-115.jpg'; end; Item.Data['ImageState']:=ListViewItemImageEmpy; FListViewUpdating:=False; item.Adapter.ResetView(item); end; end; procedure TForm1.LoadImage(const AItem: TListViewItem; const AListItemImage : TListItemImage); Var AAsyncResult : IAsyncResult; begin if Not Assigned(AItem) or Not Assigned(AListItemImage) then exit; if AItem.Data['ImageState'].AsInteger <> ListViewItemImageEmpy then exit; if AItem.Data['ImageURL'].AsString.IsEmpty then exit; AItem.Data['ImageState']:=ListViewItemImageLoading; FAsyncResultList.Items[AItem.Index]:=FHTTPClient.BeginGet( // AItem.TagObject:=TBaseAsyncResult(FHTTPClient.BeginGet( // FHTTPClient.BeginGet( procedure (const ASyncResult: IAsyncResult) Var AHTTPResponse : IHTTPResponse; begin if ASyncResult.IsCancelled then begin exit; end; try AHTTPResponse:=THTTPClient.EndAsyncHTTP(ASyncResult); if Not Assigned(AHTTPResponse) then exit; if AHTTPResponse.StatusCode <> 200 then exit; except exit; end; TThread.Synchronize(Nil, procedure begin if Not Assigned(AItem) then exit; if Not Assigned(AListItemImage) then exit; AListItemImage.BeginUpdate; AListItemImage.Bitmap:=TBitmap.Create; AListItemImage.Bitmap.LoadFromStream(AHTTPResponse.ContentStream); AListItemImage.EndUpdate; AItem.Data['ImageState']:=ListViewItemImageLoaded; FAsyncResultList.Items[AItem.Index]:=Nil; end ); end, AItem.Data['ImageURL'].AsString ); end; procedure TForm1.ListView1UpdatingObjects(const Sender: TObject; const AItem: TListViewItem; var AHandled: Boolean); function SetupImageObject(const AName : String; AWidth, AHeight, X , Y : Single; AAlign, AVertAlign: TListItemAlign) : TListItemImage; begin Result:=TListItemImage(AItem.View.FindDrawable(AName)); if Result = Nil then begin Result:=TListItemImage.Create(AItem); Result.Name:=AName; Result.Bitmap:=Nil; Result.OwnsBitmap:=True; end; Result.Width:=AWidth; Result.Height:=AHeight; Result.PlaceOffset.X:=X; Result.PlaceOffset.Y:=Y; Result.Align:=AAlign; Result.VertAlign:=AVertAlign; Result.ScalingMode:=TImageScalingMode.StretchWithAspect; Result.Visible:=True; end; Var AListItemImage : TListItemImage; begin if FListViewUpdating then exit; AListItemImage:=SetupImageObject('s_image', 35, 35, 0 , 0, TListitemalign.Leading, TListItemAlign.Center); LoadImage(AItem, AListItemImage); AHandled:=true; end; end.
  24. Евгений Корепов

    Поток в Android-е

    С помощью стандартного (тот что в палитре компонентов Delphi) это сделать затруднительно. Вам нужно написать свои реализации классов TMedia и TCustomMediaCodec. Это долгий и муторный путь. Но можно забыть про куцую оболочку Эмбаркадеро под названием TMediaPlayer, и все становиться гораздо проще: unit Unit1; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, Androidapi.JNI.Media, Androidapi.Helpers, Androidapi.JNI.GraphicsContentViewText, FMX.Controls.Presentation, FMX.StdCtrls; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } FPlayer : JMediaPlayer; public { Public declarations } end; var Form1: TForm1; implementation {$R *.fmx} procedure TForm1.Button1Click(Sender: TObject); begin FPlayer:=TJMediaPlayer.Create; FPlayer.setDataSource(StringToJString('http://cdndl.zaycev.net/228184/8640962/lana_del_rey_-_hope_is_a_dangerous_thing_for_a_woman_like_me_to_have_but_i_have_it_%28zaycev.net%29.mp3')); FPlayer.prepare; FPlayer.Start(); end; end.
  25. Евгений Корепов

    [Android] обмен сообщениями между потоками и формой

    Добавлю - вместо OnIdle, очередь можно проверять по таймеру. В TMyMessage = record не планировал вначале добавлять StringMessage, но без него в Rio косяк - компилятор выдает ошибку https://quality.embarcadero.com/browse/RSP-21806