-
Content Count
1090 -
Joined
-
Last visited
-
Days Won
23
Everything posted by aehimself
-
I never checked, but isnβt the Delphi localization file a regular resource DLL, without code? If this is the case (and your source is not IFDEF-ing resource stings) the same file can be used by 32 and 64 bit applications. Afaik a new DLL is only required if there is ANY code (including self-extracting, like UPX).
-
The whole thing started with this topic. Due to lack of possibilities, I wrote my own update mechanism which noes not rely on any advanced stuff but still should be versatile enough. Since the initial version TAEUpdater got some improvements and things are looking great. Some more stuff was implemented like hash-based verification, messages, E-tag caching, separate internal, development and production channels, ability to downgrade to a previous version, etc. I'm running it in my main application for a while and it seems to do the job correctly. Since not all of us might update from an unauthenticated web server, now 3 different file providers are shipped: HTTP, flatfile and custom. HTTP uses Delphi's TNetHTTPClient, flatfile reads the files from a local disk and custom has all necessary events exposed via events. I also got rid of some personal dependencies (like compressing the update file or using System.Zip2) and now everything is handed to the user for the solution to be more... generic. I still use my own version discovery and comparison method to be able to determine which published one is "newer" but that is going to be my next step. As a workaround, you can call .LoadUpdateFile, check the new version in each ActualProduct.Files and call .Update manually. What it needs: - An update file containing the product, all its files and versions. You can build this with TAEUpdateFile.SaveToStream in AE.Updater.UpdateFile.pas - Update packages, which can be zipped or encrypted, on FTP, HTTP, custom protocol - doesn't matter as events should be in place for performing all these actions Plans for the future: - Getting rid of more dependencies, maybe it'll become a standalone updater package with no extras - By accomplishing the above the code will get closer and closer to be truly cross-platform (atm it's WIndows only) - Get rid of modern stuff (like generics) and implement proper versioning conditionals so the package can be compiled and installed on anything other than D11.2 - Really, really basic documentation on how to use... Disclaimer: further changes are on their way. I'll possibly add / remove events / splitting the component in multiple subcomponents which might make existing .DFMs failing to be streamed. Also, publish your new versions on the Internal channel if I might break the core functionality π All words as one - I just wanted to give the existing Delphi community a chance to implement auto-updating feature to their applications without having to pay for the component or the backend. Feel free to check it out. And suggest ways of improvement of course π
-
There are lots of people who dislike Generics and would like to avoid having them in their projects. Using a standard TList and casting pointers can be an easy solution. In theory this whole thing could be D7 compatible if I'd invest enough free time in it. We'll see. So the author of said stack trace unit was you? π I remember having it for ages without any sign of where / who it came from so I can give appropriate credit. There were also some cyrillic characters in it which threw me totally off. As it seems it grew a lot, and since the new version is on GitHub it's easier just to remove it from mine. I don't like redundancy.
-
Came here to post the same. Better translation manager is working wonders.
-
Memory leak on TBlobField(FieldByname(field_name)).SavetoStream(Stream);
aehimself replied to alogrep's topic in VCL
All programs only show you the place the leaked object was created. It has no idea where you want to free it up. What is the leaked object? -
Hello, Let's imagine the following scenario. I made a custom component which resides on a VCL frame. First of all, it's a lot easier to design something if you can see it (hence no TPanel) and some components require a form or a frame as a parent. It looks like this: Install it to the IDE and create a new application. Place this component on the form. Issue #1: You can not select individual components, any click with the mouse will select the whole frame instead. This is not a big thing, as you still can select its child components in the Structure pane. Now, save your project in an empty git repository and commit once, so you can compare the differences. Load up the project again, select the TEdit and enter something as it's Text property: Save your project again and check the differences in the DFM: Issue #2: All property changes in the components children are simply discarded. I can get around this one too by setting these in runtime, but it's less convenient that directly in the IDE. I suppose this is a limitation, I shouldn't design my custom component on a frame. How else can I achieve graphical designability in the IDE and how can I force for example a dxBarManager toolbars to stay within the components boundaries? I'm also interested about the reason why these things happen, if someone knows by heart. Cheers!
-
I'm experimenting with this but I can not seem to get it to work. procedure TMyTempComponent.HitTest(Var Msg: TCMDesignHitTest); begin // ShowMessage('Edit1.BoundsRect.Top: ' + Edit1.BoundsRect.Top.ToString + sLineBreak + // 'Edit1.BoundsRect.Left: ' + Edit1.BoundsRect.Left.ToString + sLineBreak + // 'Edit1.BoundsRect.Bottom: ' + Edit1.BoundsRect.Bottom.ToString + sLineBreak + // 'Edit1.BoundsRect.Right: ' + Edit1.BoundsRect.Right.ToString + sLineBreak + sLineBreak + // 'Msg.XPos: ' + Msg.XPos.ToString + sLineBreak + // 'Msg.YPos: ' + Msg.YPos.ToString); If PtInRect(Edit1.BoundsRect, Point(Msg.XPos, Msg.YPos)) Then Msg.Result := 1 Else Msg.Result := 0; end; The event does not fire at all if I move the cursor over any child component (Edit, button or radiogroup) only when it is over the frame itself. I also tried adding ControlStyle:= ControlStyle - [csDesignInteractive]; in the constructor. I'll keep trying, but am I on the right track here? Maybe frames are just work... different? π
-
This is why π
-
Sure. There is no code whatsoever: unit TempComponent; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls; type TMyTempComponent = class(TFrame) Edit1: TEdit; Button1: TButton; RadioGroup1: TRadioGroup; private { Private declarations } public { Public declarations } end; Procedure Register; implementation Procedure Register; Begin RegisterComponents('Temp component', [TMyTempComponent]); End; {$R *.dfm} end.
-
Hello, I'm using TurboPack SynEdit latest version, fresh from GitHub. I have an editor where the user can enter multiple SQL commands, like: SELECT * FROM MYTABLE SELECT * FROM OTHERTABLE o [...] Is there a way SynEdt can return the full block, but the current command only? So, if the cursor is staying in line 1 just return "SELECT * FROM MYTABLE" but at line 2 it would "SELECT * FROM OTHERTABLE o"? Keep in mind that commands can be split across several lines, so simply returning the current line is not a good solution. Summoning @pyscripter π
-
SynEdit - Get current SQL command block
aehimself replied to aehimself's topic in Delphi Third-Party
For anyone interested I solved this without writing an actual interpreter, it parses all select aliases. The backdraw is that it offers aliases to use in statements where there is no such alias therefore executing the statement will throw an exception. This annoyance is perfectly acceptable by me, so probably I'll just leave it as it is. It derives a new analyzer from Zeos's generic analyzer, and it looks like this: Unit uAEGenericSqlAnalyser; Interface Uses ZGenericSqlAnalyser, ZTokenizer, ZSelectSchema; Type TAEGenericSqlAnalyser = Class(TZGenericStatementAnalyser) public Function DefineAllSelectSchemaFromQuery(const Tokenizer: IZTokenizer; const SQL: string): TArray<IZSelectSchema>; End; Implementation Uses System.Contnrs, System.Classes; Function TAEGenericSqlAnalyser.DefineAllSelectSchemaFromQuery(Const Tokenizer: IZTokenizer; Const SQL: string): TArray<IZSelectSchema>; Var Tokens: TZTokenList; sections: TObjectList; ss: IZSelectSchema; FromTokens: TZTokenList; deleted: Boolean; Begin SetLength(Result, 0); Tokens := TokenizeQuery(Tokenizer, SQL, True); Sections := SplitSections(Tokens); Try Repeat ss := DefineSelectSchemaFromSections(Sections); If Assigned(ss) Then Begin SetLength(Result, Length(Result) + 1); Result[High(Result)] := ss; FromTokens := FindSectionTokens(Sections, 'FROM'); deleted := False; While (Sections.Count > 0) And Not deleted Do Begin deleted := TZStatementSection(sections[0]).Tokens = FromTokens; Sections.Delete(0); End; End; Until ss = nil; Finally Tokens.Free; Sections.Free; End; End; End. To use it: Procedure TForm1.GetTableName(Var inNameOrAlias: String); Var sa: TAEGenericSqlAnalyser; sch: IZSelectSchema; a: Integer; Begin sa := TAEGenericSqlAnalyser.Create; Try For sch In sa.DefineAllSelectSchemaFromQuery(SQLConnection.DbcConnection.GetTokenizer, SQLCommand) Do For a := 0 To sch.TableCount - 1 Do If sch.Tables[a].Table.ToLower = inNameOrAlias.ToLower Then Exit Else If sch.Tables[a].Alias.ToLower = inNameOrAlias.ToLower Then Begin inNameOrAlias := sch.Tables[a].Table; Exit; End; Finally FreeAndNil(sa); End; End; SQLConnection is a TZConnection object (can be replaced with TZTokenizer.Create if no connection is available), SQLCommand is the string containing all the statements. Simply call this method to "convert" an alias to a table name. It can be optimized but since it's already pretty quick, I didn't really bother. Yeah, extracting the current block would be more elegant. In 1 hours with minimal personal code, this is what I could achieve π -
SynEdit - Get current SQL command block
aehimself replied to aehimself's topic in Delphi Third-Party
I was afraid of this so. I do use ZeosLib. The main purpose I'm interested about the current command is to parse table aliases (which Zeos is kind enough to do so) but I need to be able to provide the current command only - otherwise it only considers the first one. Even though if I can split up to separate commands, I somehow need to confirm if that is the command under the cursor... -
Custom component ParentBackground property resets to false
aehimself posted a topic in Delphi IDE and APIs
Hello, We have a custom component derived from Delphi's standard TPanel. What I realized is that the ParentBackground property resets to False each time the form is opened in the Delphi IDE (no saving is required, it automatically updates the DFM). There are no mentions / changes made to this property in the component's code and the situation does not change even if I add this published property: property ParentBackground stored true default True; I found an old topic about OldCreateOrder behaving the same way, but the proposed solution (remove the ParentBackground = False from the DFM with a text editor) only works until the form is reopened again - then it will automatically reset to False and update the DFM immediately without saving. I'll keep digging and investigating but if anyone has experience with this please do not hesitate to share the solution π Thanks! -
Custom component ParentBackground property resets to false
aehimself replied to aehimself's topic in Delphi IDE and APIs
So, the issue had nothing to do with the panel, but a label on it... and by replacing it from TLabel to TcxLabel to be precise. There was a section in the constructor which looked like this: With TLabel.Create Do Begin // [...] If criteria Then Color := clRed; End; This was fine but in the moment someone changed it with cxLabel (which has no public .Color property) that one line immediately turned ParentBackground off on the panel as it changed it's color instead of the labels. This is one reason I hate With blocks π -
Hello, I just finished rewriting a method which now only lists font names useable by TSynEdit. The theory is easy; while enumerating Screen.Fonts I'm passing them to the exact same check as TSynEdit does (SynDWrite.pas, IsFontMonospacedAndValid). If it returns true, I add it to the list. The issue is, there is a CheckOSError call in said method which break the execution of the program way too many times if it is run from the IDE. The current implementation is as follows: function IsFontMonospacedAndValid(Font: TFont): Boolean; var LogFont: TLogFont; DWFont: IDWriteFont; begin try Assert(GetObject(Font.Handle, SizeOf(TLogFont), @LogFont) <> 0); CheckOSError(TSynDWrite.GDIInterop.CreateFontFromLOGFONT(LogFont, DWFont)); Result := (DWFont as IDWriteFont1).IsMonospacedFont; if (FontFamilyName(DWFont) <> Font.Name) and (fsBold in Font.Style) then Font.Style := Font.Style - [fsBold]; except Exit(False); end; end; I can put the EOSError exception type as a globally ignored one in Tools / Options but understandably it is not an ideal solution. I also could copy and paste this check, removing the CheckOSError but if I do I have to keep an eye on the official implementation to make sure to update my version if the original changes. Is there a compiler directive which will ignore a specific exception type within a block of code? Thanks!
-
@Steven Kamradt An important rule of writing maintainable software is that you are not changing the code in any external components. Doing that will come to bite you in the back later on; especially if you try to update said component.
-
I solved this issue by changing my icons to Material Designs Webfont and providing them to my application using Icon Fonts Image list. When the style changes I just update the .FontColor property of the image list to TStyleManager.ActiveStyle.GetStyleFontColor(sfWindowTextNormal). This way I can be certain that whatever is selected, my toolbar and popup menu icons are always visible. As an extra, if you are fine with the monochrome look, you can spare the fee of the designer.
-
How does CurrentControlSet differ from HKEY_CURRENT_USER
aehimself replied to Tom F's topic in Windows API
I'd doublecheck every registry operation and make sure every .OpenKey has a .CloseKey, and every TRegistry.Create has a .Free in the Finally block. I had my share of issues when I wanted to reuse the same TRegistry object within a method to perform different writes. I ended up creating it and freeing it up after each "task". -
Types incompatibles : 'string' et 'Extended'
aehimself replied to abdellahmehdi's topic in Algorithms, Data Structures and Class Design
*** Screams in JavaScript *** -
Hello, I have a pretty basic update mechanism in my applications which gets the job done - but it has it's limitations. It would be nice to have "dev" and "stable" channels, messages to be shown to the users (e.g. if there's a yet unfixed bug with a workaround), going back to a previous version, delta updates only, etc. So I started to rework the thing as it is but I am simply stuck on the design stage... I can not agree with myself on how it should be done properly. My base theory is one file in one update archive, the application determines what it needs to update and download only those archives to minimize network traffic. - Put a static JSON with all version information, this gets refreshed each time a version is deployed? This is how the current system works: easy to implement but... - Just put the ready archive with the new version in the folder and have a service explore the changes and rebuild the static JSON accordingly? This option sounds the best to avoid any lingering files / entries (e.g. archive is placed but changed were not placed in the DB or vice versa) but where the changelog is coming from in this case? - Store everything in a database and use a PHP script to query and assemble the reply JSON? My PHP knowledge is really limited, so I'd prefer not to have this option. Although, a dynamic list is crucial to minimize traffic (why to download version information for different products, or changelogs for versions below the current one?) So my question is... are there any readily available update platforms for Delphi (server and client side too) which I can simply implement in my applications and forget about this matter? I'm also open to suggestions on how the thing should work, on all possible levels: - Backend. Is a database really needed or overkill? - Static vs dynamic update definitions. Dynamic is better from many perspectives but does it really worth the extra effort? - Protocol. Should I really stick to HTTP, or is there a better / create my own? - How the information is translated and sent, including how the application should know if a new file is added to the distribution? Cheers!
-
WinSock (Indy) select() doesn't return on network
aehimself posted a topic in Network, Cloud and Web
Hello, We have an application built on Delphi 10.4.1 / 10.4.2 which is communicating with a server using the Indy TidHttp component. It works perfectly, but there is one particular call when the result can arrive in 1-1,5 hours... and this is where things get strange. The request is sent with idHttp.Post (using a stream as an outgoing and an incoming data buffer) and if the reply arrives in 30 minutes, all is fine. Somewhere between 30 minutes and one hour, the underlying WinSock .select never returns. Data is sent out, received by the server, processed and the data is sent out - but never received by the client. Using WireShark it can be seen that the moment the server sends the reply, the client issues a TCP retransmission... maybe it thinks that the data was lost but was unable to use the channel while waiting for data? Then the reply arrives, channel gets free and it sends the retransmission but discards the data received? These are just guesses, I'm not very familiar with this low-level functionality of WinSock. Oh, one more thing... this issue is NOT present if the server and the client is on the same machine; connecting to localhost makes a difference. Stack trace where the application stops is as follows: :772729dc ntdll.ZwWaitForSingleObject + 0xc :74417555 ; C:\WINDOWS\SysWOW64\mswsock.dll :751c5f1e WS2_32.select + 0xce IdStackWindows.TIdSocketListWindows.FDSelect(???,???,nil,???) IdStackWindows.TIdSocketListWindows.SelectRead(-2) IdSocketHandle.TIdSocketHandle.Select(???) IdSocketHandle.CheckIsReadable(???) IdSocketHandle.TIdSocketHandle.Readable(-2) IdIOHandlerStack.TIdIOHandlerStack.Readable(???) IdIOHandler.TIdIOHandler.ReadFromSource(True,-2,False) IdIOHandler.TIdIOHandler.ReadLn(#$A,-1,16384,TIdASCIIEncoding($1A138AD4) as IIdTextEncoding) IdIOHandler.TIdIOHandler.ReadLn(nil) IdHTTP.TIdCustomHTTP.InternalReadLn IdHTTP.TIdCustomHTTP.DoRequest(???,'http://10.0.2.53:12345/BIN',$2E6D75A0,$2E6D73A0,(...)) IdHTTP.TIdCustomHTTP.Post('http://10.0.2.53:12345/BIN',$14E0F58,$2E6D73A0) Before you say anything, I know this is a bad design. We shouldn't wait on long lasting operations but to poll for it on HTTP. What I'd like to know is what happens and why it happens so I can get an insight if patching the mess worth it or just jump straight to refactoring. Cheers! -
WinSock (Indy) select() doesn't return on network
aehimself replied to aehimself's topic in Network, Cloud and Web
Aren't these supposed to be services (like Apple's or Google's notification service) or websockets...? -
WinSock (Indy) select() doesn't return on network
aehimself replied to aehimself's topic in Network, Cloud and Web
Honestly, I don't know if it would make any sense. It's your decision at the end. Would have made my implementation easier (and error-free the first try π) but our implementation is clearly wrong here. In normal operation the HTTP protocol should work in "bursts": request something, get an answer, repeat until all done. If someone else needs a TCP-level keep-alive in HTTP instead of fixing the real issue (like in my case) these 3 lines of extra code seems very well deserved. Plus the solution is now publicly accessible in this thread. -
WinSock (Indy) select() doesn't return on network
aehimself replied to aehimself's topic in Network, Cloud and Web
I decided to implement both. Upon enabling the TCP keepalive if a handle is allocated SetKeepAliveValues is called but there is now an OnSocketAllocated handler which checks if the keepalive was enabled and if yes, calls SetKeepAliveValues. This way the exception disappeared; your guess was right, there was no handle allocated after the DoRequest call. As I could not reproduce the freezing of the application I can not confirm whether that disappears or not... guess time will tell sooner or later. Thank you! -
WinSock (Indy) select() doesn't return on network
aehimself replied to aehimself's topic in Network, Cloud and Web
@Remy Lebeau Just yesterday a new issue was reported which is in direct connection with the solution in this thread. The code is fairly simple: procedure TCustomActionCallerThread.Execute; begin V_CONNECTION.IndyHttpClient.Socket.Binding.SetKeepAliveValues(True, FTCPKeepAlive, FTCPKeepAlive); Try try V_CONNECTION.DoRequest(FActionName, FRequestXml, FResult); except on E: Exception do FResultException := Exception(AcquireExceptionObject); end; Finally V_CONNECTION.IndyHTTPClient.Socket.Binding.SetKeepAliveValues(False, 0, 0); End; end; The DoRequest (where the HTTP communication actually takes place) is quick (less than a second) but the code in the finally block (to disable the keepalive) throws an exception: Socker Error # 10038 Socket operation on non-socket. To make things more interesting, if the application is built with Delphi 10.4.1, it completely freezes. 10.4.2 only throws the exception but the operation finishes successfully. Do you have an idea why this error might appear? We use HTTP keepalive, so the socket should still exist after the DoRequest call. Is there a check which I can use as a condition to prevent this from happening? Also, do you happen to know in any difference between the Indy versions in 10.4.1 and 10.4.2? I can not really find a logical explanation in the behavior difference. Thank you!