-
Content Count
2990 -
Joined
-
Last visited
-
Days Won
134
Posts posted by Remy Lebeau
-
-
3 hours ago, Skrim said:There is one field FName in the class below. What if I have 50 different Fxxxxx? Do I have to write Setter and Getter for every field?
If they all have different names/types, then yes (provided you need getters/setters at all - see dummzeuch's examples).
But, if they are all related, eg Name1, Name2, etc then you can use an indexed property instead, eg:
Type TEmployee = class private FNames[0..49] : string; Function GetName(Index: Integer) : string; Procedure SetName(Index: Integer; const Value : string); public Property Names[Index: Integer] : string read GetName write SetName; end; implementation {$R *.dfm} Function TEmployee.GetName(Index: Integer): string; begin Result := FNames[Index]; end; Procedure TEmployee.SetName(Index: Integer; const Value: string); begin if Value = '' then raise Exception.Create('Value must not be empty'); FNames[Index] := Value; end;
Alternatively, you could do something like this:
const EmployeeNameIdx := 0; EmployeeEmailIdx := 1; ... Type TEmployee = class private FValues[0..49] : string; Function GetValue(Index: Integer) : string; Procedure SetValue(Index: Integer; const Value : string); public Property Name : string read GetValue write SetValue index EmployeeNameIdx; Property Email : string read GetValue write SetValue index EmployeeEmailIdx; end; implementation {$R *.dfm} Function TEmployee.GetValue(Index: Integer): string; begin Result := FValues[Index]; end; Procedure TEmployee.SetValue(Index: Integer; const Value: string); begin if Value = '' then raise Exception.Create('Value must not be empty'); FValues[Index] := Value; end;
QuoteCan I declare a record inside the class?
Yes, eg:
Type TEmployee = class private ... public type TDetails = record ... end; ... end;
-
1
-
-
1 hour ago, David Heffernan said:Where does the exception get raised?
In his example, nowhere. You would need a setter for that.
-
@alank2 What you describe implies that either 1) TMultipartFormData is trying to free an invalid FStream object, or 2) you are trying to free an invalid TMultipartFormData object. Unfortunately, there is not enough detail provided to help you. You will have to debug the code for yourself, and double check the user of the pointers involved.
-
1
-
-
12 minutes ago, Berocoder said:It is VERY important that the linenumber of original exception is not lost when log callstack.
So that is why I am suspicious at ChatGpts suggestion. It also look more complicated that just modify the Errormessage.You should be able to do that, as each Exception object in the chain is preserved as-is and would carry its own CallStack, which JCL can populate as each Exception is raised.
12 minutes ago, Berocoder said:But it would be nice to find a more complete demo of how RaiseOuterException is used and how global exceptionHandler can traverse inner exceptions.
https://docwiki.embarcadero.com/Libraries/Athens/en/System.SysUtils.Exception.RaiseOuterException don't have much information.https://blog.eurekalog.com/2010/05/new-exception-class-in-delphi-2009-and_05.html
12 minutes ago, Berocoder said:Another thing. Raise Exceptions is slow. So raise one Exception and then raise again should be twice as slow ?
Not really, because you are still raising twice. Whether you raise and then re-raise 1 object, or raise 2 objects, is up to you.
-
2
-
-
I (relunctantly) agree with ChatGPT on this one - you should not modify the original exception, but instead you should raise a new exception that captures the original exception into the new exception's InnerException property.
However, the way that ChatGPT demonstrates this is wrong:
- Firstly, SysUtils.Exception does not have a public SetInnerException() method. ChatGPT suggests defining a new class, but then doesn't use that class. And that class is just trying to replicate a feature that already exists natively in SysUtils.Exception.
- Second, ChatGPT's approach does not release ownership of the original Exception, so the try..except will still try to free it when the except block exits, leaving the new Exception holding a dangling InnerException pointer to a now-dead Exception.
The correct way to capture an InnerException is to use the SysUtils.Exception.RaiseOuterException() method, eg:
try sqhpartyroles.sql := fsqlhandle.buildsql; sqhpartyroles.executesql; except Exception.RaiseOuterException(Exception.CreateFmt('error from sql %s', [sqhpartyroles.sql])); end;
(I don't like this syntax, but it is what it is...)
Higher up the call chain, you can then catch the new Exception and traverse its InnerException chain if you want to report/log all of the individual error messages, eg
procedure MyForm.Application1Exception(Sender: TObject; E: Exception); begin repeat // use E as needed, then... E := E.InnerException; until E = nil; end;
-
5
-
4 hours ago, Mark- said:Not sure what you are trying to do Remy.
Forget it...
-
2 hours ago, Mark- said:No, it is not the same.
Really? They all look the same to me... weird...
-
2 hours ago, Mark- said:Right, that is what I was searching for, "...OS wants to display". Then regardless of language, I assume, it would be correct.
But, no joy.
Of course the dialog may be localized based on the user's language. Most OS dialogs are. Bu that doesn't mean the OS makes those localized strings available to apps. So, you'll likely need to provide your own localized strings if you want to honor the user's language in your own UI/logging.
2 hours ago, Mark- said:So I went through all the fonts on my computer, which TFontDialog does not show them all, and created this matrix,
Those are all the same values that @Kas Ob. showed you earlier, which Microsoft already documents in several places of MSDN. So you didn't need to go to that effort.
-
14 minutes ago, Mark- said:Yeah, I could not find anything. The issue is multiple languages.
I wonder what TFontDialog displays on French/Spanish/etc. machines.
TFontDialog is just a wrapper for the Win32 ChooseFont() API, so it displays whatever the OS wants to display.
-
2 hours ago, Mark- said:I have the table and is it is not the text that appears in TFontDialog.
I am seeking the text "description".
There is no Win32 API to get the same text that appears in the dialog. You have to use your own logic, using the numeric values shown by @Kas Ob. with a lookup array such as shown by @dwrbudr. The EnumFontFamiliesEx() API gives you the numeric value, you have to convert it to text yourself.
-
12 minutes ago, Die Holländer said:procedure TStatsGridForm.Create(aOwner:TComponent; aQty:Integer ;aFileName:string; aQuery:String; aTitle:string); begin ... end;
Don't forgot to call 'inherited Create(aOwner);'
-
3 hours ago, corneliusdavid said:The values from the DFM are streamed in and set after it's created and before FormCreate is called
Except in this case, as the properties shown are not declared as published, so not streamed by the DFM at all.
-
25 minutes ago, Die Holländer said:In this situation I would pass the parameters to the onFormCreate procedure.
That won’t work as you describe. You can't add parameters to the OnCreate event. What you are proposing requires defining a new constructor instead.
-
-
7 hours ago, Patrick PREMARTIN said:- ShowModal of a form for Windows, Mac, Linux
The OP asked about Android, and modal dialogs are not supported on Android.
-
I can't answer your question. But an alternative approach would be to create your own dialog however you want, and then write your own class that implements the IFMXDialogServiceAsync interface to show your dialog, and then register that class with FMX using TPlatformServices.Current.AddPlatformService() (be sure to unregister the default implementation using TPlatformServices.Current.RemovePlatformService() first).
-
1
-
-
Embarcadero doesn't want your location. This warning is starting to show up for a lot of network-enabled apps. A recent Windows update is enforcing permission to Wifi info, as it CAN be used to reveal your location, which is a privacy concern.
QuoteA new prompt appears the first time an app attempts to access your location or Wi-Fi information.
-
1
-
1
-
-
You are using the version of FormatDateTime() that relies on global variables initialized with your PC's locale settings. For instance, the '/' specifier is not a literal slash character, it is a placeholder that uses the global DateSeparator variable in the SysUtils unit. This is documented behavior.
You can update those globals to customize things like the date/time separators, but this affects the whole app, and is not thread-safe. When you need custom formatting that is locale-agnostic, you should use the version of FormatDateTime() that takes a TFormatSettings parameter, eg:
procedure TForm1.Button1Click(Sender: TObject); var lDate: TDateTime; lFmt: TFormatSettings; begin lFmt := TFormatSettings.Create; lFmt.DateSeparator := '/'; lDate := EncodeDate(2025, 3, 26); Edit1.Text := FormatDateTime('dd/mm/yyyy', lDate, lFmt); end;
The alternative is to wrap desired literal text with quotes in your format string, eg:
procedure TForm1.Button1Click(Sender: TObject); var lDate: TDateTime; begin lDate := EncodeDate(2025, 3, 26); Edit1.Text := FormatDateTime('dd"/"mm"/"yyyy', lDate); end;
-
5
-
2
-
-
1 hour ago, JohnLM said:What I am thinking next, is how to add URL's to the combobox while the program is running. That idea is tricky to me as I've never done that and don't know how, yet. Then, every time I copy/paste/[Go], the URL is to be checked if in the combobox list, and if not, add it, and if it is already there, ignore it. Something like that, I guess.
That is pretty trivial to implement, eg:
procedure TMainForm.GoBtnClick(Sender: TObject); var url: string; idx: integer; begin { change this https://www.youtube.com/watch?v=ZJhJILyS388 to this https://www.youtube.com/embed/ZJhJILyS388 } url := StringReplace(addresscb.Text, 'watch?v=', 'embed/', [rfIgnoreCase]); addressCb.Text := url; WVBrowser1.Navigate(url); idx := ComboBox1.Items.IndexOf(url); if idx = -1 then ComboBox1.Items.Add(url); end;
-
1
-
-
18 minutes ago, Lars Fosdal said:simply changing from string to string[n] in a record as a workaround to the non-nullable type Generics issue, will cause these warnings.
(Ansi|Unicode|Wide)String are managed types. String[n] (ShortString) is not. But switching to ShortString has its own share of compatibility issues not related to Generics.
-
6 hours ago, Lars Fosdal said:Several record types where I had to explicitly add a string length, resulting in numerous [dcc32 Warning]: W1057 Implicit string cast from 'ShortString' to 'string'
That has nothing to do with Generics (the topic of this discussion).
Specifying an explicit length on a string at compile-time will produce an AnsiChar-based ShortString, and always has. That's nothing new. Assigning an Ansi string to a Unicode string (or vice versa) requires a data conversion at runtime, which can potentially lose data, hence the warning.
-
1 hour ago, FredS said:What I found when originally searching: https://stackoverflow.com/a/64526312
That StackOverflow post is about the error on a Custom Managed Record, but the code shown above is not using a CMR. It is just a regular Managed Record due to the string field (has nothing to do with the custom Attribute). And according to RSS-3043 (which has been closed last week as "As Designed"):
QuoteThis is actually as designed, managed records (including records with strings) cannot be use for generics restricted to records (where record is in fact traditional memory managed records). It's a restriction from the past, done to avoid bugs
-
1
-
-
Calling ReadBytes() with an AByteCount value greater than 0 will wait until the specified number of bytes have arrived in the InputBuffer. So, if you are expecting 7000 bytes then just call ReadBytes() 1 time with AByteCount set to 7000.
But, if you don't know ahead of time how many bytes are coming, then you can call ReadBytes() with an AByteCount value less than 0 and it will return however many bytes are currently available in the InputBuffer (if it is empty, ReadBytes() will wait until any bytes arrive first).
If you don't want to change your current logic, then you can simply catch the EIdReadTimeout exception and call ReadBytes() again with the AByteCount value set to the IOHandler's current InputBuffer.Size.
-
7 hours ago, Kas Ob. said:There is zero guarantee that DllMain will be called from the the Main Thread or even the the same thread that called LoadLibrary !
DLL_PROCESS_ATTACH should be in the same thread that calls LoadLibrary(), but DLL_PROCESS_DETACH is not guaranteed to be in that same thread. Raymond Chen even blogged about this issue back in 2009 (emphasis added by me):
QuoteThe thread that gets the DLL_PROCESS_DETACH notification is not necessarily the one that got the DLL_PROCESS_ATTACH notification. This is obvious if you think about it, because the thread that got the DLL_PROCESS_ATTACH notification might not even exist any longer when the DLL is unloaded. How can something that doesn’t exist send a notification?
Even so, many people fail to realize this. You can’t do anything with thread affinity in your DLL_PROCESS_ATTACH or DLL_PROCESS_DETACH handler since you have no guarantee about which thread will be called upon to handle these process notifications. Of course, you’re not supposed to be doing anything particularly interesting in your DLL_PROCESS_ATTACH handler anyway, but things with thread affinity are doubly bad.
The classic example of this, which I’m told the Developer Support team run into with alarming frequency, is a DLL that creates a window in its DLL_PROCESS_ATTACH handler and destroys it in its DLL_PROCESS_DETACH handler. Now, creating a window in DllMain is already a horrifically bad idea since arbitrary code can run during the creation of a window (for example, there may be a global hook), but the lack of a thread guarantee makes it downright insane. The DLL calls DestroyWindow in its DLL_PROCESS_DETACH handler, but since that notification comes in on a thread different from the one that received the DLL_PROCESS_ATTACH notification, the attempt to destroy the window fails since you must call DestroyWindow from the same thread that created it.
Result: The DLL’s attempt to destroy its window fails, a message comes in, and the process crashes since the window procedure no longer exists.
-
1
-
Android. TDirectory.GetFiles('/storage/emulated/0/DCIM/Camera') returns an empty list
in Cross-platform
Posted · Edited by Remy Lebeau
Have you tried TPath.GetCameraPath() yet?
Does your app have permission to access the camera files?