-
Content Count
2343 -
Joined
-
Last visited
-
Days Won
95
Everything posted by Remy Lebeau
-
Using a TFrame is fine, but you can't combine that with a VCL TListBox. Putting the TFrame objects onto a TPanel or even a TScrollBox would make more sense.
-
The FileExists() call becomes redundant if you make _IsFileValidForParsing() return False for a non-existing file. But really, I would suggest completely re-writing this loop to actually open the File, decide whether to call _ParseAsA() or _ParseAsB() based on the content of the opened file rather than just its filename, and wrap the loop code in a try/except to handle IO errors. For instance: procedure MainMethod; var File: string; begin for File in GetMainPath do begin if _IsFileValidForParsing(File) then // <-- OK to leave in only if it checks just the filename and not the content ... begin try FS := TFileStream.Create(File, fmOpenRead or fmShareDenyWrite); try if _FileIsA(FS) then _ParseAsA(FS) else if _FileIsB(FS) then _ParseAsB(FS); finally FS.Free; end; except // handle error as needed ... end; end; end; end;
-
Delphi compiler need to be opensourced
Remy Lebeau replied to AlekXL's topic in RTL and Delphi Object Pascal
Can you lock this thread from further posts? -
Class inheritance and hides method
Remy Lebeau replied to Jacek Laskowski's topic in Algorithms, Data Structures and Class Design
Note that C++11 addes the 'override' keyword to the language, and its use is highly recommended to discover typos at compile-time. If a derived class method wants to override a base class virtual method, an explicit 'override' allows the compiler to validate that the overriding method actually matches the signature of an available base virtual method, if not the compile fails. Without the explicit 'override', a mistyped overriding method would be an overload instead, which can lead to logic errors not detectable until run-time. -
Class inheritance and hides method
Remy Lebeau replied to Jacek Laskowski's topic in Algorithms, Data Structures and Class Design
If you want to use the same name with different parameters, you have to declare both methods as 'overload', or else declare the TBar method as 'reintroduce', which will still hide the TFoo method. Otherwise, I would just rename the TBar method to something more meaningful: TFoo = class procedure Terminate(aNow: Boolean); virtual; end; TBar = class(TFoo) procedure TerminateNow(); end; procedure TBar.TerminateNow(); begin inherited Terminate(True); end; -
As they should be. Makes sense, since you are saving to an ANSI database field, so you are invoking a lossy Unicode->ANSI conversion. Because you are still invoking a conversion to ANSI. Have you tried switching the database column to nvarchar, and using TField.AsWideString?
-
Delphi compiler need to be opensourced
Remy Lebeau replied to AlekXL's topic in RTL and Delphi Object Pascal
Borland tried that once before. The CLX framework in Delphi 6 was a cross-platform version of the VCL. Most of the interfaces remained the same, just the internal implementations changed. That didn't work out so well for them, CLX had to be scrapped. -
When is a Delphi form actually visible to the user on Android?
Remy Lebeau replied to Yaron's topic in Cross-platform
Multi-threading issues for TBitmap were addressed in 10.2 Tokyo: http://docwiki.embarcadero.com/RADStudio/Tokyo/en/What's_New#Multi-Threading_Support_for_TBitmap.2C_TCanvas_and_TContext3D -
When is a Delphi form actually visible to the user on Android?
Remy Lebeau replied to Yaron's topic in Cross-platform
TBitmap works in a thread, so you are probably just not using it correctly. But, if threading is not an option, then I suggest to break up your calculations into small chunks that you can execute asynchronously without blocking the UI thread, such as with TThread.ForceQueue(). Execute a chunk and queue the next chunk, then return to the main message loop. When ready, the next chunk will execute, queue the next chunk, return to the main message loop, and so on, until the calculations are finished. -
Android does not support modal dialogs, you MUST use asynchronous dialogs instead, such as via TDialogServiceAsync.MessageDialog().
-
When is a Delphi form actually visible to the user on Android?
Remy Lebeau replied to Yaron's topic in Cross-platform
The answer you were given on StackOverflow suggested you use a background thread, which is what you should be using. Start a background thread on app startup to perform your time-consuming work. Have the thread use TThread.Synchronize() or TThread.Queue(), or simply assign a handler to the TThread.OnTerminate event, to communicate back to the main thread when the background thread is finished. Have your UI display the trackbar by default, and then hide the trackbar when the background thread is finished. You don't need to in this situation. The OnIdle event is triggered whenever the main message loop receives a new message and then finishes processing all pending messages. Don't rely on the timing of that event to drive your app's logic. Application.ProcessMessages() is broken in 10.3. You shouldn't be using it at all anyway. Do the pre-calculation in a background thread, and have the background thread post status updates to the main thread as needed. Let the main thread update the UI on its own schedule. -
My android app restarts on permission request
Remy Lebeau replied to Yaron's topic in Cross-platform
Which circumstances exactly? ForceDirectories() SHOULD succeed without error in that case. Its job is to ensure a path exists, not to report an error if it does exist. The only time it should ever raise an exception is if the input path is blank. Anything else should cause it to simply return True if the path already exists or was successfully created, or False otherwise. If it is not working that way, that is a bug for Embarcadero to fix. -
Doubtful, since transitions are meant to be used asynchronously. TIntAnimation has an OnFinish event, you are supposed to use that instead of using a blocking wait loop on the Running property. Good thing SetActiveTabWithTransitionASync() has an option to skip the actual transition.
-
My android app restarts on permission request
Remy Lebeau replied to Yaron's topic in Cross-platform
and a look at it in the source code verifies it. Last time I checked, ForceDirectories() DOES NOT raise an exception if the specified directory already exists. Are you sure the documentation you quoted is referring to ForeceDirectories() and not TDirListBox? Which source code did you "verify" exactly? The ForceDirectories() documentation does warn against calling it with a blank string, which I accidentally did in my example above if TPath.GetSharedPicturePath() returns a blank string. -
TArray vs TList with VirtualStringTree
Remy Lebeau replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
Only if you use the syntax you showed, yes. An alternative would be to use the TList<T>.List property instead, which gives you somewhat more direct access to the internal array: procedure TForm1.FormCreate(Sender: TObject); var ... vList: TList<TRecLine>; vNode: PVirtualNode; ... begin ... vList.List[0].VirtualNode := vNode; ... end; Or, use the TList<T>.PList property instead, which is as bare-bones as you can get to the internal array: type TRecLine = record ... end; PRecLine = ^TRecLine; procedure TForm1.FormCreate(Sender: TObject); var ... vList: TList<TRecLine>; vNode: PVirtualNode; ... begin ... (PRecLine(vList.PList)+0{Index}).VirtualNode := vNode; ... end; Do note that System.TArray<T> (not to be confused with System.Generics.Collections.TArray<T>) has supported insert and concatenation operations since XE7. See String-Like Operations Supported on Dynamic Arrays and Dynamic Arrays in Delphi XE7. VST doesn't care how you store your data, only that you should store a user-defined value inside each VST node so you can get back to your data given a node when needed. The best way to do that would be to store an actual TRecLine pointer in the node, where that pointer points to an item in your TList/TArray. But, if you are going to be adding/removing items from your TList/TArray, then that gets dangerous since you will be invalidating stored pointers. The next best option would be to store a TList/TArray index in each node instead, but then you have to worry about invalidating stored indexes. The next best option might be to store a unique ID number in each TRecLine, and then store those IDs in each node, and look for them in your TList/TArray when needed, but then you get into performance issues having to re-iterate the TList/TArray all the time. Certainly other options may exist, but what they are really depends on the design of your data and how you intend to use/manipulate it. -
TArray vs TList with VirtualStringTree
Remy Lebeau replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
Even though TList<T> uses a dynamic array internally, accessing its elements is very different than accessing the elements of TArray<T>. In your case, accessing vArray[n] is direct access to your record items, but vList[n] uses the TList<T>.Items property, which returns a copy of your record items, and you can't modify a copy inline the way you are trying to. You would have to manually save that copy to a variable and reassign it back to the TList<T> after modifying it, eg: procedure TForm1.FormCreate(Sender: TObject); var vArray: TArray<TRecLine>; vList: TList<TRecLine>; vNode: PVirtualNode; vRec: TRecLine; begin ... vArray[0].VirtualNode := vNode; // OK //vList[0].VirtualNode := vNode; // NOT OK! vRec := vList[0]; vRec.VirtualNode := vNode; vList[0] := vRec; ... end; -
My android app restarts on permission request
Remy Lebeau replied to Yaron's topic in Cross-platform
It shouldn't. It has a Boolean return value, it should just return False on failure. @Yaron the code you have showed can be simplified a little bit: // Request permission PermissionsService.RequestPermissions( [FPermission_WRITE_EXTERNAL_STORAGE, FPermission_READ_EXTERNAL_STORAGE], procedure(const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>) var fmt: TFormatSettings; begin if (Length(AGrantResults) = 2) and (AGrantResults[0] = TPermissionStatus.Granted) and (AGrantResults[1] = TPermissionStatus.Granted) then begin { Save file } PIKAFolder := System.IOUtils.TPath.GetSharedPicturesPath; if PIKAFolder <> '' then PIKAFolder := TPath.Combine(PIKAFolder, clientGameName); if ForceDirectories(PIKAFolder) then begin fmt := TFormatSettings.Create; fmt.DateSeparator := '.'; fmt.TimeSeparator := '.'; PIKAFile := TPath.Combine(PIKAFolder, clientGameName + '_' + DateTimeToStr(Now, fmt) + saveImageFileExt); try MyImage.SaveToFile(PIKAFile); clientFadingMessage := strPictureSaved; except clientFadingMessage := strErrorSavingPicture; end; end else clientFadingMessage := strPictureFolderNotFound; end else clientFadingMessage := strErrorSavingPicture; end ); -
Since you are developing for mobile. DO NOT run blocking tasks in the main UI thread, it must service only the UI. Long-running tasks must be done in the background instead. In your case, you have a LONG-running task (5.5 seconds) that blocks the UI until the task is finished. Tokyo let you get away with it using a hack. Rio doesn't. So just don't do it anymore. You need to change the code (even for Tokyo) to work with the main UI thread correctly, stay away from ProcessMessages() at all costs (especially now that Embarcadero has broken it and doesn't want to fix it). Do things asynchronously so flow returns to the main UI message loop in a timely manner (otherwise Android is likely to kill your app!). For example: procedure TForm2.Button1Click(Sender: TObject); var StartTimer: TProc; begin ProgressBar1.Max := 100; ProgressBar1.Value := 0; StartTimer := procedure begin Timer1.Interval := 500; // <-- can be set at design-time Timer1.Enabled := True; end; {$IF CompilerVersion < 33} // 10.2 Tokyo or earlier TabControl1.TabIndex := 1; StartTimer; {$ELSE} // 10.3 Rio or later TabControl1.SetActiveTabWithTransitionAsync(TabControl1.Tabs[1], TTabTransition.None, TTabTransitionDirection.Normal, StartTimer); {$IFEND} end; procedure TForm2.Timer1Timer(Sender: TObject); begin ProgressBar1.Value := ProgressBar1.Value + 10; if ProgressBar1.Value >= ProgressBar1.Max then Timer1.Enabled := False; end; Yes, sorry about that. Fixed above. What is there not to understand? The TTabControl is transitioned to the desired Tab and a timer is started, then flow is returned to the main UI message loop. When the timer fires after 500ms, the ProgressBar is incremented and flow is returned to the main UI message loop. The timer fires again after another 500ms, and again, each time returning to the main UI message loop. Eventually, the ProgressBar reaches its Max and the timer is stopped.
-
Initialize local variables at declaration
Remy Lebeau replied to PeterPanettone's topic in Delphi IDE and APIs
There is no technical reason that I can think of, Borland/CodeGear/Embarcadero could have expanded on the Pascal syntax, like FreePascal did, but they simply never got around to implementing it that way. Remember that FreePascal is its own Pascal compiler, it is not based on Delphi, so it is free to do things however it wants to. It has quite a few Pascal language niceties that Delphi doesn't. Even Generics and Unicode support are other areas where FreePascal differs greatly from Delphi. But, FreePascal does have special compatibility modes meant for migrating Delphi code to FreePascal. In those modes, it tries to closely emulate Delphi 7 or 2009, depending on whether Unicode is being emulated or not. -
Well, then you went down the wrong rabbit hole, and you need to start climbing your way out of it. Such as? You really need to stop using ProcessMessages(), and start using proper code designs. Even if you don't want to use timers/threads, there is always TThread.ForceQueue(), for instance. Anything that allows code flow to return to the main UI message loop naturally will be better than manually pumping the message queue artificially. Then file a bug report with Embarcadero. But don't litter your code with ProcessMessages() calls just to satisfy the framework. Find other alternatives. Stay away from ProcessMessages() whenever possible. That was true even in the old days of the VCL. More so now in the days of FMX on mobile.
-
Initialize local variables at declaration
Remy Lebeau replied to PeterPanettone's topic in Delphi IDE and APIs
Note that FreePascal supports initializing variables in "old style" declarations. A shame that Delphi does not also support that, seems like something that might have been easy for Embarcadero to add while they were messing around with how variable declarations work. -
HELP: Decoding of data stored in array of char - RFID tag's
Remy Lebeau replied to CRO_Tomislav's topic in VCL
Sorry, I missed the part where you had changed the function declaration to make the UIDI parameter be a single Byte, which is of course wrong. I thought you were still passing that parameter by PChar instead. So, you need to EITHER do this: Function GetSystemInformation(...; UIDI : PByte; ...): LongInt; stdcall;external 'RR3036.dll'; ... Get_Designated_tag_info_status := GetSystemInformation(..., PByte(@(DSFIDAndUID[0].UIDasBytes)), ...); alternatively Get_Designated_tag_info_status := GetSystemInformation(..., @(DSFIDAndUID[0].UIDasBytes[0]), ...); Or this: Function GetSystemInformation(...; var UIDI : Byte; ...): LongInt; stdcall;external 'RR3036.dll'; ... Get_Designated_tag_info_status := GetSystemInformation(..., DSFIDAndUID[0].UIDasBytes[0], ...); -
This is definitely not the right way to write code. Avoid ProcessMessages() whenever possible. The example given can be replaced with a simple UI Timer instead, eg: ProgressBar1.Value : =0; ProgressBar1.Max := 100; Timer1.Enabled := True; ... procedure TForm1.Timer1Timer(Sender: TObject); begin ProgressBar1.Value := ProgressBar1.Value + 1; if ProgressBar1.Value >= ProgressBar1.Max then begin Timer1.Enabled := True; Exit; end; //... end; But, if your real project is using the ProgressBar to tracking status of actual tasks, those tasks should be done in the background, and updates to the UI synced with the main UI thread accordingly.
-
How to release a return [TJSONArray] object from a get method?
Remy Lebeau replied to Rafael Mattos's topic in MARS-Curiosity REST Library
The caller that Bill refers to is whatever code is calling your TProductResource.GetProduct() method. That code is responsible for freeing the returned array when done using it, eg: Arr := SomeProductResource.GetProduct(ClientId); ... Arr.Free; UNLESS - TBllProduct.Singlenton.RetrieveJSONArray() returns an array that is owned by the Singleton, in which case the code that calls your TProductResource.GetProduct() must not free the array at all. -
Is another process running as admin?
Remy Lebeau replied to Mike Torrettinni's topic in General Help
The easiest way to detect this is to simply send the message and handle the case where SendMessage() fails with an ERROR_ACCESS_DENIED error: "When a message is blocked by UIPI the last error, retrieved with GetLastError, is set to 5 (access denied)." It is not, actually.