Dynamic External Objects in dB2K
by Ken Mayer, dBASE QA

WITH dB2K release 1, among the various new functionalities added  is the ability to use what are called “Dynamic External Objects” (or “DEO” as an acronym). Sounds a bit strange, doesn't it?

It's actually a really spiffy thing, and is something people have been wanting for some time. This functionality provides the ability to update an application's executable, by simply sending a new object file (.PRO, .WFO, etc.) to your customer, and not have to send them a new executable!

In fact, if your application was written properly, you could even send new report (.REO) files to your customer, and allow them to be run from your application without having to compile the .EXE, providing your customers with custom designed reports, and more.

How does it work? It's pretty simple. This capability defaults to being “on”, if you don't want it to function, you have to turn it off, by setting in code, a new property of the application object, called allowDEOExeOverride. This property defaults to true, and can only be changed in code (in other words, while it can be set in the development environment by changing it in the inspector, it doesn't affect the .EXE (unless you allow your user to inspect the _app object in your .EXE), and it cannot be set in the application's .INI file).

The syntax for this would be:

  _app.allowDEOExeOverride := false

This turns the property “off”, and tells the runtime engine to ignore external objects. If you do not do this in your code, your application will automatically look for external objects, as described below.

Basically, the new runtime engine for dB2K looks in the current directory for a file, so if your code executes a program, report, etc., it checks first in the current directory to see if it exists there. If it doesn't, it then checks a list of up to ten directories that you define in your application .INI (see below), and finally, if it is not in any of those, it looks inside the .EXE.

The entries that go into your application's .INI file are:

  [ObjectPath]
  objpath0="C:\SomeFolder\Another Folder"
  ...
  objpath9="another path"

The [ObjectPath] entries can be viewed and modified by the DEO Setting Utility. The latter can be found at the end of this article (it was written by Jean-Pierre Martel).

Note that in release 1 of dB2K, the object path does not understand relative pathing, i.e., you cannot assume a folder directly under the application folder (e.g., "C:\MYAPP\SomeFolder" ) by calling it as a relative path ("SomeFolder" or ".\SomeFolder"). The development team at dBASE, Inc. is aware of the need for this functionality, and perhaps we'll see it in release 2.

This gets a bit more interesting, in that if this functionality is set to false, and you do not have a specific object compiled into your .EXE, such as a specific report ( MyCustomReport.reo, for example), and your code tries to call that object, if the object exists outside of the .EXE (and it falls into one of the paths as described above), it will be run. This is a bit confusing — it was for me when I was testing the functionality, and trying to ensure everything worked properly. Let's say you have MyApp.EXE, and you do not build MyCustomReport.reo into the .EXE itself (in other words, in the project explorer for this project, you checked “Exclude from Build” for MyCustomReport.rep). However, your code has the line:

  do MyCustomReport.rep

If you deploy the file “MyCustomReport.reo” with your .EXE, and either deploy it to the directory the .EXE is in, or a folder in the object path list (as described above) in the MyApp.INI file, it will be run!

Now, what does this mean for you, the developer? It means, as noted, that you can update your application without having to recompile it and send a new .EXE to the customer. This can be done with program files, custom control files, forms, custom forms, reports, custom reports, labels, datamodules, custom datamodules and .SQL files. In all cases, except for the .SQL file, you must compile the source code to its object (.PRO, .WFO, .CO, etc.), as this is what will be used. If you send (only) the source code (.PRG, .WFM, .CC, etc.), your application will NOT be able to find it and will not use the updated file.

If you wanted to have a folder for custom reports (e.g., ones specific to a customer), you could create a folder, and have your application check for it. It would need to look for *.REO files, and allow the customer to run reports from there. This should be a fairly simple task.

Remember the sequence that the runtime engine uses to look for object files:

   1) The current folder/directory
   2) The paths as specified in the application .INI file (as noted above)
   3) Inside the .EXE itself

This means that updated objects (e.g., if you need to fix a custom control, you give your customer a new .CO file…) are looked at BEFORE the one contained in your executable, and, if found with the correct name, that object is used.

Security

Concerned that a user could change this functionality in your application? Don't be. The only place that this can be changed is inside your .EXE, or through the inspector. However, even though this property can be changed in the inspector, does not mean that it is saved to the application's .INI file — it's not. The reason that this is not stored in the .INI is precisely because anyone can modify a .INI file if they want to (it's just a text file). If you allow your user to inspect the _app object in the .EXE, then they could conceivably change this property there, and it would affect the .EXE. I cannot see a reason to allow this in an application, but…

Where to Use This?

As noted elsewhere, you can set this property off for an application, and then turn it on and off as needed, but the big question is “Where do I set this to affect my whole application?” The best place to set this if you want it to affect your whole application is in the startup routines, or a setup program that sets other system-wide properties.

For those who would like to have their application use DEO for some objects, and not others, the simple solution is to turn it on and off as needed. For example, if you know that your user may want you to update a report for them, before running the report you could turn DEO override on, and after running it, you could turn it off (the following assumes that you have turned the DEO Override off for the application, and only want to turn it on for this report):

  _app.allowDEOExeOverride := true
  do myreport.rep
  _app.allowDEOExeOverride := false

Another example might be one where you have the ability turned on throughout your application, and you have a password entry routine. You may not want your user(s) to be able to override that one routine: turn it off for that routine, execute it, and turn it back on…


Note: The author would like to thank Flip Young, my proof-reader, for the improvements she brought to this text.