-
Content Count
1090 -
Joined
-
Last visited
-
Days Won
23
Everything posted by aehimself
-
For "general discovery" you will need a ping / port scanner. I wrote a fairly simple one a couple of years ago with DNS lookups: However, if you are looking for a specific program, you only need to display hosts where you find your application listening.
-
View toolbar icons suddenly not working
aehimself replied to Greggory's topic in Delphi IDE and APIs
So the company just allowed 20H2 on our developer machines. Unfortunately the toolbar icon still does not work but the key combination does. I could try a reinstall of Delphi but I really don't feel the motivation to do so. -
Logitech Gaming LED SDK for Delphi
aehimself replied to FPiette's topic in RTL and Delphi Object Pascal
I fell in love with A4Tech products since I bought my first, which was a A4Tech 4D+ dual scroll PS/2 mouse way back in high school. With only a small amount of exceptions, ergonomically they suited me the best. Although I didn't have a desktop PC for 15+ years now, when my mouse died I felt lucky that the XL-750BK was still available. At work I also changed my keyboard with the G800V I had laying around at home. They are also marked as "gaming" products but no fancy RGB, only keys which can be macroed and higher resolution / data transfer rates. If I'll have to change again, I'll look into A4Tech first for sure. -
View toolbar icons suddenly not working
aehimself replied to Greggory's topic in Delphi IDE and APIs
Can anyone confirm that this only happens on Windows 2004? Yesterday after installing the MS Windows patches offered by the company my "View unit" button stopped working, while it was fine before. Same Delphi version (10.4.1) on a different machine, receiving updates from MS directly (therefore, instead of 2004 it's 20H2) it's all fine. -
git and Delphi tooling?
aehimself replied to Lars Fosdal's topic in Project Planning and -Management
I'm using VS Code and git bash for the tasks VS Code can not handle. Diff view is one of the best I've seen, merge conflicts are easy to solve. Just install a time line addon and you are ready to go. -
If DateInFuture > Now Then seconds := SecondsBetween(Now, DateInFuture) else // Date already passed Then you can divide seconds down to years, months, etc. Alternatively, you can use YearsBetween, MonthsBetween, etc.
-
What OP actually asked and what he wanted to ask are two different things. We assumed what he would like to know and offered directions in how to achieve it. Now our assumptions might be wrong of course but with the information available this is how far one can get. I suppose OP's proficiency in English is not native. At least some of us attempted to help.
-
Best? Definitely no. Capable of, while being good enough? Absolutely. My own compiled Delphi code will always consume less resources (and will be more trustworthy to me) than PHP, for example.
-
Use a HTTP(s) server component. Either put the files on the disk and set it as the root folder, or store them as resources; you can even generate them on-the-fly. ICS for example has pretty good example projects to get you started.
-
Not yet, but it's the most requested feature for a long time. For the time being only a memory based SQLite database can be used as a workaround.
-
Off: for anyone wondering 7.3 is not dead, only the version number was bumped to 8.0 a while ago. It is basically the same codebase but due to the changes breaking backwards compatibility the main version number was increased instead.
-
Where can i find file Vcl.ExtCtrls.pas Vcl.ExtCtrls.hpp post installation of RAD Studio 10.4?
aehimself replied to Jenifer's topic in VCL
Maybe trial comes with compiled .DCUs only? Can you find Vcl.Controls.dcu? -
How to optimize exe loading times
aehimself replied to a topic in Software Testing and Quality Assurance
Connecting - 15 seconds. Application stops responding. Opening a large dataset with lots of BLOB fields - 1 hour. Application still does not respond. Make a change, post it back and commit. Another 5 seconds of white screen. But noone really cares at this point. For ease of code, put everything on your form. For everything else, there are Threads. (powered by Loreal and Mastercard 😄) -
How to optimize exe loading times
aehimself replied to a topic in Software Testing and Quality Assurance
That is actually (half of) the best thing(s) that can happen to an application. The other half is, having dataset operations (.Open, .Post, .Refresh, etc.) in the same thread. -
I started actively using databases in the past couple of years in my applications, and these are the things I wished to know from the beginning: - Every SQL thing should be in a separate thread. If connection takes 20 seconds or you are downloading a very large dataset over a slow connection, your application will be frozen. Publish important data or the complete dataset objects as properties. Just make sure you disconnect all datasources before any operation and reconnect them after, as data events will start to fire off in an inconsistent state causing AVs most probably. - When it comes to threads, a golden rule is that each thread should have it's own connection to the database. You also want to make sure that threads are working with different tables or you should have correct transaction isolation set up. - For service applications I wrote my own "ORM", which is basically a wrapper for a TQuery. Each field the query returns are published as typed properties. So instead of query.Edit; query.FieldByName('field').AsString := 'Hello, world'; query.Post; I simply can say: myTable.Field := 'Hello, world'; and myTable takes care of everything else. I took this one step further after a couple of DB changes and wrote my own planner. I tell it what kind of tables and fields I want, and it generates MySQL and MSSQL create and delta scripts AND all the myTable classes as Delphi source files. I make a change, I have all the SQL scripts ready to ship with the update and I already can use the new fields in all of MyTable objects... you get the point. - Depending on the component you use (and how they access the databases) client libraries might not be thread safe or requiring special parameters upon establishing the connection to be thread safe! I found it as a best practice for example to have a critical section when connecting as I had strange access violations when 7 worker threads tried to connect using the same library at the same time. - If performance is critical, do not use TQuery.FieldByName. That's all I can think of for now but I'm sure I missed a few. If anything else pops up, I'll try to remember to update this thread.
-
Where can i find file Vcl.ExtCtrls.pas Vcl.ExtCtrls.hpp post installation of RAD Studio 10.4?
aehimself replied to Jenifer's topic in VCL
Start -> cmd -> dir C:\fileiwanttofind.now /s /b Works like a charm. -
Looping Memo.Text
aehimself replied to Henry Olive's topic in Algorithms, Data Structures and Class Design
If you are willing to look into different components, Zeos has a TZSQLProcessor which does just this. You give it your full query, like "Create Table A (ID Integer); Insert into A Values(1); Update A set ID=2 Where ID=1;" Then it splits it up and executes them one by one. Other than this, you'll have to write the parser yourself. Beware though, splitting up simply by ; is NOT GOING TO WORK. Examples: INSERT INTO MYTABLE (MYSTRINGFIELD) VALUES (";") INSERT INTO MYTABLE (MYSTRINGFIELD) VALUES ("x") -- This is just for fun; DROP TABLE MYTABLE and so on, you get the point... -
Just a small heads-up; binary transfer requires different handling than text. See
-
Just store the TMemos in a TList when you create them so you don't have to find them later. When generating; have two nested cycles going through that list. Outer from Low(list) To High(list) - 1; inner from outer var to High(list). Not efficient, but easy enough to understand what is going on.
-
I guess it depends on the resolution and color depth, but isn't transferring still images and showing them as a video... laggy? I never worked with videos in my entire life, but - as far as I understood - this is why they identify key frames; so they transfer / store only the delta inbetween those.
-
That's a lesson I learned through a number of painful refactorings. NEVER put business logic to UI controls. Like... Unit Helper; Interface Function MyFunction: String; Procedure DoSomething; Implementation Function MyFunction: String; Begin Result := 'Hello, world'; End; Procedure DoSomething; Begin Sleep(1000); End; End. And then just put Helper in the uses classes of your form, and simply call ShowMessage(MyFunction); ?
-
And this is fine, unless a significant amount of them will never be looked at, and just forgotten when 10.4.2 comes out. Afaik, there are bugs what Delphi is carrying for more than 5 years.
-
TWSocketClient.OnDataAvailable not firing off after a day or two
aehimself posted a topic in Network, Cloud and Web
Hello, Most probably this is NOT going to be an issue with ICS as I experienced the very same symptom with TServerSocket before I made the switch. I'm mainly looking for tips on where can I start debugging the issue as for the time being I'm completely out of ideas. I have an application which is connecting to a server on a single, TCP socket. On average, 80 bytes (binary) are sent from the clients to the server each minute, in one packet. The TCP channel is unidirectional, messages are only going from the client to the server. Everything is working perfectly, until a seemingly random time; when for a seemingly random client the data is not received anymore. The TCP connection is still established, the client is still sending the packet and WireShark confirms that it arrives to the server machine. It seems that the socket's receive event stops firing off. What is even more interesting, that it affects random clients (with different OSes, sometimes Windows 2000, sometimes 2012 R2, sometimes 2019), only causes one client to get stuck at a time, but multiple clients can get stuck during the process. The application can remain in this state for days without memory increase (so I'm not inflating the local buffer endlessly, without triggering the data processing), memory or handle leaks. If I restart the client or the server, forcing the client to reconnect, everything jumps back to normal. As for a little background, the very same logic was working perfectly, when the binary data was converted to, and sent as text. By switching to binary the sent data size was reduced from 200-500 bytes to 60-100. I don't know why but I suspect this change triggered the error I'm seeing now; and maybe because of the data size. https://docs.microsoft.com/en-us/troubleshoot/windows/win32/data-segment-tcp-winsock mentions that TCP is not really efficient with unidirectional, small data packets but it only will result delivery delay. For me it seems to be irrelevant. Sending code looks something like this (TBufferLength = Word): Function TCommunicationEngine.Send(Const inText: String): Boolean; Var buf, len: TBytes; sent: Integer; Begin Result := False; Try // Step 1 - String to TBytes buf := TEncoding.UTF8.GetBytes(inText); // Step 2 - Encryption of "buf" // ... // If the buffer exceeds the maximum length allowed, raise an error as it can not be sent! If Length(buf) > TBufferLength.MaxValue Then Raise ETCPPortError.Create('Buffer overflow, cannot send ' + Length(buf).ToString + ' bytes!'); // Step 3 - Append the length of the buffer to the beginning of the buffer SetLength(len, SizeOf(TBufferLength)); PBufferLength(@len[0])^ := Length(buf); SetLength(buf, Length(buf) + Length(len)); Move(buf[0], buf[Length(len)], Length(buf) - Length(len)); Move(len[0], buf[0], Length(len)); // Step 4 - Send the completed buffer sent := _tcpport.Send(@buf[0], Length(buf)); // Step 5 - Post-sending verifications If sent < 1 Then Raise ETCPPortError.Create('No data was sent!'); Log(LOG_TCP, '> ' + BytesToString(buf) + ' (' + sent.ToString + ' bytes)'); Result := True; Except On E:Exception Do HandleException(E, 'while sending data'); End; End; Receiving block looks like this (TClientConnection is a descendant of TWSocketClient, _data is a strict private TBytes, _count is a strict private Integer): Procedure TClientConnection.ConnectionDataAvailable(inSender: TObject; inError: Word); Var buf: TBytes; need, len, read: Integer; debuglog: String; Begin // Note that due to how TCP works, if packets are arriving at high speed they might be appended to one single ReceiveText event. If BanList.IsBanned(Self.PeerAddr) Then Self.Close // If the IP where the data is coming from is banned, disconnect Else Begin len := Self.RcvdCount; If len = 0 Then Exit; Repeat debuglog := Self.PeerAddr + ' > Read cycle starts. received data size: ' + len.ToString + ', socket data size: ' + Length(_data).ToString + ', position: ' + _pos.ToString + '. '; If _pos = 0 Then Begin // Position is 0 = there is no fragment. Read the data size first If len < SizeOf(Word) Then Begin BanList.Failed(Self.PeerAddr, 'Packet size is incorrect'); Self.Close; // Packet is corrupted, reset the connection Exit; End; SetLength(buf, SizeOf(TBufferLength)); Self.Receive(@buf[0], Length(buf)); // buf now contains the data size. Resize socket's data length SetLength(_data, PBufferLength(@buf[0])^); // As the data size is read out, reduce the received length len := len - Length(buf); debuglog := debuglog + 'Prepared a ' + Length(_data).ToString + ' byte buffer. '; End; need := Length(_data) - _pos; If need < 0 Then Begin // this should never happen. I'll just keep it here for debugging purposes... Log(LOG_STD, 'Possible memory corruption happened. Data size of ' + Self.PeerAddr + ' is ' + Length(_data).ToString + ', position is ' + _pos.ToString); Self.Close; Exit; End Else If need > 0 Then Begin If len < need Then SetLength(buf, len) // If we received less bytes than needed to fill the buffer, read everything Else SetLength(buf, need); // If we received more bytes than needed to fill th buffer, only read what is needed debuglog := debuglog + 'Reading out ' + Length(buf).ToString + ' bytes. '; read := Self.Receive(@buf[0], Length(buf)); If read > 0 Then Begin debuglog := debuglog + read.ToString + ' bytes read. '; // Something was read from the buffer. Append it to the socket's data Move(buf[0], _data[_pos], read); // Increase data position Inc(_pos, read); // Reduce received length len := len - read; End Else debuglog := debuglog + 'Nothing was read. '; End; If _pos = Length(_data) Then Begin Log(LOG_TCP, debuglog.TrimRight); Log(LOG_TCP, Self.PeerAddr + ' > ' + BytesToString(_data) + ' (' + _pos.ToString + ' bytes)'); // Buffer is full. Process the data. // Decrypt the buffer... // ... Try ProcessLine(Self.PeerAddr, timestamp, TEncoding.UTF8.GetString(_data)); Except On E:Exception Do Begin Log(LOG_STD, TranslateException(E, 'processing client data')); BanList.Failed(Self.PeerAddr, 'data processing error: ' + E.Message); Self.Close; End; End; _pos := 0; SetLength(_data, 0); End Else If Not debuglog.IsEmpty Then Log(LOG_TCP, debuglog.TrimRight); Until len = 0; If (Length(_data) > 0) Or (_pos > 0) Then Log(LOG_TCP, 'Storing a fragment for ' + Self.PeerAddr + ': Data size: ' + Length(_data).ToString + ', position: ' + _pos.ToString); End; End; I know that there are a couple of premature exits before the actual data processing, but even when I added temporary logging before these, none of them was reached. I'll investigate on how I can, and will try to add TCP_NODELAY and SO_SNDBUF, but I doubt that they will make any difference. Until then, I'm really interested what are the aspects what I did not even think of until now. I'm using ICS v8.64, application is compiled using Delphi 10.4.1 as a 32-bit executable, and is executed as a Windows service on a Server 2012 R2 machine. Any help is greatly appreciated 🙂 -
TWSocketClient.OnDataAvailable not firing off after a day or two
aehimself replied to aehimself's topic in Network, Cloud and Web
So, ~ two weeks have passed, no issues experienced so far. Packets are leaving the client and arriving to the server and even decoded properly; which means that the new read-out logic is functioning as planned. As the longest I've seen was about a week with the old solution I'm getting more confident that this was indeed the source of the issue (just to be sure I'll wait some more before considering it fixed, though). Despite the fact that that the old, TClientSocket / TServerSocket doesn't seem to be the root cause I'm glad I went on and changed them with ICS. Now I have one less concern and a ton of opportunities (like IPV6, which did not even exist when the old components were created by... Borland?) When I detect an anomaly (like an undecryptable / corrupted packet) I'm force-closing the connection with the client from the server side (by calling Self.Close, calling from a descendant of TWSocketClient). I'm assuming this also clears and destroys the queue so I don't need to read it empty first...? As my assumption was wrong with my initial receive logic, It's safer to ask I suppose 🙂 -
This is something we (almost) all can relate to. Which makes me wonder... how many of us wrote our own password managers, instead of using an existing one? How many "fun projects" we have ready which are never used/published because we used it only to gain experience? ...or is it just me?