Jump to content

David Schwartz

Members
  • Content Count

    1264
  • Joined

  • Last visited

  • Days Won

    26

Everything posted by David Schwartz

  1. David Schwartz

    Threading question

    Hmmmm ... totally forgot about that. Never done it, actually. I'll look into it. Thanks!
  2. David Schwartz

    Threading question

    This is running inside a VM and I cannot install Delphi in it, so the debugger cannot be used. If it could, I probably would have tracked this down. (Running the code on my dev machine isn't possible at the moment for reasons I have not fully grasped.) All I'm wanting to do here is capture log data without interfering with the operation of the logic, which seems to be what's happening here. Alternative approaches are worth considering. What I'll try next: (1) install MadExcept. (2) try to set up CodeSite Lite to catch the messages rather than the current approach. We might have another logging lib available, like EurekaLog. It was used on one project I worked on a while back, and it seemed kind of clunky. I think it has a separate app to read its log files. (Or am I thinking of another one?) Any thoughts along that line are welcome.
  3. David Schwartz

    Threading question

    ok, let's review... this code was written around 2003 and has been changed very little. The company says it's "rock-solid". The guy who maintained it before me was on the job for 8 years or so, and I'm told he was meticulous. The app we've been running is dated early 2017 and it runs every day at 10 AM. None of this is a problem. However, it has a bug that's not related to any of this that I'm trying to fix. It started in early March and I stumbled onto it only because I started adding a bunch of lines that call StatusOut to report what's going on inside this code. It's basically behaving like a box containing Shröedinger's bug ... the more I try to zoom in to observe the bug, the more unstable the whole thing gets. 🙂 I used to do a lot of embedded real-time dev work, but have very little experience with multi-threading in Windows in general and Delphi in particular. The main form is very light-weight and only creates the thread and runs it. When the thread is finished, then the main form is closed. It's not getting shut down early, although I did manage to kill it once that left the thread running. That was weird to see. The code in the thread calls the StatusOut( ) method to send status info that's handled as can be seen above: it's copied into a TMemo and sent to a logger. What appears in the memo and the logger are identical. It's not sending records, allocating objects, or anything that needs to be disposed on the other side of the call. It's just sending strings. When I use the other method to send the message, it shows up as trash -- a bunch of Chinese or Japanese or some other text. I don't know why. The messages sent are mostly string constants with IntToStr() calls sometimes added at the end. I discovered there are other things happening that need to be addressed. Unfortunately, there's no way I've found to run this inside of the IDE to debug it, so I have to add debug traces to the code to see what's going on in the servers where it does run. Also, there's a server with Windows XP hosting this which is what motivated this whole thing, because the company needs to move it to over Windows 10, and that's where they started to see some weird stuff. It turns out that the weird stuff they noticed is what's supposed to happen; for whatever reason, it's NOT showing up in the Win XP machine, which is strange, but does not seem to be causing any problems. I don't care, I just need it running properly in Win 10. Yes, a lot of data is assigned to local string vars before it's being handed to StatusOut, and there are lots of Format statements generating data like: StatusOut( Format( '. . .', [a, b, c] ) ); where the variables are both local vars and members of an object. Since SendMessage is synchronous, the StatusOut calls are not returning until the other side has dispatched the data that was sent, right? I thought this process was fairly robust, but maybe I need to set up CodeSite instead. Any thoughts on this? During normal operation, the existing code fires off a Windows app as a child process. It runs for quite a while. It performs OCR on one-page PDFs. It's not interesting to watch, and all anybody does is open the VNC window to see that it's running and then they close it. I don't think anybody even looks at the log. They pretty much ignore it unless the client complains, which hasn't happened in ages from what I'm told. (I don't know why, because I discovered it has been broken so it's not attaching the PDFs to proper db records since around March 1st of this year and nobody knew.) I've replaced the Windows app with an OCR library that runs as a DLL, and it does the work in two seconds per page. A process that takes 8 hours with the app takes about 16 minutes with the DLL. The core part of this thing works as-is, except for one piece that's saving data into a zip file. It seems the more StatusOut calls I make to see what's going on, the more weird exceptions I get. Again, it's behaving more like a box containing Shröedinger's bug than a nice friendly Delphi app.
  4. David Schwartz

    Threading question

    Changing to PostMessage didn't help either. The messages all showed up as some kind of Asian pictographs and other junk. The class being threaded is derived from TThread, if that helps with anything.
  5. David Schwartz

    Threading question

    @Larry Hengen There are only 4 controls on the main form: a checkbox, two buttons, and a TMemo. The memo gets very little traffic from these StatusOut messages, at least it did until I started adding stuff. I don't know why the Application.ProcessMessages is there other than someone thought it was a Good Idea. I'll remove it and see if your theory is correct. UPDATE: Nope, it didn't help. Now I'll try changing SendMessage to PostMessage.
  6. David Schwartz

    looking for a lo-fi Delphi Style

    As I said, I can usually build a semi-functional prototype / mockup / whatever in the same time it takes a graphic artist to build a static wireframe model. I showed a prototype to some potential investors last year trying to raise some capital and I think they thought I was lying to them about how much was completed. So I showed the next ones screenshots, and they wanted to see that I was further along, more than just drawings. These guys are presented with wireframes and mockups all the time, and Delphi lets you build something that's functional, but it looks too complete for a prototype. It's hard to explain to people. I only wish there was a "Project Overview" tab on the IDE that shows the various forms and units in the project in a graphical fashion, rather than just the textual list in the Project Mgr.
  7. David Schwartz

    Difference between Pred and -1

    off-by-one errors are the bane of most programmers' existence. I eventually learned to look for "... 0 to ... -1" or "... 1 to ... <x>" where "x" does not contain a '-1' The use of pred() in place of '-1' is inconsistent at best, and rarely used, so it much harder to detect quickly when it's not used appropriately. Wirth threw it into the language as a pedagogical tool. That was nice, but it's impractical. The 'for ... in' approach is far less likely to lead to such errors. The use of 'Low(x) to High(x)' is even less error-prone than '0 to ...-1' IMO.
  8. I can't tell whether you're trying to solve a programming problem, or if you're just doing mental calisthenics trying to use different programming constructs in strange ways. I realize some people love using records and pointers to them in Pascal. But I spent 10+ years using c and c++ before getting into Delphi and I have always gotten confused about which side of the variable the ^ goes on and why you need to use it with a '.' because in c/c++ the '->' always goes on the right, and you never use any two of '.', '&', '*', and '->' next to each other. Maybe it's the same way some people have trouble grasping the difference between how ++i and i++ work in a for loop. Suffice it to say I bet you've used pointers in Delphi more in the past month than I've used them in the past 10 years. That class DOES show some effective uses of inheritance tho.
  9. One of the biggest challenges people new to OOP run into is they don't really grasp that inheritance and composition are two equally useful tools, except they solve different problems. People tend to over-do the use of inheritance at first. I guess it just seems cool. Most of the time, all you need is, say, three classes instead of one, and then you compose objects from those classes into something else. As opposed to some contorted mess created from a strange hierarchy of inherited classes that you think might do the same job. I like to think of inheritance as just another form of Dependency Injection -- one that injects things into a class via ancestry rather than constructor, property, or method injection. You can inject behavior (via methods in the base class) as well as state (via common state variables in the base class). If you only need to inject patterns of behavior (like frameworks with no default behavior) but no state, then use Interfaces. Sometimes it can be rather fuzzy which way to go. I think the most common class I spend too much time deliberating over is the lowly TStringList. Sometimes I'll need something a little off the grid and add functions to a stringgrid. Then I'll put them all into their own class (encapsulation). Then I'll scratch my head and wonder if the stringlist should be a member of the class (composition) or if I should actually inherit from it (inheritance). It's not always obvious, and sometimes it's even only worth a coin-toss. Ditto when it comes to any kind of container -- do I inherit from a TList<something> or do I compose the TList inside of the class and reflect the methods I need and skip the rest? Either way, it's necessary to create methods to reflect the things I want to do that aren't already there. A common example is adding a new widget to a list of widgets. Inheritance lets you use an inherited Add method for free, while composition requires you to implement one yourself. But sooner or later you're going to think ... why not add a AddNewWidget( ... ) function that returns a TWidget to the container class so I don't have to create a new one first then add it separately. Oh, then I can throw in some parameters that I'll usually initialize it with, sort of like a factory pattern. That AddNewWidget function must be created yourself whether you use inheritance or composition to manage the list. And there are others as well. So it's not as simple as just looking at the built-in methods. (Yes, people get obsessed wtih not having to create reflection methods like Add, Delete, and a search function when they use composition rather than inheritance.) But over time, I'd have to say I don't spend time going around looking for ways to use inheritance. That's like carrying around an umbrella on a sunny day looking for rain. Rather, I wait until I run into a situation where I've got two or three classes that CLEARLY share some common behavior and/or states, and then I'll pull the common parts into a base class that I'll then inherit from. If you're trying to figure out how to accomplish some kind of inheritance, then I'd say you're not being driven by the nature of the problem, but mostly by your desire to fit a large square peg into a small round hole. The question is not, "How do you do this?" but "Why are you even trying?" The "how" part should really should be obvious, IMHO.
  10. David Schwartz

    Read in multiple lines and display them in a TMemo object

    Just for fun, check out the use of INI files for this.
  11. check out BigINI from this guy, the latest (v5) is most interesting. http://www.hinzen.de/delphi/index.html No diffs, but it extends basic INI file managment quite nicely. I've used it for years. (And added several new things.)
  12. This does not smell right to me. You're using inheritance to model composition, IMO. Your base object is a file. You don't really need to make a class around just a file. If you want to operate on two files, you don't do that by inheritance -- that's composition. You'd want a function that takes two files and compares them and returns some kind of result. You don't need a class for this either. Another function could take one input file and the diff data and regenerate the second file. In a typical Linux command shell, you'd use: diff -e file1 file2 >diff_f1_f2 You also have directionality, so you could go the other way: diff -e file2 file 1, >diff_f2_f1 These output files are called "deltas" and one is a "forward delta" while the other is a "reverse delta". The "diff -e" command gives you an output file that consists of a series of edit commands (for 'ed') that you can feed into it with the first file to get at the second file. ed -f diff_f1_f2 file1 > file2_again I'm hard-pressed to think how I'd build classes around these commands. You could have a file mirroring scheme that uses a LocalStore and a RemoteStore. Each store would keep reverse deltas, but the LocalStore would generate forward deltas to send to the RemoteStore to reflect the latest changes to a file without having to send the entire file. This makes sense -- this scheme is maintaining state by keeping the RemoteStore an accurate reflection of the LocalStore while minimizing transmission bandwidth. If all you're trying to do is visualize things, that's a little different, but you could think of the output as commands to colorize the text rather than edit it. You'd go one direction then the other and colorize each file based on changes needed to get one side from the other. But you're still not maintaining any kind of state, since the files are static and everything can be derived again at any time from the same input files. The FILES themselves represent the "current state" of their relationship that you're looking to visualize. The 3-way compare simply uses three files and does three (or six) different comparisons. A class implements behavior and maintains state. There's no state that needs to be maintained here -- just some functions that take input files and generate output files. The same output is produced for the same input, every time. One could argue that the functions implement behavior -- yes, but they don't change the state of the underlying data. So it's just a container of convenience. This seems like a rather far reach to come up with a class design when it's of no obvious benefit.
  13. I'm using an OCR library on some text and I'm getting a lot of "noise" characters. There's some demo code that looks like this: For C:=0 To OCRResultsHeader.NumItems-1 Do Begin S:=S+Chr(OCRResultItems[C].OCRChar); If OCRResultItems[C].OCRChar=13 Then S:=S+#10; AMsg(' OCRCha'#9 +IntToStr (OCRResultItems[C].OCRChar ), False); AMsg(' Confidence'#9+FloatToStr(OCRResultItems[C].Confidence), False); End; MemoResult.Lines.Add(S); OCRChar is the character at position C in the converted results (OCRResultItems), and Confidence is a Float in the range [0..1) -- ie, it's always <1.0. I want to get a sense of what the Confidence is for individual characters, so I was thinking of taking n := Integer(Confidence * 10) to get a number in the range [0..9]. Then I'd map that to colors, like: [light-red, med-red, dark-red, light-yellow, med-yellow, dark-yellow, light-green, med-green, dark-green, white] Then I'd display each letter in a RichEdit and set its background color ("highlight") to a red/yellow/green/white color based on 'n'. Unfortunately, I can't tell this library that in certain places I'm only looking for numbers. So for example, it often spits out things that look like a '1' (vertical lines) that aren't. Same for zeros ('0'). A lot of noise characters show up as vertical-bar | , periods, and underscores. I'm thinking that if they all show up with red backgrounds, then I could filter them out by their confidence values rather than text matching against similarly-shaped characters (homographs?) Here's my code that sets various boundaries on the width and number of lines, plus it splits lines up in the stringlist. I'm not using the Confidence value here yet. S := ''; for n := 0 To OCRResultsHeader.NumItems-1 do begin ch := OCRResultItems[n].OCRChar; conf := OCRResultItems[n].Confidence; if (ch = 13) then begin if (Length(S) > 4) then // nothing we want is <= 4 chars in length scanned_rslts_sl.AddObject( S ); if (scanned_rslts_sl.Count > 9) then // it's going to be in the first 9 lines... Break; S := ''; end; if (Length(S) > 20) then // we're looking for an 8-digit number, not a novella Continue; if (ch >= 20) then // ignore control chars (20 is a space char) S := S + Chr(ch); end; One thing I was thinking was to allocate an array of bytes the same length as each line and attach it to the stringlist via AddObject. (I'm not exactly sure how to allocate an array of bytes from the heap for each line to attach as the Objects[ ] value corresponding to each character, tho. Does it need to be an actual "object"? Or can it just be a pointer to an array of bytes?) Anyway, I'm curious how to add the array of byte values for 'n' for each character to the Objects array, and also if anybody has a more obvious or interesting approach.
  14. David Schwartz

    Minifing HTML

    Aside from the tags that cannot be replaced, and the text that you want displayed, I'm not sure what you can remove. It's not like javascript where you can change all of the variable names to 2- or 3-letter codes and remove most of the white space. What do you imagine can be compressed out?
  15. I think the problem you're going to run into is in how the RTTI support changed massively in D2010. Legacy apps built before that aren't going to care. But most libraries since then have come to depend heavily on it for a variety of things, especially when it comes to offering services of many kinds. There's stuff that simply cannot be done in D7 or even D2007 that can be done with the new RTTI support in D2010 and fwd. A lot of these have to do with design-time support as well. So you can sometimes find libs that have run-time support back that far, but not much design-time support. A straightforward REST/JSON API shouldn't be terribly difficult to write, unless you want to be able to send objects over the wire and have them reconstituted at the other end. Short of that, you might find something that works for you.
  16. I'm trying to load up a project that uses Konopka Components from a Tokyo project into Rio, and I can't find this library in Rio's GetIt. (Rio 10.3.3) I see people complaining that it was missing in the initial release, but ... is it still MIA?
  17. David Schwartz

    where to find Konopka Components for Rio?

    Sheesh ... for anybody else looking for this ... they renamed it to "Bonus KSVC" within GetIt.
  18. David Schwartz

    'stdcall' for 32-bit vs. 64-bit?

    for DLL importing, there are lots of {$IFDEF WIN32} stdcall {$ENDIF} constructs I'm seeing. I'm thinking this is for distinguishing between 16-bit and 32-bit library calls. What about 64-bit? Do you need 'stdcall'? Or something else? Or nothing?
  19. David Schwartz

    'stdcall' for 32-bit vs. 64-bit?

    Thanks, but in this case, I'm only dealing with Windows and two versions of the same DLL -- a 32-bit and a 64-bit version.
  20. In my mind, "initializing the car" is mostly focused on getting the engine started and ready to drive. There are all sorts of pre-checks that could be performed, like pilots do on aircraft: tire pressure, chalk-blocks on tires, garage door, main gate, parking brake, fluid levels and brakes, lights, windows, A/C or heat, defroster, etc. So to a certain extent, the analogy breaks down. Personally, I'd say the "base class" initialization is just what's going on "under the hood" and everything else delegated to a derived class, or the driver, depending on how much of it is automated. You say po-TAY-to and I say po-TAA-to. It's not worth arguing over. 🙂
  21. David Schwartz

    'stdcall' for 32-bit vs. 64-bit?

    I ran Dr. Bob's HeadConv to convert several .h files for a DLL to Delphi, and it sprinkles these IFDEFS into all of the function calls. They're needed for older 16-bit stuff, but I haven't run into any in years. It's mainly that I wasn't sure about 64-bit needs. Glad they're no longer of any use in 64-bit mode.
  22. perhaps it's a poor choice of words. Usually one would construct an object, and when it's fully constructed, it can be used. Values can be injected via the ctor's Create parameter list. Sometimes this isn't possible for various reasons. In that case, Create would build as much as it can, then you'd use property or method injection to initialize subsequent values, then proceed to work with the object. The benefit of injecting values in the Create parameter list is that once the Create finishes, you're supposed to have confidence that it has been properly constructed if it doesn't throw an exception. When you use property or method injection to perform further required aspects of initializing the object, that's not necessarily the case, as there would be one or more methods in the class that would not work properly if these further initialization operations are not done first. This is distinct from simply setting state variables in the object to alter its run-time operation. Here's an example that could go either way, but take it in the spirit it's being offered... When you start your car (ie, initialize it), it's ready to go. You can put it in gear and just drive off. But there's a step that most older cars require that newer cars don't, which is "check need for headlights". If it's dark outside, you can drive the car away without turning on your headlights, but you risk running into something, or getting a ticket. Said another way, there's a high potential for a run-time exception to occur if the lights aren't properly initialized. So there's a secondary initialization needed where the driver needs to make an observation and then turn the headlights on before driving away if it's sufficiently dark outside. Newer cars have put sensors in the vehicle that automatically turn on the headlights when the ambient lighting drops below some predefined level. So this way, no further action is required -- you can indeed get into the car, start it, and then drive off without having to explicitly check the headlights since it's now part of the "core" initialization functions. The headlight setting in this case will not prevent the car from driving, but it can lead to a harmful situation if ignored. (In my car, I have to depress the brake pedal in order to initialize (start) the car, so it's an implicit part of the initialization sequence.) Another one is if you're low on fuel. The car won't start if you're out of gas, but if you're merely "low" it will start and run for a while, then die. If you know you need to drive 100 miles, but there's no enough gas to go that far, then you're going to get a run-time exception if you don't increase the fuel level. Not much different than running out of memory after awhile. It's left as an exercise for the driver to check the fuel level directly and add gas when needed. In the 1980's, cars had a "nag" feature built-in that would tell you things like "fasten your seat belt," "check gas level," or "door is ajar". They lasted longer than anybody imagined, but this feature was thoroughly despised by most drivers. This was a "warning" rather than an "error", so the car would drive after being started, but the "nagging" announcements were intended to alert the driver to things they presumably weren't aware of. An "out of gas" state is the only one I can think of when the car won't start (initialize). The transmission setting is distinctly NOT part of the initialization sequence, because it's something that changes the state of the vehicle during normal run-time operations.
  23. The purpose of a class constructor is to initialize the class to a known initial state when it's created. Imagine having to different ways to start your car if you were alone or if you had passengers, just so the system that supports airbags knows whether to only activate the Driver's-side airbag or all of them. Airbag activation based on secondary properties that can't be known until run-time (ie, how many people get into the car) is not part of the object's "initial state". FIRST you start the car. THEN you do other things like activate the airbags. You do NOT want to have different ways of starting the car based on factors that depend upon secondary systems. It doesn't need to be airbags -- it could be A/C vs. Heater; driving in the day vs. at night where lights are required; etc. Creating your objects is like starting the car and getting the engine running. C++ and C# (among others) have a way to inject initial state data into constructors at the time of declaration vs. run-time. Delphi doesn't have that ability, so you need to inject the really core initial parameters in the Create call, and then add others AFTER the object has been initialized via property injection.
  24. David Schwartz

    language updates in 10.4?

    As if that has never happened with anything Microsoft ever published ...
  25. David Schwartz

    language updates in 10.4?

    I don't care about nullables so much as a more efficient way of writing code that tests for a NIL value and stops rather than raising an exception, so you don't need to have if Assigned( xyx ) then all over the place to prevent exceptions. Nullables seem like a back-door way to get support for something that would save quite a bit of time and coding that isn't going to be added to the language just because it's really useful. There are so many idiomatic phrases we use habitually simply because the language never supported ways to avoid them originally; other languages are finally being enhanced to address things like this, but Delphi remains stuck in the Dark Ages when it comes to useful language enhancements. Like the use of strings on case statements that practically every language in use today supports, just not Delphi. Compilers are supposed to make the work of programmers EASIER. I don't subscribe to notions that "we don't do that because it's not in the 'spirit' of Pascal" or whatever. That's just BS. It's like saying you'll never put an electric starter on your cars because it's just not historically accurate with original car designs. So you have to get out and crank a lever in the front of the car to get the engine started. I don't know about anybody else, but I'm sick and tired of hearing off-the-cuff comments from managers and VPs who make technology usage decisions saying that Delphi old and stale so they can't wait to replace their code with "a more modern language". At the place I started working at in mid-January, the CTO cornered me one day after I'd been here about 3 weeks and said, "So how much work do you think it would take to replace all of the Delphi and MySQL with C# and SQL Server?" Why is this such a pervasive and recurring question everywhere I've worked for a decade? There may be something heart-warming about the fact that Delphi can compile code written in 1995, but it's keeping management teams at corporations of all sizes from embracing Delphi because there are plenty of language enhancements in C, C++, C#, Python, Ruby ... you name it, that are not in Delphi, and from all indications will never be. That gives the clear impression that Delphi is old, stale, stodgy, and is something nobody wants to use because it has no support fo the latest language features that other languages have. For 12 years now, I've worked at one place after another where my job was to keep a bunch of legacy Delphi code alive until they can replace it with "something more modern". That's THEIR words, not mine! And I have absolutely no defense for it. They use the Pro version of Delphi, not Ent or Arch, because they don't use any of the other stuff bundled with the bigger packages. Just the language and a few 3rd-party component libs (mostly free stuff). There are other tools that implement Pascal variants that are almost totally compatible with Delphi, and add plenty of new language features that are on-par with contemporary languages. These prove it can be done elegantly and cleanly. But the compiler builders need the motivation to do it. If the folks who own Delphi ever hope to get it mainstream again, they need to bring the language into the 21st century and add features that most other contemporary languages have had for a while now. But when the topic comes up, all we ever hear is crickets. "Oh, but look at all the work we've done with our latest Interbase enhancements!" There's lots and lots of work being done enhancing stuff that hardly anybody uses. But enhancements to the core product are nowhere to be found.
×