-
Content Count
2914 -
Joined
-
Last visited
-
Days Won
130
Everything posted by Remy Lebeau
-
Best way to prevent multiple instances? Mutex not working
Remy Lebeau replied to bilbo221's topic in VCL
No, it won't. SeCreateGlobalPrivilege applies only to file mapping objects, not to mutexes. Anyone can create a mutex in the Global namespace. See https://stackoverflow.com/a/41370046/65863 I see no mention of this anywhere in this discussion before your comment. -
Getting Stream read error on really simple program
Remy Lebeau replied to Mr. Daniel's topic in RTL and Delphi Object Pascal
You are not resetting the ms1.Position property back to 0 after calling ms1.CopyFrom(fs, fs.Size) and before calling ms2.CopyFrom(ms1, ms1.Size). After ms1.CopyFrom() is done reading from the TFileStream, ms1.Position is at the end of the stream, it is not reset back to 0 automatically. You are then asking ms2 to read ms1.Size bytes from the end of the ms1 stream, hence the error. Calling TStream.CopyFrom() with its Count parameter set to <= 0 will reset the source TStream.Position to 0 and read all of the source TStream's bytes. But you are not doing that, you are setting the Count parameter to the source TStream.Size instead, so CopyFrom() will read from the source TStream's current Position, and fail if the requested number if bytes is not read successfully.- 5 replies
-
- stream
- tmemorystream
-
(and 1 more)
Tagged with:
-
Correct. Andreas has already gone on record stating that he no longer has a license to paid versions of RADStudio and so will not be able to update IDEFixPack for new IDE versions without their Community Editions. And Embarcadero has already gone on record stating that releasing a 10.4 Community Edition has been delayed, with no ETA stated as of yet.
-
Best way to prevent multiple instances? Mutex not working
Remy Lebeau replied to bilbo221's topic in VCL
A simple mutex will work fine in those cases if you create the mutex in the 'Global\...' namespace so it exists across account/session boundaries. See Kernel Object Namespaces. -
What is the TApplication.MainFormOnTaskbar property set to? If MainFormOnTaskbar is True, then minimizing the MainForm will minimize the MainForm window to the Taskbar. In which case, checking TForm.WindowState or IsIconic(MainForm.Handle) should work. But if MainFormOnTaskbar is False, then minimizing the MainForm will hide the MainForm window and minimize the TApplication window to the Taskbar. In which case, checking IsIconic(Application.Handle) should work.
-
Help needed. Re-raising exception gives AV.
Remy Lebeau replied to a topic in RTL and Delphi Object Pascal
Sure, if you want code higher up on the call stack to catch and handle the original exception as-is. But there are use-cases where it makes sense to raise a new exception instead, in which case you should use the Exception.RaiseOuterException() method so that you can capture the original exception in the InnerException of the new exception so that it is not lost if the higher code needs it. -
Blocking the Windows Screen Saver in Delphi
Remy Lebeau replied to dummzeuch's topic in Tips / Blogs / Tutorials / Videos
That is because Win32 structures do not use 1-byte alignment, they use 8-byte alignment. See What structure packing do the Windows SDK header files expect? -
XMLDocument and popup windows
Remy Lebeau replied to Jacek Laskowski's topic in RTL and Delphi Object Pascal
Have you considered simply changing the TXMLDocument to use a different DOMVendor than MSXML? -
TIdSSLIOHandlerSocketOpenSSL and TLS 1.3 ?
Remy Lebeau replied to Lars Fosdal's topic in Network, Cloud and Web
Sorry, I can't help with that. I know nothing about OpenSSL 1.1.x or the new APIs it introduced. I did not write the new SSLIOHandler for 1.1.x. Are you able to access the same server using other apps that use TLS 1.3? Maybe the server's certificate really is faulty. -
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;