Coding in Delphi and Beyond - Thoughts from David Cornelius https://corneliusconcepts.tech/ en A New Direction for Web Development https://corneliusconcepts.tech/new-direction-web-development <span property="dc:title">A New Direction for Web Development</span> <div class="node-taxonomy-container"> <h3 class="term-title"><i class="icon-hashtag theme-color"></i> Category</h3> <ul class="taxonomy-terms"> <li class="taxonomy-term"><a href="/cloud" hreflang="en">Cloud Computing</a></li> </ul> </div> <!--/.node-taxonomy-container --> <span rel="sioc:has_creator"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span></span> <span property="dc:date dc:created" content="2023-01-01T03:02:41+00:00" datatype="xsd:dateTime">Sat, 12/31/2022 - 19:02</span> <div property="content:encoded" class="field field--name-body field--type-text-with-summary field--label-hidden field-item"><p>Back in May, I lamented about my <a href="/major-drupal-upgrade-woes" rel="nofollow">struggles to keep my Drupal websites updated</a>. If I had command-line access to the web server, I could use its recommended method, <a href="https://getcomposer.org" rel="nofollow">Composer</a>, and a cron job could magically keep everything updated for me--but I use inexpensive shared web hosting and that is not an option. This means I must manually upload a bunch of PHP files for each Drupal update. Version 7 wasn't that bad at only 12 MB and just over a 1,000 files but Drupal 8's footprint jumped up to 73 MB and over 18,000 files--and it increased further with Drupal 9 (I haven't checked the size of Drupal 10 which was released this month). The worst part is that all the files are date-stamped with the date the version came out so I can't simply upload only the new and changed files--they ALL look new! I'd just deal with it if it was an update to one or two sites every other month, but I maintain eight separate sites and there are often multiple security updates each month! Yes, I've reached my breaking point with maintaining Drupal sites on shared web hosting--it's time for something simpler, especially since most of my sites have infrequent changes and don't really need the power of a dynamic scripting language tied to an online database (it seems like I went from simple HTML in the 1990s to dynamic sites over the last 20 years and now am wanting to go back to simple HTML--what's old is new again!).</p> <p>A comment on last May's blog opened my eyes to a different approach: use a static website generator. Since I'm a big fan of Delphi and know it's fully capable of generating web sites in a variety of ways, my first thought was to write my own. But a little bit of research revealed there are dozens (hundreds even?) of website generators, in nearly every programming language, and most are open source--so why reinvent the wheel? It would take far less time to study and put to use the efforts of those who have gone down this path already than it would for me to design and write one myself. Besides not EVERYTHING has to be done in Delphi!</p> <p>As I read reviews and tried a few of them out, I kept coming back to the commenter's suggestion: <a href="https://gohugo.io" rel="nofollow">Hugo</a>. Whatever tool I was going to use had be well-supported, have lots of pre-built themes, good community support and documentation, and (hopefully) be able to import the content from my existing Drupal sites--Hugo is one of the few where I found direct support for <a href="https://github.com/search?q=hugo+drupal" rel="nofollow">converting Drupal sites</a>. The more I looked at Hugo, the more I realized I would likely need to know the programming language Hugo is written in: <strong>Go</strong>.</p> <p>So, I paused my research on website generators and took a deep look at <a href="https://golang.google.cn" rel="nofollow">Go</a>, or Golang as some refer to it. Many of today's modern languages, especially ones used in conjunction with building or running websites, are scripted: PHP, JavaScript, and Ruby just to mention three popular ones.  Go, however, is compiled--that was one surprising and positive aspect that that struck me. It's also a relatively new language (less than 15 years old at this point), designed by the engineers at Google, has security and concurrency built in, is available on nearly every platform, forces clean and simple coding, is <a href="https://github.com/gohugoio/hugo" rel="nofollow">open source</a>, and is well-supported with a growing user base and well-written documentation.</p> <p>My goal for the new year is to learn this new programming language and jump whole-heartedly into working with Hugo to convert several of the Drupal sites I maintain. Uploading one or two changed HTML files when there's a content update and not worrying about constantly updating Drupal or fighting with PHP version incompatibilities will take significantly less time and worry! That sounds like a positive direction for 2023!</p></div> <section id="node-comment"> <article data-comment-user-id="0" id="comment-529" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/0"> </article> <mark class="hidden" data-comment-timestamp="1672758654"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/529#comment-529" class="permalink" rel="bookmark" hreflang="en">Try a different web hosting provider?</a></h3> <div class="single-comment-meta"> <span><span lang="" typeof="schema:Person" property="schema:name" datatype="">John (not verified)</span> Tue, 01/03/2023 - 04:48</span> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>Hi David,</p> <p>What hosting provider are you using? I use interserver and I have direct access to the terminal and can run commands. Plus, they are very affordable and with great customer support.</p> <p>Things have changed dramatically in shared hosting. I would also expect the cpanel (or equivalent) in your current hosting to recognise the Drupal installations and offer to update them automatically when a new version arrives. </p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=529&amp;1=default&amp;2=en&amp;3=" token="J46rgvlw9Sftu1LA4PGxSN5uqHWyKrIh2O54P2oPYLM"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> <div class="indented"> <article data-comment-user-id="1" id="comment-531" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/1"> </article> <mark class="hidden" data-comment-timestamp="1672759730"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/531#comment-531" class="permalink" rel="bookmark" hreflang="en">It&#039;s an excuse to learn a new language!</a></h3> <div class="single-comment-meta"> <span><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span> Tue, 01/03/2023 - 07:28</span> <p class="visually-hidden">In reply to <a href="/comment/529#comment-529" class="permalink" rel="bookmark" hreflang="en">Try a different web hosting provider?</a> by <span lang="" typeof="schema:Person" property="schema:name" datatype="">John (not verified)</span></p> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>My sites are on <a href="https://ionblade.com/" rel="nofollow">ionBlade</a> which has an option to install Drupal on a new site but I've installed it manually by uploading files and running the install.php script. Perhaps I should've used their installer but there's a lot about that process I don't know and wanted more direct control. If I upgraded to a more expensive tier, I'm sure shell access would be available</p> <p>I used InterServer many years ago. I forget why I left but I'm in the middle of a 3-year contract with ionBlade and except for this one restriction (which I've also experienced with another web host), I'm happy with them.</p> <p>There are other aspects of moving to Hugo I'm looking forward to: simpler content management of static sites, being free of server software dependencies, and an excuse to learn a new programming language. ;-)</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=531&amp;1=default&amp;2=en&amp;3=" token="xVTNhN6jtROQLif3UvFflzMqCBSYmO6yDft04hE9OWU"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> </div> <article data-comment-user-id="0" id="comment-532" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/0"> </article> <mark class="hidden" data-comment-timestamp="1673590530"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/532#comment-532" class="permalink" rel="bookmark" hreflang="en">RemObjects Gold</a></h3> <div class="single-comment-meta"> <span><a rel="nofollow" href="https://www.emoconsult.nl" lang="" typeof="schema:Person" property="schema:name" datatype="">Erwin Mouthaan (not verified)</a> Thu, 01/12/2023 - 05:26</span> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>"Besides not EVERYTHING has to be done in Delphi!"</p> <p>Almost everything. With RemObjects Gold you can mix Go with Oxygene within same project. Even third-party Go libraries can be used with Oxygene language. You still have an excuse.</p> <p>https://www.remobjects.com/elements/gold/</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=532&amp;1=default&amp;2=en&amp;3=" token="3zQ04VPk78JDx5wW1mEZzzJCERgegdVQakZWUtTh2fA"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> <div class="comment-form-wrap"> <h2 class="add-comment-title"><i class="icon-add_comment theme-color"></i> Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=170&amp;2=comment_node_blog&amp;3=comment_node_blog" token="rMtaw-_orhY7pW_upeQvQH7HWVMHR9h-OMKWZg6LezQ"></drupal-render-placeholder> </div> <!--/.comment-form --> </section> Sun, 01 Jan 2023 03:02:41 +0000 david 170 at https://corneliusconcepts.tech For Historical Purposes https://corneliusconcepts.tech/historical-purposes <span property="dc:title">For Historical Purposes</span> <div class="node-taxonomy-container"> <h3 class="term-title"><i class="icon-hashtag theme-color"></i> Category</h3> <ul class="taxonomy-terms"> <li class="taxonomy-term"><a href="/programming" hreflang="en">Programming</a></li> </ul> </div> <!--/.node-taxonomy-container --> <span rel="sioc:has_creator"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span></span> <span property="dc:date dc:created" content="2022-11-18T19:21:38+00:00" datatype="xsd:dateTime">Fri, 11/18/2022 - 11:21</span> <div property="content:encoded" class="field field--name-body field--type-text-with-summary field--label-hidden field-item"><p>Early in my career, I was studying the code of an application written for the Apple II in preparation for developing something similar on the PC and would often ask questions of the original programmer. Most of the time, I'd get valuable information about the purpose of a routine or why something was done a particular way. But every once in a while, when pressed for an explanation, the programmer would think for a minute, then simply utter, "For Historical Purposes" and walk away chuckling.</p> <p>I was somewhat frustrated because I wanted to know the "WHY" of everything--there had to be a good reason for coding things a certain way, right?</p> <p>Well, it turns out, I've had to use that very phrase many times since those early days. Sometimes there are good reasons why code turns out the way it does--sometimes not so good. It could be that new requirements are coming so fast, you don't have time to rewrite a routine to fit multiple scenarios so you have to duplicate the effort to fit each instance. Working on legacy code where there have been several developers touching various parts over the years, there are bound to be questions about the code that no one can answer--sometimes because not all programmers play well together and have very different ideas on how things should be done, other times, a new developer may not know about how something was solved previously and do it again--differently.</p> <p>I was recently working on a legacy Delphi 5 project and needed to implement some checks in a data entry screen. There were several parts of the application that used this type of data and so the same check needed to be implemented in multiple places.  Once I figured out how to use existing data fields in the first screen, I figured it would be a simple copy-paste to the other modules. But no, in the second screen I worked on, instead of a dataset ready to use, a function went out and grabbed all the necessary information and stored it in a class. That's a better approach but now instead of simply accessing some readily available data fields, I need to go modify the class, then modify the method that fills the class before I can use it.</p> <p>The important thing is to be flexible and observant and figure out how things ought to be done but also keep a keen eye for reusing whatever constructs are already available to prevent re-inventing the wheel. Sometimes that involves searching with the right keywords, sometimes that asking the right person.</p> <p>And if the answer is vague and involves some hand-waving with references to a distant pass, just smile and get back to work!</p></div> <section id="node-comment"> <div class="comment-form-wrap"> <h2 class="add-comment-title"><i class="icon-add_comment theme-color"></i> Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=169&amp;2=comment_node_blog&amp;3=comment_node_blog" token="Oy1A4IKmSmhHDx5Rx0k3EvtRYFx6hc99PqbzoE_ZVlE"></drupal-render-placeholder> </div> <!--/.comment-form --> </section> Fri, 18 Nov 2022 19:21:38 +0000 david 169 at https://corneliusconcepts.tech Thankful for Delphi https://corneliusconcepts.tech/thankful-delphi <span property="dc:title">Thankful for Delphi</span> <div class="node-taxonomy-container"> <h3 class="term-title"><i class="icon-hashtag theme-color"></i> Category</h3> <ul class="taxonomy-terms"> <li class="taxonomy-term"><a href="/programming" hreflang="en">Programming</a></li> </ul> </div> <!--/.node-taxonomy-container --> <span rel="sioc:has_creator"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span></span> <span property="dc:date dc:created" content="2022-11-16T16:15:01+00:00" datatype="xsd:dateTime">Wed, 11/16/2022 - 08:15</span> <div property="content:encoded" class="field field--name-body field--type-text-with-summary field--label-hidden field-item"><p>This time of year in the United States is marked by a major holiday, Thanksgiving, the last Thursday of November. It is during this time that ad campaigns, religious organizations, and families everywhere tend to step up their recognition of everything they're thankful for. As a software developer, I'd like to hook into this theme and highlight features of programming tools I use that make my life better--most notably, Delphi--and create my own "thankfulness" list.</p> <p>Part of the drive behind this is the fact I've been using Delphi 5 and the BDE a lot lately to support some legacy code. Fortunately, there are plans to majorly upgrade the code base and database connectivity to newer technologies but it has definitely reminded me of how much better the newer versions are--and how much everything has progressed in the last 22 years.</p> <p>But first, while I'm using Delphi 5, I'll point out a few things for which I am thankful:</p> <ul> <li><strong>Backwards compatibility</strong>. I guess you could say this is both a blessing and a curse: a blessing because this old compiler still produces Windows applications that run on the most recent versions of Windows but also that very fact means there hasn't been a hard reason to force an upgrade of the code base.</li> <li><strong>GExperts</strong>. One of the most popular of all Delphi IDE plugins, <a href="https://www.gexperts.org" rel="nofollow">GExperts</a> is an invaluable aid as it provides so many wonderful productivity benefits, I can't imagine programming in Delphi (especially Delphi 5) without it. With its Grep Search, Set Tab Order feature, Favorite Files list, Replace Components capability, Code Librarian, Multi-line Component Palette, and (my favorite) Code Proofreader, there are many valuable and time-saving programming aids--too numerous to mention here.</li> <li><strong>Castalia</strong>. Another great IDE plug-in, this one provides, among other things, stack-based bookmarks and <a href="https://docwiki.embarcadero.com/RADStudio/Alexandria/en/Code_Editor#Structural_Highlighting" rel="nofollow">structural highlighting</a> within your code. This was originally a commercial product by TwoDesk Software but was <a href="https://www.embarcadero.com/press-releases/embarcadero-acquires-castalia-and-usertility-from-twodesk-software" rel="nofollow">acquired by Embarcadero</a> in 2015 and subsequently integrated into the Delphi IDE over the next few versions.</li> <li><strong>Optional Parameters</strong>: This is a language feature that comes in handy so many times, like when you need to make a small change in a method for a particular case but you don't want to change the functionality for the many other cases for which it was originally written.  By adding an optional parameters and defaulting it to handle all the other cases, you can keep current functionality, don't have change gobs of code, and still handle your new situation.</li> <li><strong>Speed</strong>. Because there weren't as many features in Delphi 5 as there are in newer versions of the IDE, it loads really fast on today's modern computers--another ones of those pro-versus-con type of things.</li> </ul> <p>Speaking of newer versions of Delphi, I'm glad the product has continued to evolve and change. There are sometimes lags in features I've seen in other development tools but by and large, Delphi is quite feature-rich and highly productive. Here are the things I'm most thankful for (in no particular order):</p> <ul> <li><strong>Even more plug-ins</strong>: In addition to the aforementioned GExperts (still available in Delphi 11) and Castalia (features merged into the IDE) there are additional plug-ins. I've written about a couple of these before: <a href="https://corneliusconcepts.tech/my-favorite-new-delphi-103-rio-features" rel="nofollow">Bookmarks and Navigator</a>, originally by Parnassus. These provide additional bookmarking features and add a mini map inside the code editor.  When Delphi XE8 introduced a new Welcome Page, I <a href="https://corneliusconcepts.tech/make-new-welcome-screen-delphi-usable" rel="nofollow">complained but found a way to improve it</a>; then I was very grateful when <a href="https://www.danielwolf.eu/blog/2015/1668-meine-vorstellung-einer-willkommens-seite" rel="nofollow">Daniel Wolf wrote a replacement</a> which helped me manage the plethora of projects I was working on. Now, in Delphi 11, another interface has arisen but it's much more flexible and has an API--which has already been used for another <a href="https://dwp.gksoft.ch" rel="nofollow">multi-project management plug-in</a>.</li> <li><strong>Third-Party Components</strong>: There are many software vendors that support the Delphi language with libraries and components. I've used several of them over the years to add highly-functional grids, spell-checking, enable a plug-in system, and provide a richer experience for my users.</li> <li><strong>Backwards Compatibility</strong>: I mentioned this before but meant something different. Not only can old programs compiled with Delphi 5 run on new Windows operating systems, but old Delphi code can be upgraded with little effort (many times) to compile in newer versions of Delphi. There might be many things that need to be adjusted to take advantage of newer technologies (e.g. migrating from the BDE to FireDAC) but at least you can often get old code to compile without too much heartache--seldom do you need to completely rewrite something unless third-party components are no longer supported.</li> <li><strong>Attributes</strong>: At first, I didn't really understand the need for attributes but after I saw a few examples of where you could add functionality to a class without changing the class's purpose or making multiple derivations of the class, it really started making sense.</li> <li><strong>Generics</strong>: Another language feature added several years ago, this makes building complicated object-oriented applications much nicer in that you don't have to cast to-and-from a standard TObject.</li> <li><strong>REST Support</strong>: It's so nice to tap into a huge variety of public APIs with native libraries that come with Delphi. And by using the <a href="https://corneliusconcepts.tech/trestresponsedatasetadapter-and-hidden-gems-rest-debugger" rel="nofollow">REST Debugger and it's Copy Components feature</a>, you can create these applications very quickly.</li> <li><strong>Live Templates</strong>: There are many time-saving features in code editors but I think one of my favorites (after color-syntax highlighting, code-formatting, auto-indenting, and so forth that we come to expect in all editors these days) is the ability to type abbreviations and have them immediately expand to words or even context-aware code fragments that understand how to complete a class, add variable declaration, wrap a try-finally around an object, and so forth. I talked about my <a href="https://corneliusconcepts.tech/delphi-productivity-tips-live-templates" rel="nofollow">love of Live Templates</a> over a year ago.</li> <li><strong>Error Insight</strong>: I really like having the compiler check my code as I type and warn me of mistyped variable names or invalid method parameters; it saves time later when I compile and then find out my mistake. Read more about this and the related <a href="https://docwiki.embarcadero.com/RADStudio/Alexandria/en/Code_Insight_Reference#Error_Insight" rel="nofollow">Code Insight features on Embarcadero's Docwiki</a>.</li> <li><strong>Flexible IDE</strong>: I like the fact that the IDE's windows are dockable and resizable and that you can save layouts. Every programmer is different and even I use Delphi a little differently when I'm on a laptop than when I'm on my desktop computer with multiple monitors. In fact, I've even had specific layouts for certain projects!  And I <em>really</em> like the fact that you can have a Debug layout that it switches to automatically when debugging an application.</li> <li><strong>Speed</strong>: The Delphi compiler is still one of the fastest on the market. I like being able to hit F9 and see only the modified units compiled, linked, and the program start up so quickly.</li> <li><strong>Dark Mode</strong>: I was going to say this is a silly thing to mention but the white light from three bright monitors in front of me is a little hard on my eyes--I much prefer dark edit screens. While the code editor in Delphi has had a variety of color schemes available since the beginning (greatly enhanced by the open source project, <a href="https://github.com/RRUZ/delphi-ide-theme-editor" rel="nofollow">Delphi IDE Theme Editor</a>), I was pleased when the whole of the Delphi IDE, including menus, dialogs, edit boxes, the object inspector, and other windowed areas were enhanced with an overall consistent theme applied. This took place late in 2017 with Delphi 10.2 Tokyo's second release.</li> <li><strong>Native Compiler</strong>: Ever since the early days of Turbo Pascal, which created small .COM files, this line of Pascal compilers has created tight, native compiled code. Even after going cross-platform, the generated applications have been relatively free from runtime environments which are necessary for Java or web apps. Even .NET has the CLR and it's arguably better than the JRE (Java Runtime Environment) and can now be compiled natively (from what I understand). But us Delphi developers seldom run into "DLL Hell" problems or experience application crashes because some underlying framework ot updated (unless you had to use anything related to Internet Explorer); we've been relatively free from these types of pitfalls plaguing other tools.</li> <li><strong>Platform support</strong>: With XE2 in 2011 came 64-bit Windows support and soon after that MacOS, iOS, and Android. In the early days, we never would've guessed we would be able take our skills and move them to support other platforms. Sure we've had web server support for a long time but simply spitting out HTML from a console-based Windows app is a far cry from producing mobile apps that look and feel native from a single code-base.</li> <li><strong>Project Manager</strong>: I like being able to load multiple projects, save them together in a project group, and change options such as configuration or platform for all projects at once with the Project Manager toolbar buttons. This feature has come in handy when dealing with a program with several plug-ins that are managed as a single application suite.</li> <li><strong>Webinars, books, and forums</strong>: While Delphi isn't the most popular development tool on the planet, it definitely has enough of a following to provide plenty of resources for learning the language and discussing issues with fellow programmers. The <a href="https://en.delphipraxis.net" rel="nofollow">Delphi-PRAXiS</a> forum is the most popular place to ask questions--and get plenty of answers. And there's a website devoted just to the <a href="https://delphi-books.com" rel="nofollow">books available for Delphi</a>.</li> <li><strong>Free code!</strong> The community includes developers that give back as well. There are a plethora of GitHub repositories and collections of repositories including <a href="https://github.com/jimmckeeth" rel="nofollow">Jim McKeeth</a>, <a href="https://github.com/DelphiWorlds" rel="nofollow">DelphiWorlds</a>, <a href="https://github.com/danieleteti" rel="nofollow">Daniele Teti</a>, <a href="https://github.com/andrea-magni" rel="nofollow">Andrea Magni</a>, <a href="https://github.com/HeidiSQL" rel="nofollow">HeidiSQL</a>, <a href="https://github.com/skia4delphi" rel="nofollow">Skia4Delphi</a>, <a href="https://github.com/Fr0sT-Brutal/awesome-pascal" rel="nofollow">Awesome Pascal</a>, <a href="https://github.com/graphics32" rel="nofollow">Graphics32</a>, <a href="https://github.com/DelphiPackageManager" rel="nofollow">Delphi Package Manager</a>, <a href="https://github.com/Embarcadero" rel="nofollow">Embarcadero Technologies</a>, <a href="https://github.com/IndySockets" rel="nofollow">Internet Direct</a>, <a href="https://github.com/pleriche/FastMM4" rel="nofollow">FastMM4</a>, <a href="https://github.com/VSoftTechnologies/DUnitX" rel="nofollow">DUnitX</a>, and many more (including <a href="https://github.com/corneliusdavid" rel="nofollow">my own</a>).</li> </ul> <p>What about Delphi are you thankful for?</p></div> <section id="node-comment"> <article data-comment-user-id="0" id="comment-526" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/0"> </article> <mark class="hidden" data-comment-timestamp="1668696854"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/526#comment-526" class="permalink" rel="bookmark" hreflang="en">Backwards compatibility</a></h3> <div class="single-comment-meta"> <span><a rel="nofollow" href="https://blog.dummzeuch.de" lang="" typeof="schema:Person" property="schema:name" datatype="">Thomas Müller (not verified)</a> Wed, 11/16/2022 - 23:59</span> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>Compatibility of programs compiled with Delphi 5 to the latest Windows versions is not an accomplishment of Delphi but of Windows. Microsoft has put a lot of effort into making Windows backwards compatible to old 32 bit software even to the point of breaking^D^D^D^D^D^D^D^Dadapting the security concepts for some of them so they can even continue to write to the program files and Windows directory, which has not been allowed for normal programs since Windows 2000/XP 20 years ago.</p> <p>Having said that: Yes, the backwards compatibility of the Delphi IDE to older versions is always astonishing.</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=526&amp;1=default&amp;2=en&amp;3=" token="Irm7tWTm38OiIyD373J9Wtfg7S28oUHaRR7JH6qrZgk"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> <div class="indented"> <article data-comment-user-id="1" id="comment-527" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/1"> </article> <mark class="hidden" data-comment-timestamp="1668697310"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/527#comment-527" class="permalink" rel="bookmark" hreflang="en">True</a></h3> <div class="single-comment-meta"> <span><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span> Thu, 11/17/2022 - 07:01</span> <p class="visually-hidden">In reply to <a href="/comment/526#comment-526" class="permalink" rel="bookmark" hreflang="en">Backwards compatibility</a> by <a rel="nofollow" href="https://blog.dummzeuch.de" lang="" typeof="schema:Person" property="schema:name" datatype="">Thomas Müller (not verified)</a></p> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>Yes, you're right--I almost mentioned that. I do recall, however, working on an old Visual BASIC app many years ago that actually broke because it relied on some Windows DLLs that were upgraded. I suppose that could happen with Delphi apps as well but I recall VB was especially vulnerable and many programmers lamented its lack of backwards compatibility but that may have been more in code upgrades than old apps working on newer versions--it gets kinda murky after a decade or two.</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=527&amp;1=default&amp;2=en&amp;3=" token="ZyG_cwySmsr88T7Qzk8BNjNBxSAUlvT0mEs2E5Iv0co"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> <div class="indented"> <article data-comment-user-id="0" id="comment-528" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/0"> </article> <mark class="hidden" data-comment-timestamp="1668919071"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/528#comment-528" class="permalink" rel="bookmark" hreflang="en">Visual Basic relied a lot…</a></h3> <div class="single-comment-meta"> <span><a rel="nofollow" href="https://blog.dummzeuch.de" lang="" typeof="schema:Person" property="schema:name" datatype="">Thomas Müller (not verified)</a> Sat, 11/19/2022 - 02:14</span> <p class="visually-hidden">In reply to <a href="/comment/527#comment-527" class="permalink" rel="bookmark" hreflang="en">True</a> by <span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span></p> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>Visual Basic relied a lot more an binary extensions for components and functionality. While with Delphi, if you take/took care to always buy the source code with components you buy, you will always be able to move on to new IDE versions and - as long as Windows supports 32 bit applications - to new Windows version, in VB6 and earlier that was not possible. Not sure about nowadays, I never used VB.net.</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=528&amp;1=default&amp;2=en&amp;3=" token="soVMg_uh7cTDAMOVyyUrKqJMpymNf9DkTQ7vrjh4p98"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> </div></div> <div class="comment-form-wrap"> <h2 class="add-comment-title"><i class="icon-add_comment theme-color"></i> Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=168&amp;2=comment_node_blog&amp;3=comment_node_blog" token="7m6GdlnVmrum-DYpLSUxZflKwYFRAJKCX7IBB9NegyI"></drupal-render-placeholder> </div> <!--/.comment-form --> </section> Wed, 16 Nov 2022 16:15:01 +0000 david 168 at https://corneliusconcepts.tech Resisting Windows 11 https://corneliusconcepts.tech/resisting-windows-11 <span property="dc:title">Resisting Windows 11</span> <div class="node-taxonomy-container"> <h3 class="term-title"><i class="icon-hashtag theme-color"></i> Category</h3> <ul class="taxonomy-terms"> <li class="taxonomy-term"><a href="/windows" hreflang="en">Windows</a></li> </ul> </div> <!--/.node-taxonomy-container --> <span rel="sioc:has_creator"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span></span> <span property="dc:date dc:created" content="2022-10-16T20:26:12+00:00" datatype="xsd:dateTime">Sun, 10/16/2022 - 13:26</span> <div property="content:encoded" class="field field--name-body field--type-text-with-summary field--label-hidden field-item"><p>I like to keep all my computers upgraded with the latest versions of their respective operating systems. This goes for Mac and Android devices as well as all machines running Windows--even my virtual machines get the most recent upgrades whenever I use them. My development systems all use Windows but only one has been upgraded to Windows 11--the rest are still on Windows 10.</p> <p><img alt="Windows 10 Live Tiles" data-entity-type="file" data-entity-uuid="134b7fdd-4bb4-4e49-8968-f4b0daf2dcbf" src="/sites/default/files/inline-images/Win10Menu.png" width="450" class="align-right" />The reason isn't technical--the computers are compatible and I've used my Windows 11 laptop enough to feel confident it's plenty robust and ready for serious work--after all, Windows 11 has been out for over a year now. The main reason is actually kinda silly--but not uncommon: familiarity. People, by and large, are resistant to change and with change often comes a temporary reduction in productivity accompanied by a learning curve for doing things differently. I usually embrace new technology eagerly but the removal of Live Tiles in Windows 11 threw me a curve ball!</p> <p>The screenshot at the right shows some of the tiles I've placed and carefully arranged on my main development machine: Development IDEs and source control/analysis programs at the top, a section for databases underneath, and a long assortment of utilities and tools in the next column. I like to keep my desktop clean so don't want these icons there and while there are many I just type and select from the search list, there are some seldom-used tools that I want displayed in this list to remind me that I have them--and what they're called. So the Live Tiles list has become quite useful to me--not because I need information updated on the tile themselves but just so I have a list of icons to quickly see and access all the applications installed.</p> <p><strong>Windows 11 took that away.</strong></p> <p>We're now relegated to sets of 18 pinned apps with simple icons that can't be resized or grouped. You can't see all the icons at once and you can't even scroll slowly through them--you simply have a set of icons you can page through, 18 at a time. Of course, you can click the <em>All Apps</em> button and get a big list of everything that's been installed but that's not quick and concise like the curated list of tiles I had before.</p> <p>Yes, there are alternate solutions like <a href="https://apps.microsoft.com/store/detail/live-tiles-anywhere/9NR7QQK712PL?hl=en-us&amp;gl=us" rel="nofollow">Live Tiles Anywhere</a> that provide live tiles functionality on your desktop or pinned to your start menu or  task bar. There are also Start Menu replacements like <a href="https://www.stardock.com/products/start11/" rel="nofollow">Start11</a> or <a href="https://www.startallback.com/" rel="nofollow">StartAllBack</a>. I'll probably end up trying one of these to give me the list of tiled menu items I've come to like and depend on.</p> <p>For now, I'm dealing with three pages of icons on my laptop:</p> <p><img alt="Windows 11 Start Menu" data-entity-type="file" data-entity-uuid="10fe3538-2663-41f1-849d-7a6246af06c5" src="/sites/default/files/inline-images/Win11Menu.png" width="650" /></p> <p><strong>EDIT (2022-Oct-17):</strong></p> <p>I just discovered there's a Start menu Personalization option to show "More pins" in Windows 11. This adds one more row of pinned apps making the total available 24 instead of 18 as shown in this screenshot:</p> <p><img alt="Start menu with 24 pinned apps" data-entity-type="file" data-entity-uuid="9299edc7-7c75-4028-9faa-64e997f06e3a" src="/sites/default/files/inline-images/StartMorePinned.png" width="900" /></p></div> <section id="node-comment"> <div class="comment-form-wrap"> <h2 class="add-comment-title"><i class="icon-add_comment theme-color"></i> Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=167&amp;2=comment_node_blog&amp;3=comment_node_blog" token="Dc0iXMYoLJCLhV_4ei9LOPr5u0hXwMOtqzywAySeswk"></drupal-render-placeholder> </div> <!--/.comment-form --> </section> Sun, 16 Oct 2022 20:26:12 +0000 david 167 at https://corneliusconcepts.tech Multiple Installs of Firebird https://corneliusconcepts.tech/multiple-installs-firebird <span property="dc:title">Multiple Installs of Firebird</span> <div class="node-taxonomy-container"> <h3 class="term-title"><i class="icon-hashtag theme-color"></i> Category</h3> <ul class="taxonomy-terms"> <li class="taxonomy-term"><a href="/programming" hreflang="en">Programming</a></li> </ul> </div> <!--/.node-taxonomy-container --> <span rel="sioc:has_creator"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span></span> <span property="dc:date dc:created" content="2022-10-13T05:45:07+00:00" datatype="xsd:dateTime">Wed, 10/12/2022 - 22:45</span> <div property="content:encoded" class="field field--name-body field--type-text-with-summary field--label-hidden field-item"><p>As a Delphi developer, you might be working with the <a href="https://firebirdsql.org/">Firebird</a> database engine. It's quite popular in the Delphi community, is easy to configure and deploy, has low memory requirements, and is open source. There several versions and if you have multiple clients or applications, some legacy ones might be in maintenance mode and you might need to have multiple instances of them installed side-by-side. If you're like me, you might forget which versions are listening on which ports and so use a utility like <a href="http://www.nirsoft.net/utils/cports.html">CurrPorts</a> to remind you. But if you don't use a particular database service very often, it might not be started--or not even installed on the machine on which you're working. If you also have InterBase running, there might be conflicting or generic service names like "gdb" which doesn't tell you which engine it's for nor which version.</p> <p>This confusion can be reduced if you make a few configuration file changes and install the Firebird services yourself. Here's how.</p> <p>In the Firebird folder (e.g. <code>c:\Program Files\Firebird\Firebrid_3_0</code> for Firebird 3.0), the <code>firebird.conf</code> file contains a <strong>TCP Protocol Settings</strong> section that allows you to define the <code>RemoteServiceName</code> or <code>RemoteServicePort</code>. Uncomment one of the two settings--the other one will be set by the corresponding entry found in the <code>C:\Windows\System32\drivers\etc\services</code> file--if the entry exists. (Of course, to save changes to any files in the "Program Files" or "Windows" folders, you'll need to run your editor as Administrator.)</p> <p>It'd be nice to look at the Services list in Windows and know which port on which the various database engines are listening. To do that, I manually install the database service with a custom name. Firebird's <code>instsvc.exe</code> has the <code>-n</code> parameter that allows you to do this.</p> <p>When I finished, I got Firebird 2.17, Firebird 3.0, and Firebird 4.0 installed and listening on ports 3052, 3053, and 3054, respectively, by setting up the files as follows:</p> <ul> <li><code>C:\Windows\System32\drivers\etc\services</code>: added three entries: <ul> <li><code>firebird2 3052/tcp</code></li> <li><code>firebird3 3053/tcp</code></li> <li><code>firebird4 3054/tcp</code></li> </ul> </li> <li><code>c:\Program Files\Firebird\Firebird_2_1\firebird.conf</code>: uncommented <code>RemoteServicePort</code> and set it to <strong>3052</strong>.</li> <li><code>c:\Program Files\Firebird\Firebird_3_0\firebird.conf</code>: uncommented <code>RemoteServicePort</code> and set it to <strong>3053</strong>.</li> <li><code>c:\Program Files\Firebird\Firebird_4_0\firebird.conf</code>: uncommented <code>RemoteServicePort</code> and set it to <strong>3054</strong>.</li> </ul> <p>Then I ran the following to install the services:</p> <ul> <li><code>c:\Program Files\Firebird\Firebird_2_1\bin\instsvc.exe i -n FB2-Port3052</code></li> <li><code>c:\Program Files\Firebird\Firebird_3_0\instsvc.exe i -n FB3-Port3053</code></li> <li><code>c:\Program Files\Firebird\Firebird_4_0\instsvc.exe i -n FB4-Port3054</code></li> </ul> <p>Now when I look in the services list, I see all three Firebird services listed with their custom service name clearly indicating which version is running on which port--and none conflicting with the standard InterBase port on 3050.</p> <img alt="Firebird services showing in the Windows Services list" data-entity-type="file" data-entity-uuid="310d05be-0d96-4f0f-8176-05c61f408fca" src="/sites/default/files/inline-images/FirebirdServices3.png" class="align-center" /> <p> </p> </div> <section id="node-comment"> <div class="comment-form-wrap"> <h2 class="add-comment-title"><i class="icon-add_comment theme-color"></i> Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=166&amp;2=comment_node_blog&amp;3=comment_node_blog" token="Dpset2_lYKp1nfBA9HlRHGb3eT5PbInulOPnSf4-GoE"></drupal-render-placeholder> </div> <!--/.comment-form --> </section> Thu, 13 Oct 2022 05:45:07 +0000 david 166 at https://corneliusconcepts.tech Using the Vault API https://corneliusconcepts.tech/using-vault-api <span property="dc:title">Using the Vault API</span> <div class="node-taxonomy-container"> <h3 class="term-title"><i class="icon-hashtag theme-color"></i> Category</h3> <ul class="taxonomy-terms"> <li class="taxonomy-term"><a href="/programming" hreflang="en">Programming</a></li> </ul> </div> <!--/.node-taxonomy-container --> <span rel="sioc:has_creator"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span></span> <span property="dc:date dc:created" content="2022-10-07T05:35:11+00:00" datatype="xsd:dateTime">Thu, 10/06/2022 - 22:35</span> <div property="content:encoded" class="field field--name-body field--type-text-with-summary field--label-hidden field-item"><p>Nearly two years ago, <a href="https://www.ideracorp.com/pressreleases/acquires-apilayer">Idera acquired apilayer</a>, a collection of various cloud-based APIs. Idera being Embarcadero's parent company (and Embarcadero being the publisher of my favorite development tool, Delphi), I was interested to see what this was all about and looked at the handful of APIs available. One caught my eye and I made a note to come back and check it out more when the time was right.</p> <p>Recently, I started developing a couple of products that needed to store remote configurations. I briefly considered writing my own API or simply using FTP and storing config files on my website but there are several reasons why I didn't want to take either of those routes. Then I remembered this one API I had looked at previously--the time was right to look more deeply.</p> <h2>Vault API - Overview</h2> <p>The <a href="https://apilayer.com/marketplace/vault-api">Vault API</a> is actually quite simple: it stores encrypted data organized in a Unix-like folder structure of named content items. Every piece of data you submit is encrypted. You can use a default generated encryption key for the account or, if you have a paid account, use your own encryption keys. This is nice because even if someone somehow gets your API key, they cannot see any of the data you've stored unless they also know the encryption key used to store that piece of data--and every content item can be encrypted with a different encryption key.</p> <h2>My Needs</h2> <p>As I mentioned, I need to remotely store license keys and program configuration settings. Remotely storing license keys is a common need and there are APIs available specifically for that purpose. For example, <a href="https://keygen.sh">Keygen</a> has an elaborate licensing structure to handle a huge array of customer and product types, licensing schemes multiple versions, self-activation and validation, expiration, and so forth. It's really sophisticated but a bit of an overkill for what I need--and it gets expensive quickly.</p> <p>With the Vault API, I can structure my data and store anything I need in any string format: CSV, XML, JSON, or something else I create. This means I create my own license keys, attach expiration dates, product versions, and more, and store it in Vault. Then my app can get that data and validate it to activate the software. If need be, I can update parameters that might activate another part of the program, change an option, extend the expiration date, or deactivate the license the next time the program validates with the API--if I build the program to act that way.</p> <p>Most applications I distribute have a local screen or app that allows configuration of the program and stores a local .INI. Recently, I've been writing some automated database exports that run unattended for a small vendor that sells business information services nationally to store owners who often have very little knowledge of the database field names or internet protocols required to set up the proper queries and communications. The vendor wants to manage options for these customers remotely; their current approach is to remote in to the customer's server but that often requires a locally hired IT person to go on-site to let them in. It's awkward to relay instructions to the IT person as they aren't familiar with the configuration needs and the vendor sometimes needs to try different settings to fit particular scenarios.</p> <p>So my solution is to write a replacement program that will install a Windows service to store and periodically check the settings stored online in Vault; additionally, the vendor will get a copy of a remote configuration program that will access those settings. This way, the vendor can manipulate the configurations without anyone needing to manage the software at the store; the next time the Windows service checks Vault, it'll get the changed configuration and make the necessary changes to successfully export the data in the proper format.</p> <h2>The Vault API Endpoints</h2> <p>There are only three different URLs but with combinations of HTTP methods, there are a total of 7 actions:</p> <ul> <li><strong>Generate a new encryption key</strong><br /> <br /> End Point: <code>/key</code><br /> HTTP Method: <code>POST</code><br /> Parameters: None<br /> <br /> First and foremost in Vault is Encryption Key Generation. You cannot store any content in Vault without an encryption key. You can create content using your own encryption key if you have a paid account; if not, you must generate an encryption key which is immediately stored with your account and becomes the "default" encryption key for all content stored without a custom encryption key.<br /> <br /> Vault's default encryption keys are kept in a stack of "current" keys. If you call this end-point again, it generates a new key but remembers the previous key in case you delete the new one.<br /> <br /> Sample Request URL:<br /> <code>https://api.apilayer.com/vault/key</code><br /> <br /> Sample Response: <pre> { "key": "FNKerqcdZLeUFnrP6qbkSAZYeRfznqkb8t5O4ljgAIY=", "stored": true } </pre>  </li> <li><strong>Delete the encryption key</strong><br /> <br /> End Point: <code>/key</code><br /> HTTP Method: <code>DELETE</code><br /> Parameters: None<br /> <br /> You can delete the currently stored encryption key. When you do, the previously generated encryption key gets automatically reinstated as the default encryption key for the account. If there are no more encryption keys, then you are left with an account without a default encryption key.<br /> <br /> Note: Calling <code>POST /key</code>, then <code>DELETE /key</code>, then <code>POST /key</code> again does not regenerate the same key you had previously--in my testing, generated encryption keys are always new and unique.<br /> <br /> Sample Request URL:<br /> <code>https://api.apilayer.com/vault/key</code><br /> <br /> Sample Response: <pre> { "message": "Previous key was deleted, encryption key changed. Please note that items encrypted with the previous key can not be decrypted anymore.", "key": "F_DaK4S269ARR8HgYgCHExQt9xlXCfST30NfrD2glLE=" }</pre>  </li> <li><strong>Get the encryption key</strong><br /> <br /> End Point: <code>/key</code><br /> HTTP Method: <code>GET</code><br /> Parameters: None<br /> <br /> Calling this end-point simply returns the current default encryption key for the account. When you generate a new encryption key, the new key is returned for you to keep and store and use but in case you lose it or need to check which key is currently active, this end-point is useful.<br /> <br /> Sample Request URL:<br /> <code>https://api.apilayer.com/vault/key</code><br /> <br /> Sample Response: <pre> { "key": "F_DaK4S269ARR8HgYgCHExQt9xlXCfST30NfrD2glLE=" }</pre>  </li> <li><strong>Insert or update content</strong><br /> <br /> End Point: <code>/content</code><br /> HTTP Method: <code>POST</code><br /> Parameters: <code>body</code> (required), <code>path</code> (optional), <code>encryption_key</code> (optional)<br /> <br /> Posting content adds a new content item if one by that name and on the given path does not exist; if it does, it's overwritten. Every new and updated content replaces the entire data block each time--there is no support for changing part of it or only sending a patch of changes. The size of each named content item is unlimited.<br /> <br /> The <code>path</code> parameter is sent in the query string of the API call. If this parameter is given, the content is stored in the path specified. If the path didn't previously exist, it is automatically created. There is no end-point to manipulate paths--they only exist as part of the identification of stored content items. You can store an unlimited number of named content items in a path; any name conflict is assumed to be the same content item and overwrites the previous content by the same path/name.<br /> <br /> The <code>encryption_key</code> parameter is sent in the header strings of the API call. If there is a default encryption key associated with the account, you do not have to supply an encryption key--the data will be encrypted with the account's encryption key. If you have a paid account, you can provide your own encryption key and it will be used to encrypt the stored content regardless whether there is a default encryption key or not.<br /> <br /> The <code>body</code> parameter is sent in the body of the API call as the "POST" data. It is unstructured string data--completely managed by your application.<br /> <br /> Sample Request URL:<br /> <code>https://api.apilayer.com/vault/content?path=/ProductA/MyDataExporter</code><br /> <br /> Sample Response: <pre> { "message": "Content securely stored into the vault" }</pre>  </li> <li><strong>Delete Content</strong><br /> <br /> End Point: <code>/content</code><br /> HTTP Method: <code>DELETE</code><br /> Parameters: <code>path</code> (required)<br /> <br /> To delete stored content, use it's path and content name to identify it; if it was stored without a path, the path is on the "root" path, or '/'. The deletion occurs immediately and is non-recoverable.<br /> <br /> Sample Request URL:<br /> <code>https://api.apilayer.com/vault/content?path=/ProductA/MyDataExporter</code><br /> <br /> Sample Response: <pre> { "message": "Content deleted successfully" }</pre>  </li> <li> <p><strong>Get Content</strong><br /> <br /> End Point: <code>/content</code><br /> HTTP Method: <code>GET</code><br /> Parameters: <code>path</code> (required), <code>encryption_key</code> (optional)<br /> <br /> To retrieve content previously stored, call this end-point using it's path and content name to identify it. If it was stored with a custom encryption key, you must send that as well otherwise the content will be inaccessible. If you don't use custom encryption keys, the default account encryption key that was in effect when the content was created must still be active otherwise the content will be inaccessible until you send the encryption key that was used as a custom encryption key--content is NOT re-encrypted when a new account-level encryption key is created. If you don't use custom encryption keys and never change the account-level encryption key, you do not have to send an encryption key. Indeed, if you're using the free account, you can't use custom keys so sending one is pointless--just be sure to never change the default encryption key if there's still data you want to access.<br /> <br /> Sample Request URL:<br /> <code>https://api.apilayer.com/vault/content?path=/ProductA/MyDataExporter</code><br /> <br /> Sample Response:</p> <pre> { "data": "my test data" }</pre> </li> <li><strong>Browse for Content</strong><br /> <br /> End Point: <code>/browse</code><br /> HTTP Method: <code>GET</code><br /> Parameters: <code>path</code> (required)<br /> <br /> To get a list of paths and content items (a.k.a. "files") that are stored in Vault, call this end point. The required path is a Unix-like structure starting with '/' at the "root" level.<br /> <br /> Sample request:<br /> <code>https://api.apilayer.com/vault/browse?path=/</code><br /> <br /> Sample response: <pre> { "files": [ "TestData1" ], "folders": [ "Client1", "ProductA" ] }</pre> </li> </ul> <h2><br /> Rate Limits and Pricing</h2> <p>The number of times your API key can be used within a month to call any of the end-points listed above for this API is determined by the subscription level at which you've subscribed. At the time of this writing there are <a href="https://apilayer.com/marketplace/vault-api#pricing">three price levels</a>:</p> <ul> <li><strong>FREE</strong> - limit of <strong>3,000</strong> requests per month (~100/day)</li> <li><strong>$5.99</strong>/month - limit of <strong>150,000</strong> requests per month (~5,000/day)</li> <li><strong>$39.99</strong>/month - limit of <strong>1,500,000</strong> requests per month (50,000/day)</li> </ul> <p>The current rate limits in effect and the number of requests you have left based on your subscription is returned in the headers of each API call:</p> <pre> RateLimit-Limit=5000 RateLimit-Remaining=4908 RateLimit-Reset=61728 X-RateLimit-Limit-Day=5000 X-RateLimit-Limit-Month=150000 X-RateLimit-Remaining-Day=4908 X-RateLimit-Remaining-Month=149320</pre> <p> </p> <h2>Open Source Delphi Implementation</h2> <p>To start using this API in my Delphi apps, I created a free account and started testing with the REST Debugger. Using the <a href="/trestresponsedatasetadapter-and-hidden-gems-rest-debugger">Copy Components</a> button, I quickly created a data module, then spent the next couple of days working out the nuances of each call and handling errors.</p> <p>In the process of building and testing the data module, a small app grew around this, in the end becoming a complete Vault API Manager. It allows you to enter your API key (which is saved locally for your convenience) and then try out all the different API calls with a simple interface. You can use it with default or custom encryption keys (depending on your subscription) to create, update, browse, and delete content and keys in your own account.</p> <p>After polishing it up a little, I have released it as <a href="https://github.com/corneliusdavid/VaultAPI">open source on GitHub</a>--there's a screenshot below and more on GitHub. It's written in Delphi 11 and uses Firemonkey--I've run it on both Windows and MacOSX. I hope others find this useful; I welcome comments.</p> <p><img alt="Vault API Explorer app with some sample data" data-entity-type="file" data-entity-uuid="8de7bd6d-76a2-4325-a86a-7bcd66d5cf60" src="/sites/default/files/inline-images/ManagerAppWithFiles.png" /></p> </div> <section id="node-comment"> <div class="comment-form-wrap"> <h2 class="add-comment-title"><i class="icon-add_comment theme-color"></i> Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=165&amp;2=comment_node_blog&amp;3=comment_node_blog" token="-Nv_G7avFToCYy8NHUxoXs5lycOWZLPr50WSGhK9IiQ"></drupal-render-placeholder> </div> <!--/.comment-form --> </section> Fri, 07 Oct 2022 05:35:11 +0000 david 165 at https://corneliusconcepts.tech Cloud Sync Options https://corneliusconcepts.tech/cloud-sync-options <span property="dc:title">Cloud Sync Options</span> <div class="node-taxonomy-container"> <h3 class="term-title"><i class="icon-hashtag theme-color"></i> Category</h3> <ul class="taxonomy-terms"> <li class="taxonomy-term"><a href="/cloud" hreflang="en">Cloud Computing</a></li> </ul> </div> <!--/.node-taxonomy-container --> <span rel="sioc:has_creator"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span></span> <span property="dc:date dc:created" content="2022-07-20T19:47:29+00:00" datatype="xsd:dateTime">Wed, 07/20/2022 - 12:47</span> <div property="content:encoded" class="field field--name-body field--type-text-with-summary field--label-hidden field-item"><p>Supporting a wide variety of projects using different versions of Delphi and a mix of databases, I keep several development environments alive and active. In addition to my main desktop machine, I have eight virtual machines (two just for testing, one with Windows 7 and Delphi 5), and a laptop with Windows 11. All code is stored online in either BitBucket or GitHub but there are many other files that are useful when working with these projects such as notes, sample imports and exports, contracts, and other related materials that don't fit in the source code repository. Of course, everything is backed up, but it's important to be able to quickly share a file from one machine to another, such as a screenshot taken on one machine to another where I'm preparing documentation. File syncing between machines (and through the cloud) is very useful.</p> <p>Years ago, I started the inexpensive OneDrive subscription ($1.99/month) to add 100 GB of cloud storage to the free 10 GB Microsoft offers, giving me plenty of space for these extra files. It was handy, always there and automatically set up with each new Windows installation, so I started using it more and more. I started adding photos I'd take with my phone, documents for non-business related things, and so forth. It has a pretty decent photo viewer built in so when someone asked to see some pictures I took, I could just share a folder and they'd get a nice way to view the gallery. Additionally, when I worked on a Word document, I could share it in OneDrive and a colleague and I could both make changes to it, somewhat like Google Docs.</p> <p>After a few years of use, I realized I had used 90 GB out of my 110 GB total space--and things were slowing down significantly. Sometimes, adding a file from a virtual machine wouldn't show up on my main desktop until I closed and restarted the OneDrive app; sometimes I had to view it online and manually download it because it just wasn't yet aware of the new file. And worst of all, sometimes OneDrive would just sit there "looking for changes" or "preparing to download" with nothing happening for <em>hours</em>! (I found out that sometimes OneDrive needs to re-evaluate <em>every</em> file to realize what is new--GAH!)</p> <p>Finally, I had enough and searched for "OneDrive too slow" to see if there was a fix. I was surprised to see several blogs and forums where people were complaining about the same thing I had experienced; one <a href="https://www.reddit.com/r/onedrive/comments/ce22v7/onedrive_ungodly_slow/">Reddit thread</a> said "OneDrive syncing is abysmally slow". There were a variety of suggestions, most of which I had already tried. The most frequent "solution" was to simply use a different file syncing service.</p> <p>So my next search was "OneDrive alternatives" which yielded quite a few options. There are many different ways to sync and store files in the cloud and most companies give you a small, free account so I had a chance to test them out. Two things at the top of my list is to be able to sync from multiple places and to selectively sync (different machines need to sync to different folders; none of them need everything). OneDrive allows up to 10 devices (I think) before you need to use a different account. I found some of the free service plans only allowed one or two devices to sync before you had to pay. Many services have a different price tier for personal and business accounts--usually the business accounts are for multiple users (I only evaluated the personal or individual price plans). I was actually surprised how much variance there is between the services.</p> <p>Here's a brief list of the ones I looked at and the things about them that stuck out to me:</p> <ul> <li><a href="https://onedrive.live.com/">OneDrive</a> <ul> <li>10 GB free, $1.99/month for +100 GB</li> <li>Hook up 10 Windows machines + mobile devices</li> <li>All files in one folder</li> <li>Select which folders are synced to a device</li> <li>Can share files/folders for editing or viewing</li> <li>Offers secure folder (max of 3 files unless on Office 365 subscription)</li> <li>Nice photo viewer</li> </ul> </li> <li><a href="https://www.dropbox.com">DropBox</a> <ul> <li>2 GB free, additional 500 MB for each referral; $12/month for 2 TB</li> <li>Hook up to 3 devices with free account, unlimited with paid account</li> <li>All files in one folder</li> <li>Select which folders are synced to a device (paid plan only)</li> <li>Can share files/folders for editing or viewing</li> <li>Offers secure folder (paid plans only)</li> </ul> </li> <li><a href="https://www.idrive.com/online-file-sync">iDrive Sync</a> <ul> <li>Really more of a backup solution than file sync, you buy the backup solution and get file sync for free</li> <li>10 GB free, $80/year for 5 TB of backup and 5 TB of file sync</li> <li>File sync space matches backup space but is a separate service you can enable or disable on each device</li> <li>Hook up unlimited number of machines, works on Windows, Mac, iPad, and Linux</li> <li>All files in one folder</li> <li>Cannot selectively choose folders--it's all or nothing if file sync enabled</li> </ul> </li> <li><a href="https://tresorit.com">Tresorit</a> <ul> <li>This isn't a file-sync service but secure cloud storage; it's all about security and encryption;  included in this list because I'm also interested in a small number of folders to be encrypted (health records, business contracts, legal documents) and because with the app installed, the remote files are just as easily accessed from other devices as if they were local or synced through a virtual network drive that it sets up.</li> <li>No advertised free plan but if you sign up and then cancel, you're left with a 3 GB free account!</li> <li>1 TB for $12/month</li> <li>Hook up maximum of two devices on free plan, 10 on paid; Windows, Mac, Linux, mobile</li> </ul> </li> <li><a href="https://www.box.com/">Box</a> <ul> <li>10 GB free (I was granted an extra 10 GB for some reason); $10/month for 2 TB</li> <li>Hook up only 1 device on free plan, not sure how many on paid plan</li> <li>Can share files/folders for editing or viewing</li> <li>Really difficult to ascertain specifics on the other features I'm interested in for comparison--abandoned the research on this one</li> </ul> </li> <li><a href="https://adrive.com/">ADrive</a> <ul> <li>No free plan but start as low as $2.50/month for 100 GB, $6.25/month for 250 GB, and so on up to $250/month for 10 TB</li> <li>Files are synced with SCP, SFTP, or Rsync over SSH</li> <li>This feels like a really different kind of service, so abandoned research on it.</li> </ul> </li> <li><a href="https://www.sync.com/">Sync</a> <ul> <li>5 GB for free, $8/month for 2 TB</li> <li>Hook up an unlimited number of devices, even with the free account</li> <li>All files in one folder</li> </ul> </li> <li><a href="https://www.pcloud.com/">pCloud</a> <ul> <li>10 GB free, $49/year 500 GB, $100/year 2 TB</li> <li>Only service with lifetime plans: $175 for 500 GB, $350 for 2 TB</li> <li>Hook up an unlimited number of devices, even with the free account</li> <li>Total control of which folders on your device get synced to which folders in the cloud</li> <li>Can share files/folders for editing or viewing; can share links to static HTML files!</li> <li>Video and music player for shared media</li> <li>Can add encryption for extra cost</li> </ul> </li> </ul> <p>There were a few others like <a href="https://freefilesync.com/">FreeFileSync</a> and <a href="https://owncloud.com/">ownCloud</a>, but they didn't fit my needs.</p> <h3>pCloud for me</h3> <p>I ended up keeping my free <strong>DropBox</strong> account, adding the free <strong>Sync</strong> and <strong>Tresorit</strong> services, and purchasing a subscription to <strong>pCloud</strong>. I have had DropBox for quite awhile and use it for delivering custom projects--it works well so I'm keeping that. Sync isn't very flexible but it can be installed on multiple devices and integrates well with Windows so could be useful for sharing something quickly to all machines. Tresorit's high security feature is important and I only need it on a couple of machines for a small number of files so their free service will satisfy me.</p> <p>The very best option for me is pCloud. It's very inexpensive (the <a href="https://www.pcloud.com/lifetime/">lifetime plan</a> is just over the cost of 3 year's worth of service), gives me 5 times more space than OneDrive, and has unlimited device installs. But the the most important part of this product for me is it's folder-sync flexibility and control. When pCloud starts and hooks up to your account for the first time, nothing is synced to the machine initially, even if you synced folders from other machines. Instead you add sync folders from your device to specific folders in your pCloud account. That means that on my main development machine where I keep the master copy of project documents and notes for all clients, I can sync all those folders to pCloud, then on different virtual machines, I can selectively link just the client folders that pertain to the projects on that VM and skip all the rest. I can also pick and choose folders here and there instead of using one central folder for everything, allowing me to keep the organization of my files in a way that makes sense to me instead of restricted to the cloud-sharing app I'm using. For example, I keep a list of images and icons I've collected over the years in a folder separate from the business documents and can now sync that folder and the business documents folder individually rather than using two different services or putting them in the same global-sync folder.</p> <p>This took some time to look at the options but I've already reduced my reliance on OneDrive by 50% and seen other improvements in how files are organized and synchronized between various machines. Time will tell how well pCloud holds up once the file count increases but I'm already happier with the options and speed I've seen so far.</p> </div> <section id="node-comment"> <article data-comment-user-id="0" id="comment-445" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/0"> </article> <mark class="hidden" data-comment-timestamp="1661184893"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/445#comment-445" class="permalink" rel="bookmark" hreflang="en">Hi David: I&#039;ve had to shut…</a></h3> <div class="single-comment-meta"> <span><span lang="" typeof="schema:Person" property="schema:name" datatype="">Navid (not verified)</span> Mon, 08/22/2022 - 02:36</span> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>Hi David: I've had to shut down pCloud when using the RAD Studio IDE, as it repeatedly becomes unresponsive for about 10 seconds or so (which I had heard referred to as the “flicker” bug). I hope your mileage is better. Despite this, pCloud remains my favorite.</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=445&amp;1=default&amp;2=en&amp;3=" token="9LkluNP608CGNwwYJph0FOoFY0pjA40s-CmhsUIPZLo"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> <div class="indented"> <article data-comment-user-id="1" id="comment-447" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/1"> </article> <mark class="hidden" data-comment-timestamp="1661185216"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/447#comment-447" class="permalink" rel="bookmark" hreflang="en">Interesting. I have noticed…</a></h3> <div class="single-comment-meta"> <span><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span> Mon, 08/22/2022 - 09:20</span> <p class="visually-hidden">In reply to <a href="/comment/445#comment-445" class="permalink" rel="bookmark" hreflang="en">Hi David: I&#039;ve had to shut…</a> by <span lang="" typeof="schema:Person" property="schema:name" datatype="">Navid (not verified)</span></p> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>Interesting. I have noticed some flickering in the last few weeks but hadn't traced it down yet as it doesn't happen very often. But now that you mention it, I didn't notice it before I started using pCloud. I'll look into this more deeply--Thanks!</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=447&amp;1=default&amp;2=en&amp;3=" token="se2PF9xuAQqRRweZvij_k80ub5L1TG-tqL5pYOeKs4o"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> <div class="indented"> <article data-comment-user-id="0" id="comment-448" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/0"> </article> <mark class="hidden" data-comment-timestamp="1661225071"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/448#comment-448" class="permalink" rel="bookmark" hreflang="en">In my case, it happens every…</a></h3> <div class="single-comment-meta"> <span><span lang="" typeof="schema:Person" property="schema:name" datatype="">Navid (not verified)</span> Mon, 08/22/2022 - 11:53</span> <p class="visually-hidden">In reply to <a href="/comment/447#comment-447" class="permalink" rel="bookmark" hreflang="en">Interesting. I have noticed…</a> by <span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span></p> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>In my case, it happens every couple of minute or so and I often notice the pCloud tray icon indicating activity. I assume the higher frequency is because I use pCloud as a complete backup solution in addition to synchronizing selected folders.</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=448&amp;1=default&amp;2=en&amp;3=" token="1BKd21Hrfm6wV-o8t8Y9cc-916Op2k_0BmPe1s4Yky0"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> <div class="indented"> <article data-comment-user-id="1" id="comment-451" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/1"> </article> <mark class="hidden" data-comment-timestamp="1661226195"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/451#comment-451" class="permalink" rel="bookmark" hreflang="en">Keep it Synched</a></h3> <div class="single-comment-meta"> <span><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span> Mon, 08/22/2022 - 20:40</span> <p class="visually-hidden">In reply to <a href="/comment/448#comment-448" class="permalink" rel="bookmark" hreflang="en">In my case, it happens every…</a> by <span lang="" typeof="schema:Person" property="schema:name" datatype="">Navid (not verified)</span></p> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>I just synched a new, large folder to pCloud and started watching different apps and can see a little flicker--it's not bad  in Delphi 10.4 right now, though. In my opinion, I'd rather have a high-priority process keeping files synchronized quickly than the problems I had with OneDrive. This is critical when I need something in a hurry on a virtual machine and rely on file sync to provide it quickly.</p> <p>My synchronization needs are low enough the flicker will not likely be a major problem for me. I am glad you pointed it out though--I was wondering what was causing it.</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=451&amp;1=default&amp;2=en&amp;3=" token="7ahaDJT9o-1eiG1VTcjGveicylu21JoX_0nh6m_Lf3w"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> </div></div></div> <div class="comment-form-wrap"> <h2 class="add-comment-title"><i class="icon-add_comment theme-color"></i> Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=164&amp;2=comment_node_blog&amp;3=comment_node_blog" token="DzMm1MInjCZrScxD30Z34imSS7oy-gn4yBamtiupHj8"></drupal-render-placeholder> </div> <!--/.comment-form --> </section> Wed, 20 Jul 2022 19:47:29 +0000 david 164 at https://corneliusconcepts.tech Delphi Debates: With, Goto, & Label--and Exit https://corneliusconcepts.tech/delphi-debates-with-goto-label <span property="dc:title">Delphi Debates: With, Goto, &amp; Label--and Exit</span> <div class="node-taxonomy-container"> <h3 class="term-title"><i class="icon-hashtag theme-color"></i> Category</h3> <ul class="taxonomy-terms"> <li class="taxonomy-term"><a href="/programming" hreflang="en">Programming</a></li> </ul> </div> <!--/.node-taxonomy-container --> <span rel="sioc:has_creator"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span></span> <span property="dc:date dc:created" content="2022-07-12T03:21:27+00:00" datatype="xsd:dateTime">Mon, 07/11/2022 - 20:21</span> <div property="content:encoded" class="field field--name-body field--type-text-with-summary field--label-hidden field-item"><p>The Delphi Debate series continues with another topic that has been discussed back and forth for ages. This time, instead of a procedure or function in the RTL, we are talking about three <a href="https://docwiki.embarcadero.com/RADStudio/Alexandria/en/Fundamental_Syntactic_Elements_(Delphi)#Reserved_Words">reserved words</a>: the <strong>with</strong>, <strong>goto</strong>, and <strong>label </strong>statements which pre-date Delphi--they are part of the core Pascal language itself!</p> <h3>WITH</h3> <p>The <a href="https://docwiki.embarcadero.com/RADStudio/Alexandria/en/Declarations_and_Statements_(Delphi)#With_Statements"><code>with</code></a> statement in Delphi is a shorthand for accessing methods and properties of an object. An argument for using it is that it shortens the length of a line of code thus making the code more readable. For example:</p> <pre> <code>procedure TMyForm.UpdateInventoryItem(const NewQty: Integer); begin dmStoreInventoryData.tblUpdateItemForStore.Edit; dmStoreInventoryData.tblUpdateItemForStore.FieldByName('Qty').AsInteger := NewQty;   dmStoreInventoryData.tblUpdateItemForStore.Post; end;</code> </pre> <p>Can be rewritten like this:</p> <pre> procedure TMyForm.UpdateInventoryItem(const NewQty: Integer); begin   with dmStoreInventoryData do begin     tblUpdateItemForStore.Edit;     tblUpdateItemForStore.FieldByName('Qty').AsInteger := NewQty;     tblUpdateItemForStore.Post;   end; end;</pre> <p>Furthermore, you can nest <code>with</code> statements:</p> <pre> procedure TMyForm.UpdateInventoryItem(const NewQty: Integer); begin   with dmStoreInventoryData do begin     with tblUpdateItemForStore do begin       Edit;       FieldByName('Qty').AsInteger := NewQty;       Post;     end;   end; end;</pre> <p>Now, disregarding the fact we added four "scaffolding" lines, the three original lines look rather succinct; but even if you're the only programmer who deals with this code, there will come a point in time in the future where you question whether you're calling an <code>Edit</code> method of the data module or the table object in the data module. Worse, what if you forget about this <code>with</code> statement and add an <code>Edit</code> or <code>Post</code> method to the data module? Without touching this code, you've just broken it!</p> <p>There are much better ways of handling coding tasks like this. In this small example, experienced programmers would quickly point out that a table object should not be accessed outside the data module but a procedure should be created in the data module that deals with the local table object, thus reducing the object layer by one level and thus the "need" to use a <code>with</code> statement. Of course, there are many other ways and this is just one example, but that's a topic for another day.</p> <p>Generally, the <code>with</code> statement is strongly frowned upon by experienced programmers in the Delphi community, and I agree--with rare exceptions.</p> <p>Sometimes, I need to pop up a form to request some simple option from the user, returning a single item picked from a list (for example). So I create a dialog form, remove it's global variable and add a public class function like this:</p> <pre> class function TfrmFilterSKUs.GetSKUFilter: string; begin with TfrmFilterSKUs.Create(Application.MainForm) do try if ShowModal = mrOK then Result := lbCategories.Items[lbCategories.ItemIndex] else Result := EmptyStr; finally Free; end; end; </pre> <p>This is taken straight out of one of my projects and while I recommend <em>against</em> using <code>with</code> on general principles, I would say that <em>IF</em> you're the only programmer maintaining the code and <em>IF</em> the section of code is very small and <em>IF</em> you never use more than one object in a <code>with</code> and <em>IF</em> you never, ever nest them, then in this very limited use case, they are handy.</p> <h3>GOTO &amp; LABEL</h3> <p>I am surprised--and horrified--to know that <a href="https://docwiki.embarcadero.com/RADStudio/Alexandria/en/Declarations_and_Statements_(Delphi)#Goto_Statements"><code>goto</code></a> still exists in the Delphi language. I had to look up the syntax because I honestly didn't even know how to use it--or its corresponding <code>label</code> statement. </p> <p>The <code>goto</code> statement makes an immediate jump of the execution point to another spot in the code identified by its pre-defined <code>label</code>. It can jump into or out of a <code>for</code>-loop, past an object creation statement, create unidentifiable infinite loops, and all sorts of other very bad things. One article says, "<a href="https://en.wikipedia.org/wiki/Niklaus_Wirth">it's like a ticking time bomb</a>."</p> <p>Delphi's roots are in Pascal which was designed to "encourage good programming practices using structured programming" (quoted from <a href="https://en.wikipedia.org/wiki/Pascal_(programming_language)">Wikipedia</a>) and I imagine part of that was to wean people off using line numbers and GOTO statements that were so prevalent in BASIC code, which was introduced six years earlier and quickly became the defacto programming language on many systems both large and small. So to include a way to perpetuate <a href="https://academickids.com/encyclopedia/index.php/Spaghetti_code">spaghetti code</a> perplexes me. Perhaps it was unthinkable in the day to design a language without a way to jump directly to another location in code like you could in assembly, or perhaps <a href="https://en.wikipedia.org/wiki/Niklaus_Wirth">Niklaus Worth</a> wanted to provide a way that people could get productive right away if they had code they wanted to port to help make the language popular (I have nothing to back this up).</p> <p>Whatever the reason, it can't be removed now without possibly breaking some old code (although maybe that type of code <em>should</em> be broken!).</p> <p>I hope there's no doubt remaining where I stand on this: <strong>Don't use <code>goto</code>. Ever.</strong></p> <h3>Exit</h3> <p>Now I want to throw one more into the mix because it ties in very closely with <code>goto</code>--but I'm not nearly as adamant against using it. The system procedure <a href="https://docwiki.embarcadero.com/Libraries/Alexandria/en/System.Exit"><code>Exit</code></a> in Delphi is not a reserved word but acts like a <code>goto</code> in a method with an implied <code>label</code> at the end of the method; more simply, it exits from the current procedure or function. Similar to the <code>goto</code>, it breaks out of standard control structures and can easily be missed in a long method. There are two redeeming qualities (in my opinion): first, it always exits the method--you don't have to look around for the <code>label</code> to figure out where program execution goes; and second, it can't jump into the middle of another block of code.</p> <p>From what I've seen, the most frequent use of <code>Exit</code> is to check for some constraints and if something fails, simply skip processing the rest of the code in the procedure. For example if you're opening a database table and checking the connection but find it's not accessible, then just call <code>Exit</code> rather than fall through a proper <code>if</code>-<code>else</code> structure. This can simplify things by keeping control structure code out of the way of what the method is trying to do but isn't that big of an advantage if written well.</p> <p>This question <a href="https://stackoverflow.com/questions/9842859/is-delphis-exit-statement-dangerous">was posed on StackOverflow</a> several years ago and there were some good points made in the answers. I find myself siding with those who prefer a single exit point by using control structures and avoiding arbitrary jumps, even if it's just an innocent call to the convenient <code>Exit</code> procedure.</p> </div> <section id="node-comment"> <article data-comment-user-id="0" id="comment-393" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/0"> </article> <mark class="hidden" data-comment-timestamp="1657748565"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/393#comment-393" class="permalink" rel="bookmark" hreflang="en">Thera are two more</a></h3> <div class="single-comment-meta"> <span><a rel="nofollow" href="https://blog.dummzeuch.de" lang="" typeof="schema:Person" property="schema:name" datatype="">Thomas Müller (not verified)</a> Wed, 07/13/2022 - 02:48</span> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>You missed two more goto like statements:</p> <ul> <li>Continue</li> <li>Break</li> </ul> <p>They are basically gotos with a hard coded label at the end and after a loop respectively. Unlike Exit I don't use them as they make it difficult to understand the flow of execution, in particular in nested loops.</p> <p>As for not using goto at all: I have to admit that there is exactly one goto in my whole code base where I simply couldn't think of a better way to implement what I needed.</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=393&amp;1=default&amp;2=en&amp;3=" token="sbDbrMLQLt0hxNBZlwYX00D_MJrEZpizy1w6qQyFpOY"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> <div class="indented"> <article data-comment-user-id="1" id="comment-394" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/1"> </article> <mark class="hidden" data-comment-timestamp="1657749073"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/394#comment-394" class="permalink" rel="bookmark" hreflang="en">Almost did</a></h3> <div class="single-comment-meta"> <span><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span> Wed, 07/13/2022 - 14:51</span> <p class="visually-hidden">In reply to <a href="/comment/393#comment-393" class="permalink" rel="bookmark" hreflang="en">Thera are two more</a> by <a rel="nofollow" href="https://blog.dummzeuch.de" lang="" typeof="schema:Person" property="schema:name" datatype="">Thomas Müller (not verified)</a></p> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>I briefly considered adding <code>break</code> and <code>continue</code> to the conversation but felt they are part of the loop control structure and (IMHO) not as "bad" as the goto-like constructs. Also, looping code is much more concise so anything that breaks out will be more visible and you're unlikely to skip large sections of code like an <code>Exit</code> at the top of a procedure can. Of course, these are generalities and cases could be made either way.<br /> <br /> Thanks for mentioning this!</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=394&amp;1=default&amp;2=en&amp;3=" token="G2lBva5jZq9fVVdjIOSjgeqzDu-OoDY3Gy_igLIo9BU"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> </div> <article data-comment-user-id="0" id="comment-395" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/0"> </article> <mark class="hidden" data-comment-timestamp="1658937258"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/395#comment-395" class="permalink" rel="bookmark" hreflang="en">Did you know you can do this…</a></h3> <div class="single-comment-meta"> <span><span lang="" typeof="schema:Person" property="schema:name" datatype="">IanB (not verified)</span> Tue, 07/26/2022 - 13:25</span> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>Did you know you can do this too?<br /> <br /> <strong> with dmStoreInventoryData, tblUpdateItemForStore do<br />  begin<br />       Edit;<br />       FieldByName('Qty').AsInteger := NewQty;<br />       Post;<br />   end;</strong><br /> <br /> Note I say "<em>can</em>" but not "<em>should</em>" or indeed "<em>ever</em>". </p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=395&amp;1=default&amp;2=en&amp;3=" token="pNSJBC-CQ7JRIfg3SsP2M0oQN3ySknCL0b984TVO5ys"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> <div class="indented"> <article data-comment-user-id="1" id="comment-397" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/1"> </article> <mark class="hidden" data-comment-timestamp="1658937990"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/397#comment-397" class="permalink" rel="bookmark" hreflang="en">Shorthand for Nested With</a></h3> <div class="single-comment-meta"> <span><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span> Wed, 07/27/2022 - 08:56</span> <p class="visually-hidden">In reply to <a href="/comment/395#comment-395" class="permalink" rel="bookmark" hreflang="en">Did you know you can do this…</a> by <span lang="" typeof="schema:Person" property="schema:name" datatype="">IanB (not verified)</span></p> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>Yes, I've seen that--and it's almost as bad as nesting <code>with</code> statements! In fact, it's really a short-hand way of specifying nested <code>with</code>.</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=397&amp;1=default&amp;2=en&amp;3=" token="jJfFj8eO3jy0YW97ZkaSeJwZUy7Pq8kjH-2YpzVJvGQ"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> </div> <div class="comment-form-wrap"> <h2 class="add-comment-title"><i class="icon-add_comment theme-color"></i> Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=163&amp;2=comment_node_blog&amp;3=comment_node_blog" token="VquigEOO3mDeATBhzi0sibCqTqQyrCcelCC2WJ7OkRI"></drupal-render-placeholder> </div> <!--/.comment-form --> </section> Tue, 12 Jul 2022 03:21:27 +0000 david 163 at https://corneliusconcepts.tech Delphi Debates: Assigned https://corneliusconcepts.tech/delphi-debates-assigned <span property="dc:title">Delphi Debates: Assigned</span> <div class="node-taxonomy-container"> <h3 class="term-title"><i class="icon-hashtag theme-color"></i> Category</h3> <ul class="taxonomy-terms"> <li class="taxonomy-term"><a href="/programming" hreflang="en">Programming</a></li> </ul> </div> <!--/.node-taxonomy-container --> <span rel="sioc:has_creator"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span></span> <span property="dc:date dc:created" content="2022-06-27T16:30:37+00:00" datatype="xsd:dateTime">Mon, 06/27/2022 - 09:30</span> <div property="content:encoded" class="field field--name-body field--type-text-with-summary field--label-hidden field-item"><p>As a Delphi MVP, I was surveyed on my stance of the use of <a href="/delphi-debates-freeandnil"><code>FreeAndNil</code></a>. In that questionnaire was included a question about the use of <code>Assigned()</code>. Really? Is that debated as well? I couldn't find anything on the internet debating this except for an <a href="https://stackoverflow.com/questions/8548843/why-should-i-not-use-if-assigned-before-accessing-objects">old discussion on StackOverflow</a>. I use this function frequently and as I looked more deeply at what it does, I'm even more confident of its use.</p> <p>Basically what the <code>System.Assigned()</code> function does is "<a href="https://docwiki.embarcadero.com/Libraries/Alexandria/en/System.Assigned">Tests for a nil (unassigned) pointer or procedural variable.</a>" In other words, the following statement makes sure the variable <code>P</code> is not nil:</p> <pre> if Assigned(P) then ...</pre> <p>On the surface, that seems equivalent to just simply writing:</p> <pre> if P &lt;&gt; nil then ...</pre> <p>For regular object variables, that <em>is</em> equivalent but this function also works on procedural variables. If <code>P</code> is a procedural variable, you'd have to write this instead:</p> <pre> if @P &lt;&gt; nil then ...</pre> <p>By using <code>Assigned()</code>, both of those scenarios are automatically covered.</p> <p>My most frequent use of <code>Assigned</code> is when calling event handlers from a class. For example, let's say I have a class that performs a task and it provides events for when it starts, stops, and to report its progress. The calling program or class can hook into those events to log the activity or display status on the screen to the user. It might look like this:</p> <pre> type TMyTaskClass = class private FOnStartTask: TNotifyEvent; FOnEndTask: TNotifyEvent;   FOnNotifyProgress: TGetStrProc; public property OnNotifyProgress: TGetStrProc read FOnNotifyProgress write FOnNotifyProgress; property OnStartTask: TNotifyEvent read FOnStartTask write FOnStartTask; property OnEndTask: TNotifyEvent read FOnEndTask write FOnEndTask; end;</pre> <p>The class would have additional methods to do the work for which this class was designed and somewhere in those methods the event handlers would need to be called. But hooking into the events is optional--this class could be used, for example, from a unit test that completely ignores the events. So each time the event handlers are called, they need to be checked to see if they're assigned. Doing that every time you need to call an event is messy so I put those checks into "Do" methods, typically in a "protected" section of the class:</p> <pre> protected   procedure DoOnNotifyProgress(const S: string); procedure DoOnStartTask(Sender: TObject); procedure DoOnEndTask(Sender: TObject);</pre> <p>The bodies of these procedures look like this:</p> <pre> procedure TMyClass.DoOnNotifyProgress(const S: string); begin   if Assigned(FOnNotifyProgress) then   FOnNotifyProgress('Progress: ' + S); end; procedure TMyClass.DoOnEndTask(Sender: TObject); begin   if Assigned(FOnEndTask) then   FOnEndTask(Self); end; procedure TMyClass.DoOnStartTask(Sender: TObject); begin   if Assigned(FOnStartTask) then   FOnStartTask(Self); end;</pre> <p>The call to <code>Assigned()</code> in these procedures checks to see if the event handlers are hooked up and if so, calls them. The main body of the class can now simply call these "Do" procedures whenever it needs to trigger event handlers.</p> <p>Another common use is when parsing XML or JSON data. For example, the <a href="https://docwiki.embarcadero.com/Libraries/Alexandria/en/System.JSON.TJSONValue.GetValue"><code>System.JSON.TJSONValue.GetValue</code></a> function returns an object parsed from a JSON string but if the <code>Path</code> parameter was not found, the result is nil. Checking for this is key when dealing with public REST APIs that may return varying results.</p> <p>Other places where I use the <code>Assigned()</code> test is in the destructor of a class that created objects in the constructor and I want to make sure they were actually created before attempting to free their memory. (Of course, if you adhere to <a href="https://jonlennartaasenden.wordpress.com/2015/01/13/inversion-of-control-dependency-injection-service-oriented-programming/">Dependency Injection</a> principles, strongly promoted by <a href="https://lp.embarcadero.com/DependencyInjection">Nick Hodges</a>, your classes won't typically create their own objects--a practice I should adopt more rigidly.)</p> <p>An aesthetic advantage to using <code>Assigned()</code> over explicitly checking the value against nil is if there are multiple clauses in an <code>if</code> statement, you don't need to use as many parenthesis--a minor point but keeps the code just a little cleaner and easier to read.</p> <p>One important thing to remember is that <code>Assigned()</code> does not validate a pointer, it simply checks to see if the pointer has some value indicating it was purposefully assigned at some point with a valid address; the object to which it points could've been freed--<a href="https://www.tek-tips.com/viewthread.cfm?qid=952145">an argument for promoting the use of FreeAndNil</a>.</p> <p>Overall, I believe that <code>Assigned()</code> is very useful in many cases--I doubt many will disagree.</p> </div> <section id="node-comment"> <article data-comment-user-id="0" id="comment-372" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/0"> </article> <mark class="hidden" data-comment-timestamp="1656429309"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/372#comment-372" class="permalink" rel="bookmark" hreflang="en">You are missing an important…</a></h3> <div class="single-comment-meta"> <span><a rel="nofollow" href="https://blog.dummzeuch.de" lang="" typeof="schema:Person" property="schema:name" datatype="">Thomas Müller (not verified)</a> Tue, 06/28/2022 - 04:40</span> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>You are missing an important point of what Assigned() does for event handlers: It only checks the code part of a method pointer (see the TMethod record declaration), not the data part.</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=372&amp;1=default&amp;2=en&amp;3=" token="OqrMzFHyBf9HOwPmTNRTWR8uHscnxdAZQyZVvSFMXX4"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> <div class="indented"> <article data-comment-user-id="1" id="comment-374" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/1"> </article> <mark class="hidden" data-comment-timestamp="1656430796"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/374#comment-374" class="permalink" rel="bookmark" hreflang="en">Need deep-dive info</a></h3> <div class="single-comment-meta"> <span><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span> Tue, 06/28/2022 - 08:39</span> <p class="visually-hidden">In reply to <a href="/comment/372#comment-372" class="permalink" rel="bookmark" hreflang="en">You are missing an important…</a> by <a rel="nofollow" href="https://blog.dummzeuch.de" lang="" typeof="schema:Person" property="schema:name" datatype="">Thomas Müller (not verified)</a></p> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>Good point but this wasn't a deep dive into what <code>Assigned()</code> does under the hood, just a discussion of when it's used.</p> <p>I would like more information about why this is important--there's not much to go on with just the basic <a href="https://docwiki.embarcadero.com/Libraries/Alexandria/en/System.TMethod" rel="nofollow">TMethod</a> documentation.</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=374&amp;1=default&amp;2=en&amp;3=" token="Bgu4oXG6TISsIFsKFl9UZEHa_ac08Oy_RBaUvWMbJGg"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> </div> <div class="comment-form-wrap"> <h2 class="add-comment-title"><i class="icon-add_comment theme-color"></i> Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=162&amp;2=comment_node_blog&amp;3=comment_node_blog" token="arBwYz1afMski90mIpzYelLaiAUSrsENAShLbl_MFkY"></drupal-render-placeholder> </div> <!--/.comment-form --> </section> Mon, 27 Jun 2022 16:30:37 +0000 david 162 at https://corneliusconcepts.tech Delphi Debates: FreeAndNil https://corneliusconcepts.tech/delphi-debates-freeandnil <span property="dc:title">Delphi Debates: FreeAndNil</span> <div class="node-taxonomy-container"> <h3 class="term-title"><i class="icon-hashtag theme-color"></i> Category</h3> <ul class="taxonomy-terms"> <li class="taxonomy-term"><a href="/programming" hreflang="en">Programming</a></li> </ul> </div> <!--/.node-taxonomy-container --> <span rel="sioc:has_creator"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span></span> <span property="dc:date dc:created" content="2022-06-24T03:25:29+00:00" datatype="xsd:dateTime">Thu, 06/23/2022 - 20:25</span> <div property="content:encoded" class="field field--name-body field--type-text-with-summary field--label-hidden field-item"><p>When freeing an object in Delphi, simply calling its <code>Free</code> method calls the object's <code>destructor</code> and releases the memory allocated to the instance of the object. But it doesn't change the address of the referencing variable which, therefore, still points to the place in memory where the object existed. The released memory can be quickly reused by other objects or resources and if you try to access the object again without re-creating it, you could get an Access Violation or some other error or worse yet, unpredictable behavior. Of course, it would be silly to purposefully access a freed object but sometimes you inherit messy code and don't totally understand when or where objects are created and freed. In some cases, programmers take one additional step after freeing an object--and that is, setting its reference variable to <code>nil</code>.</p> <p>This practice of "nilling" an object reference was so popular that back in Delphi 5, a procedure was added to the <code>SysUtils</code> unit called <code>FreeAndNil</code> which does exactly as the name suggests. Soon after, there erupted a debate as to whether you should use this procedure always, sometimes, or never. Proponents for it say it's <a href="https://blog.eurekalog.com/2009/04/why-should-you-always-use-freeandnil_28.html">a safer way to program</a>; those against say <a href="https://codeverge.com/embarcadero.delphi.non-tech/freeing-your-life-of-freeandnil-a/1086202">if you design your code well, you should never need it</a> (long read). In Delphi 10.4, <a href="http://www.delphimagazine.com/2020/06/05/magic-behind-freeandnil/">type-safety was added to the procedure</a>.</p> <p>The debate continues--and will be <a href="https://blogs.embarcadero.com/freeandnil-delphi-developer-debate/">discussed next week</a>. It should be a lively webinar!</p> <p>My purpose here is not to add any arguments for or against but simply to state where I stand:<strong> I seldom use <code>FreeAndNil</code></strong>. Note that I didn't say "never" but I rarely need it.</p> <p>My personal opinion is that frequent use of <code>FreeAndNil</code> is lazy programming--you're not really thinking about when your object variables are in use and you're constantly checking if they're nil before you use them because you didn't take the time to limit their scope or carefully construct the flow of your application in such a way you know whey they're created and freed.</p> <p>It's similar to using the emergency brake when you park your car on level ground. It's not going to move anyway when you leave it in gear (or Park). I think about whether or not I really need to use the emergency brake and only set it when parking on hills. Likewise, I only use <code>FreeAndNil</code> when I know I'll need to check the status of an object variable later. Sure, I could set a Boolean flag or use some other way of knowing whether or not I need to create an object but <a href="http://tech.turbu-rpg.com/106/delphi-memory-management-made-simple">sometimes it's just more convenient to check the object variable and set it to nil when it's freed</a>.</p> <p><em>My advice</em>: Think about your code. Know the status of your objects. And use <code>FreeAndNil</code> sparingly but intentionally.</p> </div> <section id="node-comment"> <article data-comment-user-id="0" id="comment-363" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/0"> </article> <mark class="hidden" data-comment-timestamp="1656084691"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/363#comment-363" class="permalink" rel="bookmark" hreflang="en">Why typesafety</a></h3> <div class="single-comment-meta"> <span><a rel="nofollow" href="https://blog.dummzeuch.de" lang="" typeof="schema:Person" property="schema:name" datatype="">Thomas Müller (not verified)</a> Fri, 06/24/2022 - 07:18</span> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>The only problem with using FreeAndNil(MyObject) rather than just MyObject.Free is that if you later on change MyObject from an object instance to an interface or a record, your code will still compile but you will get very odd runtime errors. That's why adding type safety to FreeAndNil was a good thing.</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=363&amp;1=default&amp;2=en&amp;3=" token="8m2xBNay51-VrHfb8T22dIVXzh91mVBHwLSibr1NpM0"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> <div class="indented"> <article data-comment-user-id="0" id="comment-364" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/0"> </article> <mark class="hidden" data-comment-timestamp="1656173743"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/364#comment-364" class="permalink" rel="bookmark" hreflang="en">That is no longer the case…</a></h3> <div class="single-comment-meta"> <span><a rel="nofollow" href="https://www.uweraabe.de/" lang="" typeof="schema:Person" property="schema:name" datatype="">Uwe Raabe (not verified)</a> Sat, 06/25/2022 - 05:12</span> <p class="visually-hidden">In reply to <a href="/comment/363#comment-363" class="permalink" rel="bookmark" hreflang="en">Why typesafety</a> by <a rel="nofollow" href="https://blog.dummzeuch.de" lang="" typeof="schema:Person" property="schema:name" datatype="">Thomas Müller (not verified)</a></p> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>That is no longer the case with newer Delphi versions. A FreeAndNil on an interface or record variable is caught by the compiler.</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=364&amp;1=default&amp;2=en&amp;3=" token="nfzjni71FYiC4G1A2slDc5307_I7bFwIjMs57gMqBcw"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> <div class="indented"> <article data-comment-user-id="0" id="comment-373" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/0"> </article> <mark class="hidden" data-comment-timestamp="1656429289"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/373#comment-373" class="permalink" rel="bookmark" hreflang="en">That&#039;s the type safety I was…</a></h3> <div class="single-comment-meta"> <span><a rel="nofollow" href="https://blog.dummzeuch.de" lang="" typeof="schema:Person" property="schema:name" datatype="">Thomas Müller (not verified)</a> Tue, 06/28/2022 - 04:41</span> <p class="visually-hidden">In reply to <a href="/comment/364#comment-364" class="permalink" rel="bookmark" hreflang="en">That is no longer the case…</a> by <a rel="nofollow" href="https://www.uweraabe.de/" lang="" typeof="schema:Person" property="schema:name" datatype="">Uwe Raabe (not verified)</a></p> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>That's the type safety I was talking about.</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=373&amp;1=default&amp;2=en&amp;3=" token="pVtLmTHSY7Ydk9S3m69MQg2X5sUTuwTQHtPjjf_3p58"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> </div></div> <article data-comment-user-id="0" id="comment-370" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/0"> </article> <mark class="hidden" data-comment-timestamp="1656384356"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/370#comment-370" class="permalink" rel="bookmark" hreflang="en">I agree...but</a></h3> <div class="single-comment-meta"> <span><span lang="" typeof="schema:Person" property="schema:name" datatype="">Darian Miller (not verified)</span> Mon, 06/27/2022 - 16:38</span> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>I agree... but for the sake of discussion, change your key sentence to this with one word altered: FreeAndNil changed to Free:<br /> <br /> "My personal opinion is that frequent use of <strong><code>Free</code> </strong>is lazy programming--you're not really thinking about when your object variables are in use and you're constantly checking if they're nil before you use them because you didn't take the time to limit their scope or carefully construct the flow of your application in such a way you know whey they're created and freed."</p> <p>For this discussion, we are ignore the use of Free in destructors which is expected...instead, consider the following code snippet that you have probably seen a million times.  What is the benefit of using Free here - why shouldn't you use Destroy?</p> <blockquote> <p>  x := TStringList.Create;<br />   try<br />     x.Add(myParam1);<br />     x.Add(myParam2);<br />     DoSomething(x);<br />   finally<br />     x.Free;<br />   end;<br />  </p> </blockquote></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=370&amp;1=default&amp;2=en&amp;3=" token="7BjTcsGl4tsbgNFeUQgE2ZxxOCgoJIZfT3kESnXhNX4"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> <div class="indented"> <article data-comment-user-id="1" id="comment-371" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/1"> </article> <mark class="hidden" data-comment-timestamp="1656384955"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/371#comment-371" class="permalink" rel="bookmark" hreflang="en">Never call Destroy!</a></h3> <div class="single-comment-meta"> <span><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span> Mon, 06/27/2022 - 19:55</span> <p class="visually-hidden">In reply to <a href="/comment/370#comment-370" class="permalink" rel="bookmark" hreflang="en">I agree...but</a> by <span lang="" typeof="schema:Person" property="schema:name" datatype="">Darian Miller (not verified)</span></p> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>Are you saying that instead of calling <code>x.Free</code> you should call <code>x.Destroy?</code>  We are <em>never</em> supposed to call <a href="https://docwiki.embarcadero.com/Libraries/Sydney/en/System.TObject.Destroy" rel="nofollow">Destroy</a> directly.</p> <p>And for your initial argument, my point is that <code>FreeAndNil</code> is lazier than <code>Free</code> by itself because the latter requires you to know if an object has been freed instead of simply checking to see if it's nil before reusing it.</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=371&amp;1=default&amp;2=en&amp;3=" token="x3Gm7U4jNk5lxRqc1DTRC9ElAh2PIp2_9CEp9YJTT34"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> <div class="indented"> <article data-comment-user-id="0" id="comment-375" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/0"> </article> <mark class="hidden" data-comment-timestamp="1656440413"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/375#comment-375" class="permalink" rel="bookmark" hreflang="en">Correct, in that particular…</a></h3> <div class="single-comment-meta"> <span><span lang="" typeof="schema:Person" property="schema:name" datatype="">Darian Miller (not verified)</span> Tue, 06/28/2022 - 10:58</span> <p class="visually-hidden">In reply to <a href="/comment/371#comment-371" class="permalink" rel="bookmark" hreflang="en">Never call Destroy!</a> by <span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span></p> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>Correct, in that particular code snippet provided, what is the benefit of calling Free instead of Destroy?  (Why wouldn't you call Destroy instead of Free in that scenario?)</p> <p>This would seem to follow along with your exact advice for FreeAndNil:</p> <p>"My personal opinion is that frequent use of <strong><code>Free</code> </strong>is lazy programming--you're not really thinking about when your object variables are in use and you're constantly checking if they're nil before you use them because you didn't take the time to limit their scope or carefully construct the flow of your application in such a way you know whey they're created and freed."</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=375&amp;1=default&amp;2=en&amp;3=" token="82K5I9SklDNgr9f7-0rY1jGARQXUkctkif5pDfV2-u4"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> <div class="indented"> <article data-comment-user-id="1" id="comment-376" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/1"> </article> <mark class="hidden" data-comment-timestamp="1656444538"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/376#comment-376" class="permalink" rel="bookmark" hreflang="en">Unrelated</a></h3> <div class="single-comment-meta"> <span><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span> Tue, 06/28/2022 - 12:00</span> <p class="visually-hidden">In reply to <a href="/comment/375#comment-375" class="permalink" rel="bookmark" hreflang="en">Correct, in that particular…</a> by <span lang="" typeof="schema:Person" property="schema:name" datatype="">Darian Miller (not verified)</span></p> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>I don't see these two issues as related. Calling <code>Destroy</code> is explicitly forbidden in the documentation and it's been that way ever since Delphi 1 (I just checked in my copy of the first edition of <a href="https://www.goodreads.com/en/book/show/1365487.Delphi_Developer_s_Guide" rel="nofollow">Delphi Developer's Guide</a>). But <code>FreeAndNil</code> is a convenience that wasn't introduced until Delphi 5.</p> <p>Since calling <code>Free</code> does a check before calling <code>Destroy</code> allowing you to call <code>Free</code> multiple times without error it can therefore be considered in the same discussion as whether or not to use <code>FreeAndNil</code> which is frequently the basis for a similar check in the user's code but there's still a big difference between a core tenet of the language and an optional procedure.</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=376&amp;1=default&amp;2=en&amp;3=" token="NoFwqzt8GgBdOw79j-rsMLrIhWetAJKF5ZcB08WGrC0"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> </div></div></div> <div class="comment-form-wrap"> <h2 class="add-comment-title"><i class="icon-add_comment theme-color"></i> Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=161&amp;2=comment_node_blog&amp;3=comment_node_blog" token="ott8yG_QoeR9qzugKwwV7YdgdLATtctKjrL0KPSxoys"></drupal-render-placeholder> </div> <!--/.comment-form --> </section> Fri, 24 Jun 2022 03:25:29 +0000 david 161 at https://corneliusconcepts.tech