Jump to content

Remy Lebeau

Members
  • Content Count

    2982
  • Joined

  • Last visited

  • Days Won

    134

Everything posted by Remy Lebeau

  1. Remy Lebeau

    Streaming binary data via TIdHTTPServer

    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.
  2. Remy Lebeau

    MsgWaitForMultipleObjects Usage

    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;
  3. Remy Lebeau

    proper way to check server up or down in rest application

    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;
  4. Remy Lebeau

    Form border Style bsNone Covers taskbar

    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.
  5. Not when you take the 2nd sentence into account. But whatever. I reworded my previous comment, just for you.
  6. Remy Lebeau

    proper way to check server up or down in rest application

    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.
  7. Remy Lebeau

    Form border Style bsNone Covers taskbar

    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.
  8. 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.
  9. Is that a typo? Shouldn't it be "json" instead of "josn"?
  10. Remy Lebeau

    Delphi Registry entries default...

    In the "shell" key, set its "(Default)" value to "option 3". See Specifying the Position and Order of Static Verbs
  11. 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.
  12. 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.
  13. 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;
  14. Remy Lebeau

    What is wrong with TStringList

    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.
  15. Remy Lebeau

    What is wrong with TStringList

    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;
  16. 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;
  17. 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;
  18. Remy Lebeau

    Use of Ansistring in Unicode world?

    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.
  19. Remy Lebeau

    Delphi 10.4.1 LIBSUFFIX AUTO

    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
  20. Remy Lebeau

    Main form appears when i openn a sub-sub form. :-(

    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.
  21. Remy Lebeau

    does a class property or a variable exist

    Well, that is pretty easy to accomplish using TRttiContext.GetType() or TRttiContext.FindType(), and then using TRttiType.GetField() and TRttiType.GetProperty().
  22. Remy Lebeau

    does a class property or a variable exist

    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?
  23. Remy Lebeau

    Font Dialog(s)

    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.
  24. Remy Lebeau

    Draw TBitmap

    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.
  25. Remy Lebeau

    Free Resource Builder Utility?

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