-
Content Count
2982 -
Joined
-
Last visited
-
Days Won
134
Everything posted by Remy Lebeau
-
Streaming binary data via TIdHTTPServer
Remy Lebeau replied to Erix A.'s topic in Network, Cloud and Web
TIdHTTPServer is not really designed for streaming media. If you don't provide the AResponseInfo with a ContentText or ContentStream value, a default HTML page is sent instead. And if you don't provide a ContentLength value, one is calculated automatically from the ContentText/ContentStream by default. And if you don't send a response before the OnCommand... event handler exits, one is sent automatically. So basically, the only way I see to do what you want is to either: - NOT using HTTP's "chunked" encoding - have your OnCommand... event handler set the AResponseInfo.HeaderHasBeenWritten property to True to disable the server's default response handling, send your own HTTP response headers manually, and then run a loop that sends the raw media samples as needed until finished, and then exit the event handler. - USING HTTP's "chunked" encoding - have your OnCommand... event handler set the AResponseInfo.TransferEncoding property to 'chunked' to disable the server's default ContentLength handling, then call AResponseInfo.WriteHeader() to send the server's default HTTP response headers normally, and then run a loop that sends the media samples (in HTTP chunked format) as needed until finished, and then exit the event handler. There are probably other solutions, like writing a custom TStream class for use with the ContentStream, but that can get a little ugly. -
In this situation, it is best not to block the main thread at all. Disable the UI if you need to, but let the worker thread and main message queue run in parallel normally, and have the worker thread notify the main thread when it is finished, eg: procedure TForm2.Button1Click(Sender: TObject); var Thrd : TThread; begin Thrd := TThread.CreateAnonymousThread( procedure begin Sleep(10000); end ); Thrd.OnTerminate := ThreadFinished; Thrd.Start; // disable UI as needed ... end; procedure TForm2.ThreadFinished(Sender: TObject); begin Memo1.Lines.Add('Thread finished'); // enable UI as needed ... end;
-
proper way to check server up or down in rest application
Remy Lebeau replied to toufik's topic in FMX
The simplest option is to use either TThread.CreateAnonymousThread(), eg: uses ..., System.Classes; procedure TFrmLogin.StartLogin; begin TThread.CreateAnonymousThread( procedure var Success: Boolean; begin try RequestLogin.Execute; Success := RequestLogin.Response.Status.Success; except Success := False; end; TThread.Queue(nil, procedure begin AfterLogin(Success); end ); end ).Start; end; procedure TFrmLogin.AfterLogin(Success: Boolean); begin if Success then begin ShowMessage ('server up'); do something ... end else begin ShowMessage ('server down'); do something else ... end; end; Or similarly, TTask.Run(): uses ..., System.Threading; procedure TFrmLogin.StartLogin; begin TTask.Run( procedure var Success: Boolean; begin try RequestLogin.Execute; Success := RequestLogin.Response.Status.Success; except Success := False; end; TThread.Queue(nil, procedure begin AfterLogin(Success); end ); end ); end; procedure TFrmLogin.AfterLogin(Success: Boolean); begin if Success then begin ShowMessage ('server up'); do something ... end else begin ShowMessage ('server down'); do something else ... end; end;- 6 replies
-
- server
- firemonkey
-
(and 2 more)
Tagged with:
-
My answer applies to FMX as well as VCL, though the details of the implementation will differ between them. To handle the WM_GETMINMAXINFO message in VCL, you can simply override the Form's virtual WndProc() method, or subclass its WindowProc property, or use a message method directly on your Form's class type. But in FMX, those options are not available. There is no WndProc/WindowProc to override/subclass. And FMX dispatches only window messages that FMX itself uses internally. So, you would have to instead manually subclass the Form's HWND directly by using the Win32 SetWindowsLongPtr(GWL_WNDPROC) or SetWindowsSubclass() function.
-
Generics: Classes vs Records - Differences in use
Remy Lebeau replied to Lars Fosdal's topic in Algorithms, Data Structures and Class Design
Not when you take the 2nd sentence into account. But whatever. I reworded my previous comment, just for you. -
proper way to check server up or down in rest application
Remy Lebeau replied to toufik's topic in FMX
The best way to handle that is to perform an actual REST request, and handle any error it may produce. Move the code to a worker thread so that it doesn't block the UI thread.- 6 replies
-
- server
- firemonkey
-
(and 2 more)
Tagged with:
-
Setting the Form's position+size that way is not quite the same thing as actually maximizing the window. That is two different states. If you really want to control the Form's size/position when maximized, have the Form handle the WM_GETMINMAXINFO message. You can use the Screen.WorkAreaRect to help you decide what bounds to set from there. Alternatively, try using the Screen.WorkAreaRest to help you set the Form's Contraints property as needed.
-
Generics: Classes vs Records - Differences in use
Remy Lebeau replied to Lars Fosdal's topic in Algorithms, Data Structures and Class Design
The RTL's TList<T> class does not provide access to elements by reference in the TList<T>.Items[] property, only access by value. You would have to use the TList<T>.List property instead, which gives you access to the underlying dynamic array so you can access the raw element data directly. -
Problem downloading GetIt package from GitHub
Remy Lebeau replied to Jud's topic in Delphi IDE and APIs
Is that a typo? Shouldn't it be "json" instead of "josn"? -
In the "shell" key, set its "(Default)" value to "option 3". See Specifying the Position and Order of Static Verbs
-
Create component ERROR in expert in D10.2.3
Remy Lebeau replied to limelect's topic in RTL and Delphi Object Pascal
Not sure if this will help, but you might try calling ActivateClassGroup(TControl) before calling CreateComponent() to create a TControl-derived component. At least in DFM streaming, ActivateClassGroup() can be used to limit the class types that are looked at, to avoid ambiguities with VCL and FMX classes of the same name. -
Create component ERROR in expert in D10.2.3
Remy Lebeau replied to limelect's topic in RTL and Delphi Object Pascal
FMX didn't exist yet in D7, there was only VCL. Not easily, and not always. If a component's class name is fully qualified, you could look at the prefix to see if it begins with 'Vcl.' vs 'Fmx.' but that only works for components that are distinctly VCL or FMX, it will not work for classes that are shared by both frameworks, ie classes whose qualified names begin with 'System.', 'Data.', 'Xml.', etc. Otherwise, you could drill into each component's RTTI to see if they derive from Vcl.Controls.TControl vs Fmx.Controls.TControl, but again that only works for VISUAL components that are distinctly VCL vs FMX, but will not work for NON-VISUAL components that derive from TComponent and not from TControl. And, of course, the situation may get more complicated in cases where users (despite being told not to) decide to mix VCL and FMX together into a single project. -
Should Exit be used instead of 'record Exists?' boolean?
Remy Lebeau replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
In cases where I need the index of a found item, I prefer to have my Find* functions accept an optional parameter to output the index. That way, I can still return a pointer to the actual data and not have to re-index back into the data storage after Find* exits, but I can still use the index for other things as needed. For example: function FindRecord(aID: Integer; vIndex: PInteger = nil): PDataRec; var i: Integer; begin Result := nil; if vIndex <> nil then vIndex^ := -1; for i := Low(Data) to High(Data) do begin if Data[i].DataID = aID then begin Result := @Data[i]; if vIndex <> nil then vIndex^ := i; Exit; end; end; end; Or: function FindRecord(aID: Integer; var vIndex: Integer): PDataRec; overload; var i: Integer; begin Result := nil; vIndex := -1; for i := Low(Data) to High(Data) do begin if Data[i].DataID = aID then begin Result := @Data[i]; vIndex := i; Exit; end; end; end; function FindRecord(aID: Integer): PDataRec; overload; var ignore: Integer; begin Result := FindRecord(aProjectID, aID, ignore); end; -
What is wrong with TStringList
Remy Lebeau replied to pyscripter's topic in RTL and Delphi Object Pascal
It is worse than that. It loads the entire file into a local byte array, then it decodes the entire byte array into a single Unicode string, and then it parses that string to extract the lines into individual substrings. So, by the time the TStringList has finished being populated, and before TStrings.LoadFrom...() actually exits, you could be using upwards of 4-5 times the original file size in memory! Granted, this is temporary, and all of that memory gets freed when LoadFrom...() finally exits. But there is small window where you have a LOT of memory allocated at one time. -
What is wrong with TStringList
Remy Lebeau replied to pyscripter's topic in RTL and Delphi Object Pascal
You can do something similar using TStreamReader and its ReadLine() method, eg: var Stream := TReadOnlyCachedFileStream.Create('c:\temp\t'); try var Reader := TStreamReader.Create(Stream); try while not Reader.EndOfStream do begin S := Reader.ReadLine; // use S as needed... end; finally Reader.Free; end; finally Stream.Free; end; -
Should Exit be used instead of 'record Exists?' boolean?
Remy Lebeau replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
Yes. Though, in the case of multiple identifiers, it may make sense to wrap them in another record, if that suits your needs, eg: type TDataRecID = record ProjectID: Integer; // or wharever ID: integer; end; PDataRec = ^TDataRec TDataRec = record DataID: TDataRecID; DataName: string; end; var Data: TArray<TDataRec>; function FindRecord(const aID: TDataRecID): PDataRec; var i: Integer; begin Result := nil; for i := Low(Data) to High(Data) do begin if (Data[i].DataID.ProjectID = aID.ProjectID) and (Data[i].DataID.ID = aID.ID) then begin Result := @Data[i]; Exit; end; end; end; function AddRecord(const aID: TDataRecID): PDataRec; var vNewData: TDataRec; begin vNewData := Default(TDataRec); vNewData.DataID := aID; Data := Data + [vNewData]; Result := @Data[High(Data)]; end; function EnsureRecord(const aID: TDataRecID): PDataRec; begin Result := FindRecord(aID); if Result = nil then Result := AddRecord(aID); end; procedure SaveData1(const aID: TDataRecID; const aName: string); var pData: PDataRec; begin pData := EnsureRecord(aID); pData.DataName := aName; end; Otherwise, yes, you could just handle them individually: type PDataRec = ^TDataRec TDataRec = record DataID: Integer; DataName: string; ProjectID: Integer; // or whatever end; var Data: TArray<TDataRec>; function FindRecord(aProjectID, aID: TDataRecID): PDataRec; var i: Integer; begin Result := nil; for i := Low(Data) to High(Data) do begin if (Data[i].ProjectID = aProjectID) and (Data[i].DataID = aID) then begin Result := @Data[i]; Exit; end; end; end; function AddRecord(aProjectID, aID: Integer): PDataRec; var vNewData: TDataRec; begin vNewData := Default(TDataRec); vNewData.DataID := aID; vNewData.ProjectID := aProjectID; Data := Data + [vNewData]; Result := @Data[High(Data)]; end; function EnsureRecord(aProjectID, aID: Integer): PDataRec; begin Result := FindRecord(aProjectID, aID); if Result = nil then Result := AddRecord(aProjectID, aID); end; procedure SaveData1(aProjectID, aID: Integer; const aName: string); var pData: PDataRec; begin pData := EnsureRecord(aProjectID, aID); pData.DataName := aName; end; -
Should Exit be used instead of 'record Exists?' boolean?
Remy Lebeau replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
Just my 2-cents worth - I prefer to use something more like this: type PDataRec = ^TDataRec TDataRec = record DataID: integer; DataName: string; end; var Data: TArray<TDataRec>; function FindRecord(aID: Integer): PDataRec; var i: Integer; begin Result := nil; for i := Low(Data) to High(Data) do begin if Data[i].DataID = aID then begin Result := @Data[i]; Exit; end; end; end; function AddRecord(aID: Integer): PDataRec; var vNewData: TDataRec; begin vNewData := Default(TDataRec); vNewData.DataID := aID; Data := Data + [vNewData]; Result := @Data[High(Data)]; end; function EnsureRecord(aID: Integer): PDataRec; begin Result := FindRecord(aID); if Result = nil then Result := AddRecord(aID); end; procedure SaveData1(aID: Integer; const aName: string); var pData: PDataRec; begin pData := EnsureRecord(aID); pData.DataName := aName; end; -
More accurately, UTF-8 is the default encoding used by Lazarus, not by FreePascal itself. On Windows, FPC's RTL sets the default encoding to the system encoding, just as Delphi does (on Linux/OSX, the default encoding is set to UTF-8). Lazarus overrides that RTL setting. https://wiki.freepascal.org/Unicode_Support_in_Lazarus#RTL_with_default_codepage_UTF-8 That is still a beta feature, and it doesn't work quite as well as everyone had hoped it would. Maybe in the future, it will be better.
-
Delphi 10.4.1 LIBSUFFIX AUTO
Remy Lebeau replied to pyscripter's topic in RTL and Delphi Object Pascal
The actual {$LIBSUFFIX AUTO} directive was added to the compiler in 10.4, but they didn't advertise its existence yet, so nobody has really played with it until now. So, if the linkage is broken now, it was probably broken in 10.4 to begin with and nobody noticed. 10.4.1 just adds the ability to set the libsuffix to AUTO in the Project Options dialog. I don't have 10.4 or 10.4.1 installed, so I can't test this myself. I created a ticket for this: RSP-30820: Fail to load package with LIBSUFFIX AUTO -
Main form appears when i openn a sub-sub form. :-(
Remy Lebeau replied to Ian Branch's topic in General Help
How do you want the Forms to behave in relation to each other? Do you want the MainForm to ever be able to appear on top of Form2? If not, then set the MainForm as the PopupParent for Form2. And set Form2 as the PopupParent for Form3, if you don't want Form2 to ever appear on top of Form3. See this blog article: PopupMode and PopupParent. -
Well, that is pretty easy to accomplish using TRttiContext.GetType() or TRttiContext.FindType(), and then using TRttiType.GetField() and TRttiType.GetProperty().
-
Are you looking for an RTTI solution to detect at run-time whether or not a given class type has declared a given class variable/property at compile-time? What is the end goal here that you are trying to accomplish?
-
Yup. Microsoft Office is notorious for using its own UIs that are very different from the rest of the OS experience. Office is like Microsoft's test bed for new UI ideas. For example, Office used ribbon controls before they were officially integrated into the Win32 API. Basically. no. Not in the standard Win32 API , no.
-
Where is localBMP coming from? You don't really need to make a copy of a TBitmap just to draw it, so you could just use localBMP as-is and get rid of tempBMP. Unless localBMP is actually supposed to be aBMP instead (typo?), then using tempBMP makes more sense if you are trying to draw a TBitmap onto itself. When calling DrawBitmap(), RecFull is supposed to be a rectangle within the TBItmap that you are drawing from, but you are setting it to the rectangle of the TBitmap that you are drawing onto. Change aBMP to tempBMP when populating RecFull.
-
Or, you could just treat them as-is as RCDATA resources instead. Any data can be stored in RCDATA, it is just raw bytes. Now, whether you can load a PNG image from an RCDATA resource at runtime is another issue, depending on HOW you load it.