Jump to content

Clément

Members
  • Content Count

    358
  • Joined

  • Last visited

  • Days Won

    4

Everything posted by Clément

  1. Clément

    Click a link in EDGE Browser

    JS is scary! Goes against everything that is safe and sound. I really can't say who ( or what) wrote that code. It be can be a person, a framework or ChatGPT. They prefer to use a "Desktop application" to handle filtering and selection rather than modify the code. This should raise some red flags.
  2. Clément

    Click a link in EDGE Browser

    Just managed to do it in plain JavaScript! I navigate to the corresponding row using DOM, found the <a> tag and called click..... So easy!
  3. Clément

    Use case or if else ?

    In order to decide between them I usually check a few things: 1. "Staticness" : How often will upper bound change. Is the Index "Static". 2. "Filledness" : How well will the structure be filled. 3. Missed Index : How often will I hit one of those empty index, and if I hit what happens ( How easily can I deal with it ) 4. Element Owner : If the elements are instances "Who" will created them. "Who" will free them. Will "Who" be the same? 🙂 Indexed array is a great structure. Can be both performant and low size. "Dictionary" has it's weight. I'll use them whenever it's benefit outweigh this cost. Anyway.. I often use TDictionary or (TObjectDictionary) because I rarely have static index. ex: GUID is the key to another structure. In the example I gave, it is possible to add decoders at runtime ( from a DLL, or a database). And it wouldn't required a recompilation. Which very cool!
  4. Clément

    Use case or if else ?

    I would also register my methods. They all seems to have the same signature. unit Unit121; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Generics.Collections, Vcl.StdCtrls; type TMyDecodeProcedure = procedure ( aDoc, aCdn, aRequestCorrelation, aMessageText : string ) of object; TForm121 = class(TForm) Button1: TButton; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure Button1Click(Sender: TObject); private { Private declarations } fDecoders : TDictionary<String, TMyDecodeProcedure >; procedure RegisterDecodeFunc; procedure DecodeExportAcc( aDoc, aCdn, aRequestCorrelation, aMessageText : string); procedure DecodeExportErr( aDoc, aCdn, aRequestCorrelation, aMessageText : string ); procedure DecodeExportReg( aDoc, aCdn, aRequestCorrelation, aMessageText : string ); public { Public declarations } function Execute( aDecoder, aDoc, aCdn, aRequestCorrelation, aMessageText : string; var aErrMsg : String ) : Boolean; end; var Form121: TForm121; implementation {$R *.dfm} procedure TForm121.FormCreate(Sender: TObject); begin fDecoders := TDictionary<String, TMyDecodeProcedure >.Create; RegisterDecodeFunc; end; procedure TForm121.FormDestroy(Sender: TObject); begin fDecoders.Free; end; procedure TForm121.DecodeExportAcc(aDoc, aCdn, aRequestCorrelation, aMessageText: string); begin // end; procedure TForm121.DecodeExportErr(aDoc, aCdn, aRequestCorrelation, aMessageText: string); begin // end; procedure TForm121.DecodeExportReg(aDoc, aCdn, aRequestCorrelation, aMessageText: string); begin // end; function TForm121.Execute(aDecoder, aDoc, aCdn, aRequestCorrelation, aMessageText: string; var aErrMsg : String): Boolean; var lDecoder : TMyDecodeProcedure; begin Result := fDecoders.TryGetValue(aDecoder, lDecoder); if Result then lDecoder( aDoc, aCdn, aRequestCorrelation, aMessageText ) else aErrMsg := Format('Decoder not found: %s', [aDecoder] ); end; procedure TForm121.RegisterDecodeFunc; begin fDecoders.Add('EXPREG', DecodeExportReg ); fDecoders.Add('EXPACC', DecodeExportAcc ); fDecoders.Add('EXPERR', DecodeExportErr ); { ... } end; procedure TForm121.Button1Click(Sender: TObject); var lErrMsg : String; begin lErrMsg := ''; Execute('EXPREG','1','2','3','4', lErrMsg); end; end. And you can add decoders more easily too..
  5. Clément

    Delphi Professional Database Connectivity

    FireDac has restrictions in Professional and Community edition. Check this: https://www.embarcadero.com/docs/rad-studio-feature-matrix.pdf
  6. Actually I guess is the other way around! The more expert I get the more refactoring I do! As @Lars Fosdal said, cryptic code, WTF was I thinking code, Code that could benefit from modern RTL code if there's no performance penalty. For example: For lPair in SomeObjectList is slower than for i:= 0 to SomeobjectList.count-1. So I won't refactor it. Depending where the code is placed, I will use one form or the other. Replacing TStringList (used as dictionary) with a TDictionary/TDictionaryObject makes a huge diference!
  7. Clément

    Migrating projects from Delphi to .Net

    How productive is you customer using you application? That's the question you should ask yourself. You can take whatever IDE you like... and what? Clone you exact application UI, behavior and functionality. All the fancy .NET stuff would still be under the hood. So.. what does he need.... exactly! Ah! He wants something new, which means something different. What exactly? less clicks? More "live" information on screen? Dashboards? Notifications? Users wants productivity. Developers wants fast time to market, One excludes the other
  8. It doesn't seem to be a dproj or dpr related, but how old is your .dproj ? Have you create a new one for 11.x? Are all your .pas files in you dproj? if not, make sure your are deleting all dcus files manually prior to rebuild. For example, if you select "CLEAN" the IDE will delete only the files present in your dproj, you might get some old dcus in your built! If this is the case, third party dcus are the first you need to check.
  9. Clément

    Using EdgeBrowser

    Hi, Is it possible to scan the content, downloaded using Edge Browse, for a specific text? I don't want to inject or alter anything. Just scan the page for a specific text. Can this be done with edgeBrowser? Is there a sample? TIA, Clément
  10. Clément

    Using EdgeBrowser

    Yeap! That did the trick. Thank you
  11. Clément

    Delphi 11.3 is available now!

    Just downloaded and installed (WebInstall). I use the migration tool to backup and restore my settings. All my programs compiled without any errors, except, of course, those using VCL Styles from Get it. After downloading all the styles (again) they compiled just fine! Can't wait to see if the debugger got some love!
  12. Hi, I'm using D11.2 and ICS 8.71 I'm working on a two-part project. Desktop application + Windows Service. All UI part is done in the Desktop Application. For example: I can set smtp server parameters within the Desktop application. The Service will get the smtp parameters and effectively send the email. The email can be sent by several triggers. For example: running low on disk space, a specific folder is larger than specified, etc.. For normal smtp accounts everything works smoothly! Here enters the OAuth2. If I understood correctly, OAuth2 requires a user intervention during the sending process, which in my case will happen in the service -> no UI -> no Browser! I was wondering if it's possible to "send the required data" from the Service, so the desktop could display the Browser and allow the user to interact, get the required information, and send it back to the service. Hopefully, this authentication will happen once or twice. Since it's possible for an external browser (different process than the application which send the mail ) to collect the data, and validate the application, I was wondering if it's possible to take things one step further, and send the data to the service. (I would not like to send an email from the Desktop Application, if it comes to that, I prefer having a separate application which would handle the authentication). In order to communicate properly, would it be possible to have IcsRestEmail in my Desktop Application, collect the data and get back to the service? procedure TSslSmtpTestForm.SslSmtpClientGetNewToken(Sender: TObject); { V8.65 } begin if NOT IcsRestEmail1.GetNewToken(True, Self) then // allow interaction, waits for broweser window to be completed, V8.71 added self Display('Failed to get OAuth2 Bearer Token') else begin if (Pos (IcsRestEmail1.NewAccEmail, SslSmtpClient.UserName) = 0) and (IcsRestEmail1.NewAccEmail <> '') then Display('OAuth2 Token for Wrong Account, Expected: ' + SslSmtpClient.Username + ', Got: ' + IcsRestEmail1.NewAccEmail) else begin SslSmtpClient.OAuthToken := IcsRestEmail1.AccToken; SslSmtpClient.TokenExpireDT := IcsRestEmail1.AccExpireDT; Display('Got New OAuth2 Bearer Token OK'); end; end; end; I should send a blocking IPC request from this event, wait until the user interacted, and return with OAuthToken and TokenExpireDT. How often would AccToken require renewing? It's seems a lot of work and the user might end up having email issues when the token expiration hits (probably on a Sunday) On the other hand, I use Thunderbird to send emails from my gmail account. And I did only once the validation process. Since my application is not Thunderbird, it might differ. Hopefully, it's possible to have some nice code written to solve this. Have you done such a thing? Is it even possible? Clément
  13. I see. I can use the refresh token even if "some days" has passed? For example, some tasks might run over a weekend. I will use the refresh token on Sunday to send the last email, and only refresh it again next saturday before sending the first email.
  14. I will protect both ClientID and ClientKey in my desktop application. They will only get unprotected to assign the corresponding properties in IcsRestEmail. How is this call done? Are they transmitted to google protected?
  15. Just for clarification, I can use only IcsRestEmail in my desktop application. The user will enter it's account setting, and I will call "GetNewToken". If everthing works fine, I'll endup with AccToken and AccExpireDT assigned, and in my case I will store them in my database. Later, the service will read those parameter and send the mail ... this is great! Thanks!
  16. Hi, I'm using ICS 8.68 and Delphi XE. This Windows Service checks for emails every minute, and send all emails that are found. The SMTP server is replying with 452 - 4.3.1 Insufficient system resources after a while. If my customer restart the service ( takes a little over a minute ), another batch of emails are sent without errors ( Same server, same port) Until I receive the error 452 again. From his point of view, my application has a problem, since a service restart solves the problem. From mine, the SMTP server, which might be shared with other applications, has some issues, either disk space or memory. The service does a lot of other things and all of them are working fine ( no errors in any log). Should I abort sending emails when this the SMTP server replies with this error? How can I trap it? TIA, Clément
  17. The routine is working fine, and I really don't want to upset the protocol. I asked for this information. Unfortunately most IT folks are in holiday break. My best guess: this SMTP server is share among other applications. I'm placing a delay before the initial call to connect (or MailForm).
  18. Sorry if I make it sound like was an ICS issue. It's an internal SMTP server ( I don't know which ). I ask their IT folks to help me with some information, but this time of the year emails must be sent, and since most IT fellows are in holidays.... Getting back to ICS, is there a "better" event to add some delay ( emails per hour throttle comes to my mind ) procedure TRNBWSMTP.SmtpRequestDone(Sender: TObject; RqType: TSmtpRequest; ErrorCode: Word); begin if not fDebugMode then Log(FSMTP.LastResponse); // This is where it logs the error 452 if (ErrorCode > 0) and (ErrorCode < 10000) then begin Log('RequestDone Rq=' + IntToStr(Ord(RqType)) + ' Error='+ FSMTP.ErrorMessage); if ErrorCode = 501 then begin FSuccess := false; FSMTP.Quit; exit; end; end; {...} end;
  19. Hi, Happy XMas! I must read a bunch o PNG images to my program and store them in a database. The pictures (PNG) have different height and width, and I must process them in order to keep all of them in 600x600. For example: I read a PNG image 704x404, I must cut 704 -> 600 and a must fill 404 -> 600 , without loosing transparency. I have some ideas of how to do it, but it must be "fast" and correct. Any ideas?
  20. Hi. I received this email yesterday " We are pleased to send to RAD Studio customers on Update Subscription this invite to join the beta program for Embarcadero’s next major release of Delphi, C++Builder, and RAD Studio, codenamed Malawi. This invitation is for all Update Subscription customers (after an initial beta invite limited to Premium Update Subscription customers only). This invite is personal and cannot be shared with other developers without an active Update Subscription. " Merry Christmas
  21. Hi, Is it possible to generate a "create table" script using Firedac? How about "Create Index"? Any sample? TIA, Clément
  22. Clément

    Using firedac to generate table script

    I looked the code and managed to build what I need. But I'm still not sure the code is correct. The only way I found is to derive from TFDTable like: TFDMyTable = Class( TFDTable ) public procedure GenerateSQLCreate( aGenerateDrop : Boolean; AParts: TFDPhysCreateTableParts; aResult : TStrings ); end; In order to get any result I must execute as: procedure TForm98.FormCreate(Sender: TObject); var lTable : TFDMyTable; begin lTable := TFDMyTable.Create(nil); try lTable.Connection := FDConnection1; lTable.TableName := 'Employees'; lTable.Open; lTable.Close; lTable.GenerateSQLCreate(true,[tpTable,tpGenerators,tpPrimaryKey,tpIndexes], Memo1.Lines); finally lTable.Free; end; end; If I don't "Open/Close" before calling "GenerateSQLCreate" I get an Access Violation try // Build Table from FieldDefs and IndexDefs CheckTable; // Access Violation here if I don't Open/Close before the call OpenIndexes; The table (lTable in this case) won't be attached to any visual components (like DBGrid or such). How many record will Open retrieve? In this case I really don't need any records, just metadata should be enough. Is there a method I should call that will only retrieve metadadata info? And the result is pretty cool! DROP TABLE Employees; CREATE TABLE Employees ( EmployeeID INT IDENTITY(1,1) NOT NULL, LastName NVARCHAR(20) NOT NULL, FirstName NVARCHAR(10) NOT NULL, Title NVARCHAR(30), TitleOfCourtesy NVARCHAR(25), BirthDate DATETIME2, HireDate DATETIME2, Address NVARCHAR(60), City NVARCHAR(15), Region NVARCHAR(15), PostalCode NVARCHAR(10), Country NVARCHAR(15), HomePhone NVARCHAR(24), Extension NVARCHAR(4), Photo VARBINARY(MAX), Notes NVARCHAR(MAX), ReportsTo INT, PhotoPath NVARCHAR(255) ); ALTER TABLE Employees ADD CONSTRAINT [PK_Employees] PRIMARY KEY (EmployeeID); CREATE INDEX LastName ON Employees (LastName, EmployeeID); CREATE INDEX PostalCode ON Employees (PostalCode, EmployeeID);
  23. Hi, My customer has a database with a lot of employee picture ( for smartcard tags). He wants to overlap each photo to a custom background. The idea is to trim out as much as possible the white rectangular background of the employee photo, leaving only his face, and then apply it over a custom background. What are my options? (All pictures and backgrounds are JPG)
  24. Well.... It's cool enough! Here is the code I wrote. unit Unit40; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, jpeg, ExtCtrls, StdCtrls; type TForm40 = class(TForm) imgBackground: TImage; imgEmployee: TImage; imgOverlap: TImage; btnOverlap: TButton; procedure btnOverlapClick(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form40: TForm40; implementation {$R *.dfm} type PRGB = ^TRGB; TRGB = record B : byte; G : Byte; R : Byte; end; function ColorBetween( Color : TRGB; I1, I2 : Byte ) : Boolean; begin result := (Color.R>=I1) and (Color.R<=I2) and (Color.G>=I1) and (Color.G<=I2) and (Color.B>=I1) and (Color.R<=I2); end; function IsWhitish(Color: TRGB): TRGB; begin if ColorBetween( Color, 210,255 ) then begin Color.R := 255; Color.G := 255; Color.B := 255; end; result := Color; end; procedure NormalizeWhite(Bitmap: TBitmap); var Row : integer; p : PRGB; Col : integer; begin for Row := 0 to Bitmap.Height-1 do begin p := PRGB(Bitmap.ScanLine[Row]); Col := Bitmap.Width; while (Col > 0) do begin p^ := IsWhitish(p^); inc(p); dec(Col); end; end; end; procedure TForm40.btnOverlapClick(Sender: TObject); var ldstBmp, lsrcBmp : TBitmap; i: Integer; j: Integer; begin lsrcBmp := TBitmap.Create; ldstBmp := TBitmap.Create; try // Loading employee picture with rectangular white background lsrcBmp.Assign(imgEmployee.Picture.Graphic); // Converting Whitish to White NormalizeWhite(lsrcBmp); // Loading destination background ldstBmp.Assign(imgBackground.Picture.Graphic); // Drawing employee picture over background transparently TransparentBlt( ldstBmp.Canvas.Handle, 50, 70 , lsrcBmp.Width, lsrcBmp.Height, lsrcBmp.Canvas.handle, 0, 0 , lsrcBmp.Width, lSrcBmp.Height, clWhite ); imgOverlap.Picture.Bitmap.Assign(ldstBmp); finally lsrcBmp.Free; ldstBmp.Free; end; end; end. The result is very cool. Thanks
  25. Clément

    Parse Json again, complicated

    There are a few things to resolve before getting to "animal id". This is just an example using Delphi 11, but older version should work too. (Might need to remove the inline variable). This is how I would do it: unit Unit94; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, System.JSON; type TForm94 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } const _JSON = ' {"result": [{ "animals": [{ "id":1, "name":"pig" } ] } ] }'; public { Public declarations } end; var Form94: TForm94; implementation {$R *.dfm} procedure TForm94.Button1Click(Sender: TObject); var lJsResult : TJsonObject; lJsResultArray : TJsonArray; lJsAnimals : TJsonObject; lJsAnimalArray : TJsonArray; lJsAnimal : TJsonObject; lId : Integer; lname : String; begin lJsResult := TJSONObject.ParseJSONValue(_JSON) as TJsonObject; try if lJsResult.TryGetValue<TJsonArray>('result', lJsResultArray) then begin for var r := 0 to lJsResultArray.Count-1 do begin lJsAnimals := lJsResultArray.Items[r] as TJsonObject; lJsAnimalArray := lJsAnimals.GetValue<TJsonArray>('animals'); for var i := 0 to lJsAnimalArray.Count-1 do begin lJsAnimal := lJsAnimalArray.Items[i] as TJSONObject; if not lJsAnimal.TryGetValue<Integer>('id', lId ) then raise Exception.Create('No "ID" Field'); if not lJsAnimal.TryGetValue<String>('name', lname) then raise Exception.Create('No "name" Field'); // DO something with lId, and name end; end; end finally lJsResult.Free; end; end;
×