A.M. Hoornweg
Members-
Content Count
473 -
Joined
-
Last visited
-
Days Won
9
Everything posted by A.M. Hoornweg
-
Best type for data buffer: TBytes, RawByteString, String, AnsiString, ...
A.M. Hoornweg replied to Rollo62's topic in Algorithms, Data Structures and Class Design
Because the data is not strings. But the in-memory representation of a dynamic array is rather similar so the functionality should be straightforward to duplicate. And thanks to generics, it's probably possible to design the base class to be universal, IIRC a tbytes is just something like "tarray<byte>" (but I may be wrong here). -
Best type for data buffer: TBytes, RawByteString, String, AnsiString, ...
A.M. Hoornweg replied to Rollo62's topic in Algorithms, Data Structures and Class Design
Lookup "operator overloading". It should be possible to do something like newbytes:=something+somethingelse+'Hello world'; But adding text to tBytes or searching a text inside a tBytes would mean code page handling. You could consider defining a Binarystring type (type binarystring=type Ansistring (28591)) because that's least likely to get messed up. -
Best type for data buffer: TBytes, RawByteString, String, AnsiString, ...
A.M. Hoornweg replied to Rollo62's topic in Algorithms, Data Structures and Class Design
Strings should not be used for byte manipulation (which does not contradict an earlier post of mine, explaining how to get legacy code libraries working if they do this). Rather, write a new class or class helper that enhances tbytes and offers the functionality that makes strings so practical (insert, delete, append, concatenate, pos). Maybe even some new classes "tBytelist" and "tByteBuilder" as an analog to tStringlist and tStringbuilder. -
[Delphi 10.4] Deleting controls/components on Ancient form, Causing AVs on inherited forms!
A.M. Hoornweg replied to c0d3r's topic in Delphi IDE and APIs
When I want to safely delete a component on an ancestor form, I do it outside of Delphi. I use a search tool (Agent Ransack) to find out if the component appears in any descendant forms. I simply search the folder for all *.dfm files which contain the text "inherited mycomponentname". Then I simply drag &drop the resulting list of file names onto an empty Notepad++ window and perform the deletion manually. When this is done, it is safe to delete the component on the ancestor form. -
Which brings us back to the solution I proposed originally. Ensure that the compressed data is valid (with another checksum) before trying to feed it to the decompressor.
-
If the data can only be verified after expansion and the expansion algorithm crashes, then we still don't know if the data or the implementation is broken.
-
If that were true, a non-matching checksum would mean a broken algorithm and not necessarily broken data. It would tell us exactly nothing.
-
"contains a checksum value of the uncompressed data" This isn't foolproof because the decompressor can only verify that checksum after decompression. What if the data is corrupted in such a way that it causes the decompressor to crash (I quote OP: "endless loop") during decompression? One should also append a record containing the size and checksum of the compressed data at the end of the stream. That way one can check the integrity of the file before decompressing the data.
-
Automatically killing a service when stuck
A.M. Hoornweg replied to Thijs van Dien's topic in Windows API
I had situations where an ADO connection to MS SQL Server would just "lock up" whilst executing a SQL statement and the call never returned. No exception, no error, just a lockup. I suspect it had something to do with our IT department's scheduled backup job saving the SQL Server VM because it always happened around that time of night. Since lockups like that are out of the programmer's control, all I could do was design a workaround and it turned out that a watchdog thread worked brilliantly. -
"Simulating" a com port with data arriving
A.M. Hoornweg replied to Dave Nottage's topic in Windows API
VMWare workstation will let you do this. Just add two virtual serial ports to the VM, in the field "connection" specify "named pipe". Both COM ports must use the same name for the named pipe (just call it "serialportpipe") and one port must be configured to be "server", the other must be "client". That way, the two COM ports are crosswired. Now you can start 2 applications, connect them up to the two COM ports and let them talk to each other. -
I don't know if Zlib writes a checksum or not, maybe you should append it yourself at the end of the compressed stream. Then first verify that checksum before trying to decompress it.
-
Automatically killing a service when stuck
A.M. Hoornweg replied to Thijs van Dien's topic in Windows API
I do it slightly differently: - The main process launches a child process for the work. - The child process creates a mutex. The service application knows if the child process is still running by periodically checking the mutex. - The child process has a separate thread called tWatchdogThread. - The main thread must call "tWatchdogThread.Notify" regularly to prove it's still alive. - If that sign of life stays out for more than 10 seconds, the watchdog assumes the main thread has crashed and ends the process using TerminateProcess(). This mechanism has proved to be extremely reliable, I use it often. -
Workaround for binary data in strings ...
A.M. Hoornweg posted a topic in RTL and Delphi Object Pascal
Hello all, when Delphi didn't know about unicode yet people would often stuff binary data into strings because strings were soooo practical and easy to manipulate. Yes that is and was bad practice and highly frowned upon, but as we all know it was done anyway and people got away with it because Delphi didn't care about code pages at the time so it just worked. It was even done in professional libraries. Code like that is very hard to port to newer Delphi versions which are unicode-enabled and codepage-aware and when you attempt a conversion, subtle errors may happen where you least expect it. If you still have precious old code libraries that do such things and which would be too costly or too complex to rewrite, you may consider trying this workaround as a quick & dirty fix: Type Binarystring= type Ansistring (437); The nice thing about code page 437 is that all 256 possible ansichar values map to valid unicode code points so you can safely assign these strings to Unicodestrings and back again without data loss and Delphi's built-in string functions won't break the binary data contained in these strings. So you just may me able to salvage some legacy code by declaring this new type and then replacing all "string" declarations in the code with "binarystring" and all (p)Char with (p)Ansichar. And yes, it's still bad practice... (The idea is originally from Raymond Chen: https://devblogs.microsoft.com/oldnewthing/20200831-00/?p=104142 ) -
Workaround for binary data in strings ...
A.M. Hoornweg replied to A.M. Hoornweg's topic in RTL and Delphi Object Pascal
For my own source code I switched to tBytes too, long ago. But I have several legacy third party libraries of companies that no longer exist and that are still very nice when used with Delphi 2007. So when I came across Raymond Chen's blog I had the idea to salvage these libraries without mutilating the existing source code too much. And I stated loudly and clearly that this is just a quick & dirty fix to get it done. As Remy Lebeau stated, assigning RawByteString <-> String may still do a codepage conversion. Ansistring(437) and Ansistring (28591) are safe. -
Workaround for binary data in strings ...
A.M. Hoornweg replied to A.M. Hoornweg's topic in RTL and Delphi Object Pascal
The VCL was strictly Ansi. I needed visual unicode compatibility in the early noughties and was forced to switch to a third-party VCL (first TNT, then LMD Elpack). -
Workaround for binary data in strings ...
A.M. Hoornweg replied to A.M. Hoornweg's topic in RTL and Delphi Object Pascal
Fair point. Moreover, the only tool in Delphi that could perform operations like copy/append/delete/insert/search on variable length data out of the box was "string". If all you have is a hammer, every problem looks like a nail. I remember that Windows NT (from the mid-1990s) was already unicode-enabled but Delphi didn't support that until version 2009. So all that time, a char and a byte were basically synonymous in Delphi. -
Workaround for binary data in strings ...
A.M. Hoornweg replied to A.M. Hoornweg's topic in RTL and Delphi Object Pascal
I do, all the time. But this is about salvaging older (possibly third party) ansi code without a rewrite. -
Workaround for binary data in strings ...
A.M. Hoornweg replied to A.M. Hoornweg's topic in RTL and Delphi Object Pascal
The documentation says that "Rawbytestring should only be used as a const or value type parameter or a return type from a function. It should never be passed by reference (passed by var), and should never be instantiated as a variable." I read that as "you may not declare variables of type "RawByteString" and do stuff with them". But if I define a Type Ansistring(437) and put binary data into it, the data is associated with a valid code page. A conversion to unicodestring will convert characters [#128..#255] to some different unicode code points, which is uninteresting, because a back-conversion to ansi codepage 437 will produce the original binary data again. So it is safe to use all normal unicode string functions as long as we remember to assign the final results back to an Ansistring(437). -
Voted!
-
Hello all, could it be that function UCS4StringToWideString misses the final character during the conversion? VAR s:UCS4String; W:String; begin s:=[220,98,101,114,109,228,223,105,103]; w:=UCS4StringToWideString(s); showmessage(w); end;
-
UCS4StringToWideString broken?
A.M. Hoornweg replied to A.M. Hoornweg's topic in RTL and Delphi Object Pascal
Most programmers ignore that UTF-16 is a variable-length encoding and treat it like UCS-2, falsely assuming that one widechar corresponds to one codepoint. While most of us probably don't handle exotic languages, this is the 21st century and sooner or later you'll stumble upon strings that contain Emoticons which just won't fit in one widechar ( https://en.wikipedia.org/wiki/Emoticons_(Unicode_block) . Delphi could use some better support for that, like iterators etc. Something like this: Function ReverseString (S:String):String; VAR c:UCS4Char; Begin Result:=''; FOR c in s do result:=c + result; End; And if you think this is far fetched, just look how elegantly Freepascal solves this, https://wiki.freepascal.org/for-in_loop#Traversing_UTF-8_strings . -
I've just discovered that ExtractFileDrive misses a corner case on Windows and posted a QC about it. https://quality.embarcadero.com/browse/RSP-31109 I was experimenting with very long file and path names (see https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation) and stumbled upon a file name syntax which I hadn't seen before. It turned out that Delphi didn't know about it either. The syntax is \\?\UNC\server\share which is just another way of writing \\server\share. Delphi's ExtractFileDrive returns "\\?\UNC" on such a path which is meaningless.
-
UCS4StringToWideString broken?
A.M. Hoornweg replied to A.M. Hoornweg's topic in RTL and Delphi Object Pascal
OK, I'll post a QC then. Edit: https://quality.embarcadero.com/browse/RSP-31114 -
The word "drive" is a bit unlucky. What ExtractFileDrive() really does is to return the root of a volume, which can be either a drive letter or a file share. ExtractFileDrive ('\\server\share\folder\filename.ext") returns "\\server\share", which is perfectly OK. I can use the result as the root path for a folder structure without problems. ExtractFileDrive ('\\?\C:\folder\filename.ext") returns "\\?\C:" which is also perfectly OK. This result, too, can be used as the root path for a folder structure without problems. The only corner case that isn't handled correctly is "\\?\UNC\" which is needed for long network paths. I think it wouldn't hurt anyone to support that syntax as well.
-
I respectfully disagree. The prefix "\\?\" is a well-known method to tell Windows that a program is able to parse file names longer than MAX_PATH. It seems that the prefix \\?\UNC\ extends this ability to network paths. Otherwise I wouldn't bother with them. But in this specific case I needed to test if two paths referred to the same volume so I used ExtractFileDrive, which failed on this syntax.