Jump to content

Leaderboard


Popular Content

Showing content with the highest reputation since 01/09/25 in Posts

  1. I decided to purchase a 3yr OV certificate from SSL.com and load it onto a Yubikey token to be 100% sure they work with Signotaur (have tested with self signed certs before). I had 2 tokens already and bought another one locally - SSL.com are overcharging a lot for them (USD$297 vs USD$106 locally). You need the FIPS versions (e.g 5C FIPS ) for code signing. Note that by default on the order page, they add their cloud service to the price - make sure to de-select that! You do have to go through the process of generating a Certificate Signing Request and then exporting the the attestation certificate and intermediate from the token to upload to their portal. This is quite well documented and pretty easy to follow. Their web portal is pretty horrible (tiny text and links - even with my glasses on). I had some issues initially - the first time they generated an RSA cert instead of an ECDSA (yubikey only supports 2048bit RSA, code signing needs at least 3072bit) - contacted support and then went through the attestation process again, eventually got an ECDSA cert - but that did not work either - signtool sign said success but verify said failed. Important - I discovered that if you change the yubikey pin/puk/managementpin after doing the attestation and before importing the cert, that will cause it to fail (doh!) - so had to go through the process again. It only takes a few minutes on your end, then an email to support for them to generated the cert again - and then you download and import the certificate onto the token. Remember to unplug and plug the token in again after importing (this triggers the import of the public key into the windows certificate store). Third time lucky, everything works fine. Note that to use Yubikeys with Signtool - you need to have the MiniDriver installed (you will get the smartcard pin prompt when calling sign tool) - you don't need the mini driver with Signotaur - you just need the PIV Tool (which has the pkcs#11 driver). I then enquired about backup tokens, and was told to delete the attestation on their portal and redo it for the backup token. So over the course of a few days and some back and forth (timezones make everything slow down under) - I now have 3 yubkey tokens with my certificate installed. This gives me a lot of comfort as I have a backup in case of hardware failure or theft - I have a Nano token which I can deploy in the data center where are servers live - much less likely to be stolen than the safenet token (which has a bright blue led that screams "take me"). Also thanks to @DelphiUdIT we have now confirmed that Signotaur also works fine with Certum tokens.
  2. If you build a project, all visible sources are compiled. Only when you just compile a project where the dcu and source are both available, the source is compiled only when its time stamp or memory content is newer than the binary. If you don't want to compile any library sources but always use the binary, you must remove the source from Tools > Options > Language > Delphi > Library > Library path as well as from Project > Options > Building > Delphi-Compiler > Search Path. Unfortunately that removes the ability to debug the library sources. To fix that, you have two options. Which you choose depends on where you add the binaries folder: A: Tools > Options > Language > Delphi > Library > Library path: Add the source folder to Browsing path in the same dialog. B: Project > Options > Building > Delphi-Compiler > Search Path: Add the source folder to Project > Options > Debugger > Source Path Obviously, solution A affects all projects, while B affects the current project only. Note: You have to compile the library with debug options to make that work. With solution A you can separate the debug binaries from the release ones by placing the debug binaries in a separate folder (just as the Delphi lib folder is organized) and add the debug binaries folder to the Debug DCU path entry of Tools > Options > Language > Delphi Options > Library. This will allow to use the debug binaries when the Use debug .dcus option is set in Project > Options > Building > Delphi-Compiler > Compiling.
  3. Vincent Parrett

    New Code Signing Certificate Recommendations

    Thanks to @DelphiUdIT I can now confirm that Signotaur works with Certum code signing tokens 😀
  4. dummzeuch

    AI in the IDE??

    Today I have - for the first time - used ChatGPT and then Claude in earnest. Everything before that was just testing. Actually I didn't expect this time to be of much help either, but it turned out that it was helpful up to a point. I have now got some code to give a the guy who applies for a Delphi position today to refactor, debug and explain. And then explain what's wrong with the "fixed" version of the code generated by ChatGPT. That's going to be interesting.
  5. Sherlock

    New Code Signing Certificate Recommendations

    That would have been nice to know...but alas, just finished the verification process. Note to self: always read a thread to the end, before responding.
  6. I can of course not say I made this as the channel header tell. My contribution is very minor. But I want to share this story anyway. And hopefully someone can use or even contribute to Bold framework. Yesterday I committed a bunch of updates to Bold for Delphi https://github.com/Embarcadero/BoldForDelphi/tree/develop History of Bold for Delphi Bold for Delphi is a Model-Driven Architecture (MDA) framework developed in the late 1990s by BoldSoft, a Swedish company. Jan Norden was one of thefounders. Bolds main focus was to enable developers to create business applications by defining the application's data structure and business rules at a higher abstraction level, rather than writing low-level code manually. In 2002, Borland acquired BoldSoft, and Bold for Delphi was included in Borland’s release of Delphi 7 Architect. This made the framework more accessible to Delphi developers and encouraged its adoption within the Delphi ecosystem. However, after Borland sold its development tools division to CodeGear. Later Embarcadero took over. Now Bold for Delphi was no longer actively developed as part of the Delphi IDE. Bold for Delphi was used in many hundreds of projects with Delphi 7. Developers was now abandoned with no support from Code/Gear/Embarcadero. But development never stopped completely. Before Borland bought Boldsoft Ahola Transport, a Finnish company bought source-code license from Boldsoft. They developed an in-house ERP application Attracs for transport business now with full access to Bolds source. Around 2010 Attracs company hired a consultant Daniel Mauric from Serbia mainly because hes deep knowledge about Bold. This turned out to be a very good move. Now the development of Bold for Delphi really gained momentum. Daniel has made countless optimizations, bug fixes but also some completely new additions to Bold. But one problem was that Bolds Intellectual Property (IP) was still hold by Embarcadero. We couldn't legally publish our changes even if we want to cooperate with other developers around the source. In May 2012 I Roland Bengtsson made a petition to release Bold with opensource license. And the petition got attention. After a while when I contacted Embarcadero we agreed on a meeting and it looks promising. But later Embarcadero changed their mind and set up unreasonable conditions like all changes with source remain Embarcaderos IP. And the years passed... Delphi product manager at Embarcadero changed to Marco Cantu. When I discussed this issue with him I noticed quickly he had a more realistic view on this. Marco realized that the only way forward was to make Bold opensource. I have no idea what happened behind the scenes but Marco convinced Embarcaderos management and published this blog 22 Sep 2020 So a big thank you to @Marco Cantu for his work! Our company now renamed to Ahola Digital continue to work with our main Bold application Attracs. Our Bold source continue to improves thanks to Daniel Mauric. These changes are now published in repository above. There is a lot more to do. Many inhouse tools and source are not commited in to repository yet. Main Purpose of Bold for Delphi The primary purpose of Bold for Delphi is to simplify the development of business applications by automating data persistence, object lifecycle management, and business rule enforcement. It emphasizes separation of concerns by letting developers focus on defining the model (business logic and rules) while the framework handles boilerplate tasks such as: Object-relational mapping (ORM) Automatic data synchronization with a database Real-time data updating in the user interface Implementation of business logic constraints and rules It essentially follows the MDA approach, where application logic is derived from the model, reducing repetitive code writing and increasing consistency across applications. Main Components of Bold for Delphi Bold Model and UML Integration: Developers define the application’s data model using UML diagrams. Rational Rose is supported but unfortunately IBM have The model is then translated into Delphi classes with associated behavior and persistence rules. Bold Business Classes: These are Delphi classes generated from the UML model. They represent the business objects, containing properties, associations, and behavior logic. Bold Object Layer: Handles the lifecycle of objects, including creation, deletion, and memory management. Ensures real-time synchronization between the application and the database. Bold Persistence Layer: Manages object persistence by mapping the Bold business classes to database tables. Abstracts database interactions, so developers don't need to write SQL manually. Bold Expression Language (OCL): A query and constraint language similar to SQL but designed for object models. Used to express rules, queries, and computed attributes in the model. Bold User Interface Components: Data-aware UI components, such as grids and editors, are bound directly to the Bold object model. Provides real-time updates as the model changes. Bold Subscription System: Ensures the UI and other dependent components react automatically when model data changes. Key Advantages of Bold for Delphi Rapid Development: Reduces repetitive coding by generating classes and managing persistence automatically. Model-Driven Approach: Ensures the application's logic and design are consistent and well-structured. Scalability: Supports complex object relationships and business rules with minimal effort. Real-Time Updates: Automatically synchronizes UI and data layers for responsive applications. Example Use Cases Bold for Delphi is particularly suited for: Enterprise resource planning (ERP) systems Customer relationship management (CRM) systems Applications with complex domain models and business rules It remains a valuable tool for Delphi developers who need to build sophisticated, model-driven applications efficiently. If you are interested to contribute or just have a question about Bold do not hesitate to ask me. I would do my best to help!
  7. bravesofts

    Introducing TRange<T>

    unit API.Utils; interface uses System.SysUtils, System.Types, System.Generics.Defaults; type TRange<T> = class public // Check if a value is within the range [aMin, aMax] using a custom comparer class function IsIn(const aValue, aMin, aMax: T; const aComparer: IComparer<T>): Boolean; overload; static; // Check if a value is within the range [aMin, aMax] using the default comparer class function IsIn(const aValue, aMin, aMax: T): Boolean; overload; static; end; implementation { TRange<T> } class function TRange<T>.IsIn(const aValue, aMin, aMax: T; const aComparer: IComparer<T>): Boolean; begin Result := (aComparer.Compare(aValue, aMin) >= 0) and (aComparer.Compare(aValue, aMax) <= 0); end; class function TRange<T>.IsIn(const aValue, aMin, aMax: T): Boolean; begin Result := IsIn(aValue, aMin, aMax, TComparer<T>.Default); end; end. to put this Super class in test i build a new console project: this unit here act as my objects: unit API.Objects.Comparer; interface uses System.Types, System.Generics.Defaults; type ICustomRecord = interface; // Forward ICustomRecordUpdate = interface function Edit(const aName: string; const aValue: Integer): ICustomRecord; end; ICustomRecord = interface function GetName: string; function GetValue: Integer; function GetCustomRecordUpdate: ICustomRecordUpdate; property Name: string read GetName; property Value: Integer read GetValue; property New: ICustomRecordUpdate read GetCustomRecordUpdate; end; IProduct = interface; // Forward IProductUpdate = interface function Edit(const aID: Integer; const aPrice: Currency): IProduct; end; IProduct = interface function GetID: Integer; function GetPrice: Currency; function GetIProductUpdate: IProductUpdate; property ID: Integer read GetID; property Price: Currency read GetPrice; property New: IProductUpdate read GetIProductUpdate; end; IClient = interface; // Forward IClientUpdate = interface function Edit(const aName: string; const aAge: Integer): IClient; end; IClient = interface function GetName: string; function GetAge: Integer; function GetIClientUpdate: IClientUpdate; property Name: string read GetName; property Age: Integer read GetAge; property New: IClientUpdate read GetIClientUpdate; end; // Compare Custom Records <Helper function> function CompareCustomRecord(const R1, R2: ICustomRecord): Integer; // Compare Products by thier Prices <Helper function> function CompareProductByPrice(const P1, P2: IProduct): Integer; // Compare Clients by thier Ages <Helper function> function CompareClientByAge(const C1, C2: IClient): Integer; // points comparison <Helper functions> function ComparePoints(const P1, P2: TPoint): Integer; overload; function ComparePoints(const P1, P2: TPointF): Integer; overload; // Returns a custom comparer for TPoint function PointComparer: IComparer<TPoint>; function GetTCustomRecord(const aName: string; aValue: Integer): ICustomRecord; function GetTProduct(aID: Integer; aPrice: Currency): IProduct; function GetTClient(const aName: string; aAge: Integer): IClient; implementation uses System.Math; type TCustomRecord = class(TInterfacedObject, ICustomRecord, ICustomRecordUpdate) strict private fName: string; fValue: Integer; function GetName: string; function GetValue: Integer; function GetCustomRecordupdate: ICustomRecordUpdate; function Edit(const aName: string; const aValue: Integer): ICustomRecord; public constructor Create(const aName: string; aValue: Integer); end; TProduct = class(TInterfacedObject, IProduct, IProductUpdate) private fID: Integer; fPrice: Currency; function GetID: Integer; function GetPrice: Currency; function GetIProductUpdate: IProductUpdate; function Edit(const aID: Integer; const aPrice: Currency): IProduct; public constructor Create(aID: Integer; aPrice: Currency); end; TClient = class(TInterfacedObject, IClient, IClientUpdate) private fName: string; fAge: Integer; function GetName: string; function GetAge: Integer; function GetIClientUpdate: IClientUpdate; function Edit(const aName: string; const aAge: Integer): IClient; public constructor Create(const aName: string; aAge: Integer); end; function GetTCustomRecord(const aName: string; aValue: Integer): ICustomRecord; begin Result := TCustomRecord.Create(aName, aValue); end; function GetTProduct(aID: Integer; aPrice: Currency): IProduct; begin Result := TProduct.Create(aID, aPrice); end; function GetTClient(const aName: string; aAge: Integer): IClient; begin Result := TClient.Create(aName, aAge); end; {$REGION ' Points Comparer & Helper Functions .. '} function ComparePoints(const P1, P2: TPoint): Integer; begin if P1.X < P2.X then Exit(-1) else if P1.X > P2.X then Exit(1); if P1.Y < P2.Y then Exit(-1) else if P1.Y > P2.Y then Exit(1); Result := 0; // Points are equal end; function ComparePoints(const P1, P2: TPointF): Integer; begin if P1.X <> P2.X then Result := Sign(P1.X - P2.X) else Result := Sign(P1.Y - P2.Y); end; function PointComparer: IComparer<TPoint>; begin Result := TComparer<TPoint>.Construct( function(const P1, P2: TPoint): Integer begin Result := ComparePoints(P1, P2); end ); end; {$ENDREGION} { Helper CustomRecord function } function CompareCustomRecord(const R1, R2: ICustomRecord): Integer; begin Result := R1.Value - R2.Value; end; { Helper ProductByPrice function } function CompareProductByPrice(const P1, P2: IProduct): Integer; begin if P1.Price < P2.Price then Result := -1 else if P1.Price > P2.Price then Result := 1 else Result := 0; end; { Helper ClientByAge function } function CompareClientByAge(const C1, C2: IClient): Integer; begin Result := C1.Age - C2.Age; end; { TCustomRecord } {$REGION ' TCustomRecord .. '} constructor TCustomRecord.Create(const aName: string; aValue: Integer); begin fName := aName; fValue := aValue; end; function TCustomRecord.GetName: string; begin Result := fName; end; function TCustomRecord.GetValue: Integer; begin Result := fValue; end; function TCustomRecord.GetCustomRecordupdate: ICustomRecordUpdate; begin Result := Self as ICustomRecordUpdate; end; function TCustomRecord.Edit(const aName: string; const aValue: Integer): ICustomRecord; begin fName := aName; fValue := aValue; end; {$ENDREGION} { TProduct } {$REGION ' TProduct .. '} constructor TProduct.Create(aID: Integer; aPrice: Currency); begin fID := aID; fPrice := aPrice; end; function TProduct.GetID: Integer; begin Result := fID; end; function TProduct.GetPrice: Currency; begin Result := fPrice; end; function TProduct.GetIProductUpdate: IProductUpdate; begin Result := Self as IProductUpdate; end; function TProduct.Edit(const aID: Integer; const aPrice: Currency): IProduct; begin fID := aID; fPrice := aPrice; end; {$ENDREGION} { TClient } {$REGION ' TClient .. '} constructor TClient.Create(const aName: string; aAge: Integer); begin fName := aName; fAge := aAge; end; function TClient.GetName: string; begin Result := fName; end; function TClient.GetAge: Integer; begin Result := fAge; end; function TClient.GetIClientUpdate: IClientUpdate; begin Result := Self as IClientUpdate; end; function TClient.Edit(const aName: string; const aAge: Integer): IClient; begin fName := aName; fAge := aAge; end; {$ENDREGION} end. now here is my dpr console code: program RangChecker; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, System.Types, DateUtils, System.Generics.Defaults, API.Utils in 'API\API.Utils.pas', API.Objects.Comparer in 'API\API.Objects.Comparer.pas'; var gPoint1, gPoint2, gPoint3: TPoint; gRec1, gRec2, gRec3: ICustomRecord; gRecordComparer: IComparer<ICustomRecord>; gProduct1, gProduct2, gProduct3: IProduct; gProductComparer: IComparer<IProduct>; gClient1, gClient2, gClient3: IClient; gClientComparer: IComparer<IClient>; gEndDateStr: string; begin gPoint1 := TPoint.Create(1, 2); gPoint2 := TPoint.Create(0, 0); gPoint3 := TPoint.Create(3, 4); gRec1 := GetTCustomRecord('Low', 10); gRec2 := GetTCustomRecord('Mid', 20); gRec3 := GetTCustomRecord('High', 30); gRecordComparer := TComparer<ICustomRecord>.Construct(CompareCustomRecord); gProduct1 := GetTProduct(1, 10.0); gProduct2 := GetTProduct(2, 20.0); gProduct3 := GetTProduct(3, 30.0); gProductComparer := TComparer<IProduct>.Construct(CompareProductByPrice); gClient1 := GetTClient('Alice', 25); gClient2 := GetTClient('Bob', 30); gClient3 := GetTClient('Charlie', 35); gClientComparer := TComparer<IClient>.Construct(CompareClientByAge); with FormatSettings do begin ShortDateFormat := 'DD MMMM YYYY'; CurrencyString := 'DA'; DecimalSeparator := ','; ThousandSeparator := '.'; end; gEndDateStr := DateToStr(Today +10, FormatSettings); try Writeln('-----------------<< Integer Tests >>--------------------------------'); {$REGION ' Integer Tests .. '} if TRange<Integer>.IsIn(5, 1, 10) then Writeln('5 is within the range [1, 10]') else Writeln('5 is outside the range [1, 10]'); if TRange<Integer>.IsIn(5, 6, 10) then Writeln('5 is within the range [6, 10]') else Writeln('5 is outside the range [6, 10]'); {$ENDREGION} Writeln('-----------------<< Int64 Tests >>--------------------------------'); {$REGION ' Int64 Tests .. '} if TRange<Int64>.IsIn(5_000_000_000_000_000_001, 5_000_000_000_000_000_000, 5_000_000_000_000_000_010) then Writeln('5_000_000_000_000_000_001 is within the range [5_000_000_000_000_000_000, 5_000_000_000_000_000_010]') else Writeln('5 is outside the range [5_000_000_000_000_000_000, 5_000_000_000_000_000_010]'); if TRange<Int64>.IsIn(5_000_000_000_000_000_000, 5_000_000_000_000_000_001, 5_000_000_000_000_000_010) then Writeln('5_000_000_000_000_000_000 is within the range [5_000_000_000_000_000_001, 5_000_000_000_000_000_010]') else Writeln('5_000_000_000_000_000_000 is outside the range [5_000_000_000_000_000_001, 5_000_000_000_000_000_010]'); {$ENDREGION} Writeln('-----------------<< Float Tests >>----------------------------------'); {$REGION ' Float Tests .. '} if TRange<Double>.IsIn(7.5, 5.0, 10.0) then Writeln('7.5 is within the range [5.0, 10.0]') else Writeln('7.5 is outside the range [5.0, 10.0]'); if TRange<Double>.IsIn(7.5, 7.6, 10.0) then Writeln('7.5 is within the range [7.6, 10.0]') else Writeln('7.5 is outside the range [7.6, 10.0]'); {$ENDREGION} Writeln('-----------------<< DateTime Tests >>------------------------------'); {$REGION ' DateTime Tests .. '} if TRange<TDateTime>.IsIn(Today, Today, Today +10) then Writeln('Today is within ['+Today.ToString+'] and ['+gEndDateStr+']') else Writeln('Today is outside ['+Today.ToString+'] and ['+gEndDateStr+']'); if TRange<TDateTime>.IsIn(Yesterday, Today, Today +10) then Writeln('Yesterday is within ['+Today.ToString+'] and ['+gEndDateStr+']') else Writeln('Yesterday is outside ['+Today.ToString+'] and ['+gEndDateStr+']'); {$ENDREGION} Writeln('-----------------<< String Tests >>--------------------------------'); {$REGION ' String Tests .. '} if TRange<string>.IsIn('hello', 'alpha', 'zulu') then Writeln('"hello" is within the range [alpha, zulu]') else Writeln('"hello" is outside the range [alpha, zulu]'); if TRange<string>.IsIn('zulu', 'alpha', 'omega') then Writeln('"zulu" is within the range [alpha, omega]') else Writeln('"zulu" is outside the range [alpha, omega]'); {$ENDREGION} Writeln('-----------------<< TPoint Tests >>-----------------------------'); {$REGION ' TPoint Tests .. '} if TRange<TPoint>.IsIn(gPoint1, gPoint2, gPoint3, PointComparer) then Writeln('Point(1, 2) is within the range [Point(0, 0), Point(3, 4)]') else Writeln('Point(1, 2) is outside the range [Point(0, 0), Point(3, 4)]'); if TRange<TPoint>.IsIn(Point(5, 5), Point(0, 0), Point(3, 4), PointComparer) then Writeln('Point(5, 5) is within the range [Point(0, 0), Point(3, 4)]') else Writeln('Point(5, 5) is outside the range [Point(0, 0), Point(3, 4)]'); {$ENDREGION} Writeln('-----------------<< TCustomRecord Tests >>-----------------------------'); {$REGION ' TCustomRecord Tests .. '} if TRange<ICustomRecord>.IsIn(gRec2, gRec1, gRec3, gRecordComparer) then Writeln('Record is within the range') else Writeln('Record is outside the range'); gRec2.New.Edit('Mid', 40); if TRange<ICustomRecord>.IsIn(gRec2, gRec1, gRec3, gRecordComparer) then Writeln('Record is within the range') else Writeln('Record is outside the range'); {$ENDREGION} Writeln('-----------------<< TProduct Tests >>-----------------------------'); {$REGION ' TProduct Tests .. '} if TRange<IProduct>.IsIn(gProduct2, gProduct1, gProduct3, gProductComparer) then Writeln('Product price is within the range') else Writeln('Product price is outside the range'); gProduct2.New.Edit(2, 40); if TRange<IProduct>.IsIn(gProduct2, gProduct1, gProduct3, gProductComparer) then Writeln('Product price is within the range') else Writeln('Product price is outside the range'); {$ENDREGION} Writeln('-----------------<< TClient Tests >>-----------------------------'); {$REGION ' TClient Tests .. '} if TRange<IClient>.IsIn(gClient2, gClient1, gClient3, gClientComparer) then Writeln('Client age is within the range') else Writeln('Client age is outside the range'); gClient2.New.Edit('Bob', 40); if TRange<IClient>.IsIn(gClient2, gClient1, gClient3, gClientComparer) then Writeln('Client age is within the range') else Writeln('Client age is outside the range'); {$ENDREGION} except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; Readln; end. Output Result: My Github Link
  8. dummzeuch

    AI in the IDE??

    Unfortunately, I'm not morally depraved enough for that.
  9. Hi everyone, more than twenty-one years ago, I started the German-language part of this forum and could not even begin to imagine what it would become. Thanks to the tireless support of many moderators as well as your thirst for knowledge and willingness to answer other people's questions, it has become a pillar of the virtual Delphi community - even far beyond the German-speaking world. Since 2018, this English-language part of the forum has also been available, with considerable support from Lars. With an online presence of this size comes the obligation to take proper care of it. I have always been very happy to do this, but over twenty-one years is a very long time and life and its priorities change. I can't help but realize that my daily/weekly time has become less available and the time has come for me to hand over the management of the forum to someone else. Thankfully, Thomas B. ("TBx") has agreed to take over the Delphi-PRAXiS and continue it in good hands - together with Lars, of course. You know Thomas as a longtime moderator of this forum and now he will take over my previous role. I myself will of course remain part of the Delphi community - not least because I continue to work a lot with Delphi in my job. I will also remain a part of this forum. Thank you all for over 21 great years!
  10. Temporary Solution: By using Delphi's TValue type from the System.Rtti unit, I was able to implement a robust custom Writeln procedure usin overload. Here's how it works: Main Procedure to Process Arguments This procedure processes the arguments, determining their types and formatting them as needed: procedure DoCustomWriteln(const Args: array of TValue); var LArg: TValue; LOutput: string; I: Integer; begin LOutput := ''; for I := Low(Args) to High(Args) do begin LArg := Args[I]; case LArg.Kind of tkInteger: LOutput := LOutput + IntToStr(LArg.AsInteger); tkFloat: LOutput := LOutput + FloatToStr(LArg.AsExtended); tkString, tkLString, tkUString, tkWString: LOutput := LOutput + LArg.AsString; tkChar, tkWChar: LOutput := LOutput + LArg.AsString; tkVariant: try LOutput := LOutput + VarToStr(LArg.AsVariant); except LOutput := LOutput + '<invalid variant>'; end; else LOutput := LOutput + '<unsupported type>'; end; // Add a separator unless it's the last argument if I < High(Args) then LOutput := LOutput + ', '; end; Writeln(LOutput); end; Overloading Writeln To make calling this function straightforward without requiring brackets, I created multiple overloads for the CustomWriteln procedure: procedure CustomWriteln(A1: TValue); overload; begin DoCustomWriteln([A1]); end; procedure CustomWriteln(A1, A2: TValue); overload; begin DoCustomWriteln([A1, A2]); end; procedure CustomWriteln(A1, A2, A3: TValue); overload; begin DoCustomWriteln([A1, A2, A3]); end; // Add more overloads as needed for additional parameters Test in Project: begin try // Examples of usage with different types CustomWriteln(42); CustomWriteln(3.14, 'Hello'); CustomWriteln(1, 2.2, 'Text', True); CustomWriteln(1, 'Two', 3.3, 'Four', False, 6); except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; Readln; end. Example Output ------- 42 3,14, Hello 1, 2,2, Text, <unsupported type> 1, Two, 3,3, Four, <unsupported type>, 6 Advantages of This Approach: Flexible Input: Handles integers, floats, strings, characters, and variants. Type-Safe: Uses TValue to handle types dynamically. Scalable: Easy to extend by adding more overloads or enhancing DoCustomWriteln. --- Final Project: program CustomWritelnProj; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, System.Variants, System.Math, System.Rtti; procedure DoCustomWriteln(const Args: array of TValue); var LArg: TValue; LOutput: string; I: Integer; begin LOutput := ''; for I := Low(Args) to High(Args) do begin LArg := Args[I]; case LArg.Kind of tkInteger, tkInt64: LOutput := LOutput + LArg.AsInt64.ToString; tkFloat: LOutput := LOutput + LArg.AsExtended.ToString; tkEnumeration: LOutput := LOutput + BoolToStr(LArg.AsBoolean, True); tkString, tkLString, tkUString, tkWString, tkChar, tkWChar: LOutput := LOutput + LArg.AsString; tkVariant: try LOutput := LOutput + LArg.AsVariant.ToString; except LOutput := LOutput + '<invalid variant>'; end; else LOutput := LOutput + '<unsupported type>'; end; // Add a separator unless processing the last element if I < High(Args) then LOutput := LOutput + ', '; end; Writeln(LOutput); end; // Overloaded CustomWriteln implementations procedure CustomWriteln(A1: TValue); overload; begin DoCustomWriteln([A1]); end; procedure CustomWriteln(A1, A2: TValue); overload; begin DoCustomWriteln([A1, A2]); end; procedure CustomWriteln(A1, A2, A3: TValue); overload; begin DoCustomWriteln([A1, A2, A3]); end; procedure CustomWriteln(A1, A2, A3, A4: TValue); overload; begin DoCustomWriteln([A1, A2, A3, A4]); end; procedure CustomWriteln(A1, A2, A3, A4, A5: TValue); overload; begin DoCustomWriteln([A1, A2, A3, A4, A5]); end; procedure CustomWriteln(A1, A2, A3, A4, A5, A6: TValue); overload; begin DoCustomWriteln([A1, A2, A3, A4, A5, A6]); end; procedure CustomWriteln(A1, A2, A3, A4, A5, A6, A7: TValue); overload; begin DoCustomWriteln([A1, A2, A3, A4, A5, A6, A7]); end; procedure CustomWriteln(A1, A2, A3, A4, A5, A6, A7, A8: TValue); overload; begin DoCustomWriteln([A1, A2, A3, A4, A5, A6, A7, A8]); end; procedure CustomWriteln(A1, A2, A3, A4, A5, A6, A7, A8, A9: TValue); overload; begin DoCustomWriteln([A1, A2, A3, A4, A5, A6, A7, A8, A9]); end; begin try // Examples of usage with different types CustomWriteln(42); CustomWriteln(MaxComp,'The max value of Int64'); CustomWriteln(MaxComp,MinComp, 'Int64 Interval'); CustomWriteln(1, 2.2, 'Text', True); CustomWriteln(1, 'Two', 3.3, 'Four', False, 6); except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; Readln; end.
  11. GabrielMoraru

    New Code Signing Certificate Recommendations

    Yes I knew about the hardware token but I honestly tough that they apply only to the EV (my first certificate was an EV). Maybe because of the wording? Even the article that rvk pointed to, uses some strange wording "for standard code signing certificates" instead of the "OV". Basically, those 300 words of that article can be summarized as: "the rules for storing EV now also apply to OV". Dang it! _____________ PS: for completeness of information, the missing parameter from the command line to sing an exe file WITH a time server is: /tr http://timestamp.digicert.com /td SHA256 😞
  12. Delfanbuoi

    Delphi for Mobile Applications

    Around 2016-2017 I prototyped an app for Android & IOS that pulled patient data from an oracle database and supplied that data to a GUI. At first I planned on using FMX exclusively for screens, but found that although the screens worked well, they were most definitely not performant with FMX controls and screen draws. So I went back to the drawing board and utilizing Oracle stored procedures, kbmMW (middleware, awesome!), and the web browser control on both platforms, I pulled the data in the stored procs, built HTML in the stored procs, used kbmMW to ship it to the clients, and created a tabbed interface using native controls, with all content areas displayed in the embedded web browser. The generation of the html in Oracle was instantaneous, the transfer of the html content using kbmMW was instantaneous on the local network, and display performance was quite good. I've heard that SKIA has better performance for UI draws; since we are talking about small device graphics, UI performance will always be an area that requires special attention. As the examples above show, Delphi is quite capable of creating complex, highly performant mobile UI's; minimal conditional defines are required, all of your existing Delphi source/servers/application logic is available to you. It allows you to work within a familiar environment, utilizing ONE(1) toolchain - albeit with some 3rd party stuff potentially - and has excellent performance. The cost of using Delphi for mobile is really measured not in the purchase price of Delphi, nor of 3rd party components, but in the learning curve. It's a different programming paradigm, so the ability to utilize just ONE(1) tool that you are familiar with greatly minimizes the time to learn how to work with mobile. Native combined with RAD will get you to the expert level with mobile faster than other environments/languages/tools with which you may not have any experience. Another nice example app: https://www.linkedin.com/posts/piotr-stefanowicz-035465294_delphi-tms-web-core-pwa-prodvisor-application-ugcPost-7283126982035345409-m4HG?utm_source=share&utm_medium=member_desktop
  13. UncleWael

    Edge Webview2 Environment

    Thank you all . I've found the issue and it was in the installation of the webview2 runtime , not in Delphi. Now all is working as it should be.
  14. I renewed my Sectigo code signing certificate last Saturday, submitted documents on Monday, which the web site said were rejected, yet the order was approved and shipped Tuesday morning via UPS, and arrived Thursday, quite impressed. Although the Sectigo London office is a few miles away, the token was shipped from Sectigo's Lille office in France to London, with an invoice valuing the 'electronic document' at $10 so no customs duty to pay. Perhaps Sectigo has an arrangement with Thales (a large French company) who sell the Safenet tokens to provision them as well. Plugged the token into my PC, and the new certificate appears in the Windows Store, as reported by the ICS Delphi PemTool. All much less painful than I was expecting, except the massive cost increase over electronic certificates, and no invoice yet from K Software. Angus
  15. DelphiUdIT

    Compile&Run cmd for Android

    In the "Project/Deployement" window you can set the files that you don't want overwrite (deploy) ("ovewrite" column options). By default all newer local files will be deployed and overwrite the remote ones. Bye
  16. I finally received the EV certificate. It was an exhausting journey, where every day the certification body (Certum) asked me for a document, a clarification or something else. Having to follow the complete path (I had never purchased any certificate from them) they rightly verified everything and even more than everything. The installation of the certificate (keys and certificate itself) on the hardware key was done through their control panel via browser in two stages. Everything worked the first time and the cost was in line with that of direct competitors (at least for the three-year solution). The hardware key is seen directly through the "storage" of Windows certificates and so the certificate is visible and usable by any application. In the Rad Studio IDE I inserted a new Tool (under "tools menu") that allows me to immediately sign the executable file (or DLL) compiled from the project. Now the second step is to verify with Microsoft the pairing for signing the drivers.
  17. AFAIK you can use any certificate (at least that used to be the case[*] - maybe a bug in signtool) but only code signing certificates will be validated as such so there's not much point in trying to use something else. What would be the point? *) Back in the day, when Denmark introduced digital IDs, every citizen got issued a certificate. So naturally I used my personal certificate to sign all my software 🙂 I think that the new certificates are still just files. They just need to be on a secure token in order to be usable. AFAIK once you have a token with a certificate on it you can copy it to other tokens. That's what we are planning on doing anyway; We just received an EV certificate on a token and two extra blank tokens yesterday. One is used by the build server (via Signotaur - works great!), one will go in the safe for backup, and one will be shipped to me for R&D (my client is in another country), and in the darkness bind them.
  18. Hi I thought it make sense to share the link to our discussion on that topic in the TestComplete forum. https://community.smartbear.com/discussions/testcomplete-questions/delphi-12-x-officially-unsupported/267672 Well, I know that a lot is not properly running (e.g. VCL Styles). But until now, we were still able to run our large number of test cases. But after dropping AQTime, now SmartBear silently drops the Delphi support for TestComplete as well. TBO, the new SmartBear subscription model is far too expensive compare with our last 3 year renewals. And we do normally not use any software on "rental base" that stops suddenly. But transferring all the tests to something "new" is a nightmare ... somehow a deadlock again. regards Günther
  19. Brian Evans

    "Death to WITH" in your Delphi Code

    If they added the ability to provide an alias the ambiguity would be gone. Aliases in SQL are useful and perform a similar function. Shortening repeated references can make code easier to read, the problem is the current WITH creates ambiguity. A code snipped from the blog post redone with the ability to provide an alias as an example: procedure TMyForm.UpdateInventoryItem(const NewQty: Integer); begin with dmStoreInventoryData as A do begin with A.tblUpdateItemForStore as B do begin B.Edit; B.FieldByName('Qty').AsInteger := NewQty; B.Post; end; end; end;
  20. This is still a work in progress, but I'm really happy with how it is going so far so I thought I would share... https://github.com/jimmckeeth/FMXColorDialog There are a few custom components in the dialog, and there is a palette code library, plus there is a new CMYK color record. Just fun project to mess around with on vacation. FMXColorDialogSample_PseA8aEaZC.mp4
  21. Dalija Prasnikar

    How many people use Delphi?

    Are you sure that poor speed is acceptable on desktop applications? What about applications that need to load complex forms where opening such form can take half a minute or more? Or when you need to create plenty of business objects, or when you need to do some textual processing. Good compiler means a lot in such cases. In last few Delphi releases @Stefan Glienke has continuously submitted hand crafted code to improve speed of some widely used RTL routines that have impact on overall performance. With better compiler that would not be necessary. Not only that, but with better compiler all other code we write would be faster and when speed matters we wouldn't have to bend over backwards, writing horribly unreadable code just to get the speed we need. We could just write the clean and readable and maintainable code and leave the dirty work to the compiler. Speed always matters. Now, Delphi still might be fast enough for many purposes, but for many that is simply not enough. Once it was state of the art compiler, now it is seriously falling behind.
  22. Lars Fosdal

    How many people use Delphi?

    Well, if you work with databases, everything else feels fast 😛
  23. Be careful with Mida. From my experience the software is not working. Although it can still be purchased, it won't run because at startup the software cannot contact their license server and then stops. I tried contacting the developer many times, never had a response.
  24. Fun fact: some years ago someone (not me, not Andreas) achieved being able to inherit record helpers by simply patching one flag in the compiler. 😉 I am just saying this to emphasize that its not some technical limitation but simply the compiler does not allow it when it sees "record helper" but does when its "class helper" as otherwise its the same code.
×