Welcome to Foxite.COM Community Weblog Sign in | Join | Help



Am I being too persistent?

Persistent: lasting or enduring tenaciously, remaining beyond the period, retained....

Since discovering two Native VFP functions, i.e., STRTOFILE() and FILETOSTR(); it became among my favorites as can be seen on Live Backup Revealed source codes.  Those two have a lot of usages on my side most specifically about persistency, and so I want to put a spotlight on those now!

The ability to use external files to work with a project gives additional flexibility and control over an app.  Basic examples of this are the config.fpw and the .ini files.  Both are outside the exe and thus both can be configured anytime.  But aside from those two, can we still use an additional means for our configurations?  Definitely yes!

I will briefly explain here some useful tricks that I am doing on my side that is related to persistency.

Of Master and Slaves

When we develop an app for a multi-user environment, we consider that one will function as a master (server) and the rest slaves (workstations).  IMHO, in the developer's eye, the main difference between a server and a workstation lies with data location.  Simply saying, server's data needs to be on the same unit where the app is running while workstation's data belongs to another unit; and is connected via file mapping or straightly referenced thru UNC paths.  

Mapping vs UNC path

The easiest method of connecting workstation to a server data is via drive mapping.  When you map a drive, it will be treated by the local unit as among the local drives.  The connection is persistent (this is not the topic) meaning you can shutdown and restart your computer and every time that mapped drive will always be there.  Except if you disconnected it purposely. But mapping may pose several problems. Due to the fact that it is treated as some sort of local drive, it can be seen by the users via Explorer and thus pose a threat on data security. Users can accidentally or intentionally (in case of users with grudges to the company, believe me there are some) delete some files there.  Users, especially those with some knowledge on developing, can also be tempted to play as hackers even just out of curiosity. And by solely giving them the ability to see those files and thus the opportunity to play with it outside your app, you are already exposing it into those foreseeable problems.

Universal Naming Convention (UNC for short) specifies a common syntax to describe the location of a network resource, such as a shared file, directory, or printer .  One thing about using UNC paths is that it will always go out to the network, so it is a total no no to use UNC path to describe a location on your local drives.  If you are after a more secured way of connecting data then UNC is my best bet.  Your app can connect to your server via UNC path and your user is none the wiser.  They can not easily know where your data is located as it is now virtually hidden away from their prying eyes.  It is absolutely no longer part in any way of the local unit.


How can you know during developing time exactly what UNC path to assign for your app?  

The answer is there is no way unless you have done an ocular inspection of their units beforehand or details of those are given to you by somebody!  Working on a scenario that you have no way of knowing beforehand, then your data path must be flexible enough to allow changes, anytime.  

If you hardcoded the path, you are already doomed.  Let us say you hardcoded drive Z:\ to act as your path (a mapped drive) for a workstation; only to find out later that that drive is already assigned to other multi-tasking software on your client's side.  So you'll go back to your own place and unit, adjust all hardcoded paths into a new mapped drive letter, compile it, and go back to your client claiming you "fixed" the problem and now your app can be installed.

How Wonderful and Convenient!

How about doing it this way then?  Create a text file to hold the path and use that for your app:

MAIN.PRG
_screen.addproperty("oDataPath","")
If !file(ADDBS(JUSTPATH(SYS(16)))+"mypath.txt")
    STRTOFILE(Inputbox("Please specify the data location: ",;
         "Setting up of path",""
),"mypath.txt")
       && this may happen only on initial running if file is not yet there
endif
_Screen.oDataPath = FILETOSTR(ADDBS(JUSTPATH(SYS(16)))+"mypath.txt")
If !DIRECTORY(_screen.oDataPath)
    Messagebox("Sorry, but the path you specified do not exist!",0+64,"Invalid Path")
    RETURN(.f.)
else
    SET DEFAULT TO (_screen.oDataPath)
endif


*Just for this example, we will be using the almost obsolete simple InputBox() instead of GetDIR() as I wanted the scenario of manually keying-in UNC path.

And there you have it, a data path which is persistent, and can be modified easily.  The file holding the path, in this simple illustration should be on the same folder where your exe is.  Now if you want, you can mask that file with another extension so it won't be associated with text editors.  You can change the extension into .dll, .ocx, or a totally unheard of extension like .omg (oh My Goodness!), LOL.  As I have said, you can use a UNC path there like:

\\this_server\your_shared_app_folder\datafolder

Is there any other benefits of using a text file to hold data locations?  

Let us consider this scenario, your server broke down.   You have backed up or can extract the latest data.  But the problem is your server cannot be fixed within the shortest possible time.  With the ability to easily change data path, you can copy the entire folder of your app from your server into another unit, temporarily use that and just change now the data path inside that text file.

Previous location: \\this_server\your_shared_app_folder\datafolder
New Temporary location: \\backup_unit\your_shared_app_folder\datafolder

And the transition easily done will now enable your users to continue working.

But don't overlook one fact.  In both cases, mapped drive or UNC path, the users must still be given read/write permission on said folders otherwise any of those won't work.  The only advantage of UNC over mapping is that those files will be hidden away from prying eyes.

To complete the camouflage, it is better to run the exe locally.  You must create a folder for your app on each local units.  On updating of local exe matters, I will refer you to this link: Sweet Potato's application loader.

But you said you've found STRTOFILE() and FILETOSTR() very useful?  Is that all?

Of course not! I was sidestracked explaining things but another example of this is when you have a pdf converter.  Most free pdf converter is in a form of a pdf printer driver (CutePDF, PrimoPDF, etc).  Setting that up so your app can recognize it only needs to be done once like the data path and so following the same trick:

_screen.AddProperty("oPDFPrinter","")
If !file(ADDBS(JUSTPATH(SYS(16)))+"mypdfprinter.txt")
   STRTOFILE(Inputbox("Please type the name of your PDF printer: ","Specifying PDF Printer for conversion",""),"mypdfprinter.txt")
endif
_Screen.oPDFPrinter = Filetostr(ADDBS(JUSTPATH(SYS(16)))+"mypdfprinter.txt")


And now you have a _screen variable holding the PDF printer's name that can be called anywhere in your application.  Thus when converting report to a PDF, all you need to do now is:

Local laPrinters
APRINTERS(laPrinters)
IF ASCAN(laPrinters,_screen.oPDFPrinter)  > 0
   SET PRINTER TO NAME "&_screen.oPDFPrinter"     
   REPORT FORM < your report > TO PRINTER NOCONSOLE
   SET PRINTER TO DEFAULT
ELSE
   MESSAGEBOX("That PDF Converter is not found, please check!",0+64,"Convertion aborted!")
ENDIF


Gotcha!  What will happen if the PDF printer is not installed or you've misspelled the name and you don't have the IF above to validate the printer name?

Your app will generate an error, if you choose to ignore, your app will default to the default printer (Duh?!!!)

Why go to those effort of creating text files?  Again, can't that PDF printer be hardcoded?  

What effort?  Yes it can be, but what IF there is a new PDF driver that appears in the market in the near future which is more superior than the one you are currently using now, and that your client demands to use it instead?  With hardcoding, you need to go back to your source code and change all occurrences of that hardcoded pdf printer name, then recompile the project and update (jezzzz!) the exe again on your client side.  While with this trick, all you need to do is open up the file holding the printer name and change it as easy as 1.. 2.. oh! It is done!!!

More samples please?

Download Live Backup v2.1.3 source codes.  Persistency is used on retaining skins, list of folders to backup, list of file types to backup, snapshot folder location, and archive destination location.


Just random thoughts of an idle mind! 


Published Friday, May 01, 2009 10:12 PM by sandstorm36
Filed Under:

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# re: Am I being too persistent?

Monday, May 04, 2009 11:02 AM by wOOdy
Hi Jun,

Just wondering: You're creating _Screen Properties named like "oDataPath" and "oPdfPrinter", but then you store strings in it? Normally, the "o" denotes an Object, thus anyone accessing those properties would be completely irritated by that type-mismatch. What is the reason for you to not follow worldwide accepted naming conventions?

# o that one?  It is just a habit of me that although "o" denotes an object, I used it on my side on _screen and form properties.  And frankly, I don't know the naming convention for those two.  Is it "sc" for _screen character value and "fc" for form's property character value?  Doesn't seem right to me either.  Using lc is misleading too.

Anyway, you are right.   I guess now that I started blogging,  I better adhere to the norm and so I will read help again.  Thanks for the notice!  

# re: Am I being too persistent?

Monday, May 04, 2009 11:03 AM by wOOdy
This line:
>> SET PRINTER TO NAME "&_screen.oPDFPrinter" <<

would be better without any use of Macro-operator, instead using indirect Referencing:

SET PRINTER TO NAME (_screen.oPDFPrinter)

# Hello Woody!  Good thing you asked so I can explain to those likewise wondering!

Actually that is what I used before, but I read in Mike Gagnon's response to Pete Sass inside foxite
219318  that that is the preferred way.  According to them, direct referencing for SET PRINTER TO NAME command sometimes fail based on OS or VFP version.  It hasn't happened to me yet, but then if they say so I believe they already experienced it.  And if they said that that is the workaround for the problem of failing, then I have to follow; and might as well include that approach in this blog so others will see it too. 

And so that approach!
 

# re: Am I being too persistent?

Monday, May 04, 2009 7:33 PM by BennyThomas
Hi Jun, I use a similar tech.  But instead of text file, I store the data in a VFP table along with the application (EXE) file.  Is there any disadvantage in using table?

Benny

# Hello Benny.  Originally before I found STRTOFILE() and FILETOSTR(), I never bother using text file because I will be forced to use the F thing, err... commands (FWRITE(), FCLOSE(), FREAD(), F..... ); which is cumbersome, and so I simply use tables.

The main difference between using tables and text files though is the ability to easily modify content.  With tables, it is either you create a module inside your app or create another app just to modify contents of those tables.  Whereas with text files (masked or not), you can simply use notepad or you can go to the DOS environment and use edit to change things.  In short, a tool to modify contents of those is everpresent.  And changing content can be done very easily and fast.

Also, placing that table along with your exe will disallow any changes (if any) that needs to be done in the near future.  So basically, IMHO, it is similar to plain hardcoding.  The goal is to make it flexible, i.e., values can be changed anytime and outright.
 
Besides those?  I can't see any major difference between your approach and mine.

What do you think?

(required) 
required 
(required)