Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chris Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Saving state of open files at a point in code 2

Status
Not open for further replies.

Jerim65

Technical User
Aug 8, 2010
99
AU
My applications deal with a 3rd party app's tables.

To avoid any problems I CLOSE ALL when I wish to run that application from within my application.

I have that working nicely BUT I would like the user to be able to continue in my application from where they were when they opened the 3rd party app.

I am thinking of something similar to the hibernate command in Windows where the RAM state is saved to disk.

If this is not possible the user will have to be sent back to step 1 and restart my application which is what I have at present.

I vaguely remember something similar in Clipper many years ago.

Any comments please.

Coldan
 
I have solved a similar situation in regard of open aliases. I save info on open cursors and their recordpointer position to restore this and to close all tables hat were opened by a third party class while creating a result cursor.

There actually is a single command doing that: CREATE VIEW (not to be confused with CREATE SQL VIEW), but while it seems very accurate in storing the actual session, it does not save record pointer positions and this is not working for me. You have to store more actually, all settings, which are global to all datasessions for example SET CLASSLIB and SET PROCEDURE.

I wonder why you would need to reset/ restart your app, though, you can RUN a second EXE and keep your own application running as is.

It's much easier to run a secondary process via RUN than to run a secondary app within the process of your app. The need to store your settings like an image of the RAM state is lowered a little, if you call the second app in a new datasession, but you still need to save all global settings and restore them. The _set class of the ffc/environ.vcx lib can help there to save and restore settings, or you use SET() function and the corresponding SET command to do that yourself.

The modern way of doing this is by defining a service bus, SOA, which is centralizing functionality to a service, like data is centralized in a database. But it's a long way to change seperate apps to such a decoupled architecture.

Bye, Olaf.
 
Coldan,

If the third-party app is a separate executable, and if you run it via RUN or ShellExecute() (as opposed to DO), then there is no need to do a CLOSE ALL. The two apps will be quite separate, and opening or closing a file in one of them won't affect the other (unless one or both the apps opens a table exclusively).

Also, I'd suggest that before opening any table, in any app, you always check to see if it is already open. In other words, instead of doing this:

USE MyTable

you always do this:

IF NOT USED("MyTable")
USE MyTable IN 0
ENDIF
SELECT MyTable

I know this might seem long-winded, but it avoids several problems, especially where tables are opened behind the scenes (for example, in a SQL SELECT). It's a good habit to acquire.

If you did this, it would remove the problems to which you refer.

Hope this helps.

Mike


__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro tips, advice, training, consultancy
Custom software for your business
 
Thanks to Mike and Olaf,

You both know I am a 'learner' and a bit long in the tooth so I have built my apps the best way I can with a lot of help from this forum.

Olaf has 'swamped' me with his technical explanation but I am trying to understand all aspects of his answer.

Mike - as I have been told to do previously I always use the

IF NOT USED("MyTable")

construct in all my apps.

As I am dealing with tables in a 3rd party app I like to be sure nothing I do will 'chamge' them unless I set out to do that in my code.

I do use ShellExecute() as on your page on your site.

<The two apps will be quite separate, and opening or closing a file in one of them won't affect the other (unless one or both the apps opens a table exclusively)>

I 'think' the 3rd party app opens tables exclusively, as running that app whilst my app is open and using its tables causes an error message to be generated from it.

Thus I was looking for an 'ultra clean' solution when I open it.

FYI - I open the 3rd party app to show the result of processing functions in my Utility app.

Then it's 'nicer' for my user to be able to continue with functions in my app .

Regards

Coldan
 
Coldan,

You say that you "think" the 3rd-party opens the tables exclusively. Obviously, it's important to know one way or the other, as that will affect your strategy.

What is the error message you see that made you think the app opens the tables exclusively?

Also, if it does open the tables exclusively, is that essential? If its main job is to report on the results of processing, it's likely that it doesn't need exclusive use. Can you alter the 3rd-party app so that it opens the tables non-exclusively?

If the app definitely needs exclusive use, your best bet would be to do CLOSE ALL DATA and CLOSE ALL TABLES immediately before calling the app. In your own app, always open the tables explicitly whenever you need them, using the IF NOT USED... construct.

I asppreciate the difficulties you have as a learner, especially in something as complicated as VFP. I hope at least that the above will put you in the right direction.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro tips, advice, training, consultancy
Custom software for your business
 
From your original question I assumed like Mike, that you eg DO main IN 3rdparty.EXE to start the other EXE, which would yield problems in that this EXE then is not starting as a seperate process and fiddling around with your settings and mixing with your datasessions.

Like Mike I don't assume any app will work exclusive on tables, unless it's meant for single users. Then exclusive access can make sense in accellerating data access a bit, but you already have a good performance, when data is on a local drive, which mostly would be the case in single user applications.

Maybe you're opening dbfs exclusive and not the 3rd party app. Even if you do SET EXCLUSIVE OFF, this is not a general setting, it's per datasession and you need to set it in a forms data environments BeforeOpenTables method for tables of the data environment to open shared. Otherwise you could be the one holding files open exclusive.

Bye, Olaf.
 
Hi Coldan,

maybe this would be of a help:
Code:
*****************************************************************************************
Function ATables(taDBFs)
*****************************************************************************************
** Function Name: A[rray of open] Tables
** Purpose      : List all the table files currently open.
** Description  : Essentially - a wrapper/extension for VFP's built-in AUSED() function,
**                which gives only alias name and work area #, whereas we might need some
**                more info as well (i.e. full path, open mode, index, etc.)
**                After initial parameter validation, runs AUSED() function to determine
**                the number of the table files currently open. If finds none - exits.
**                If found - re-dimensions the given array-parameter with the following
**                columns/structure:
**                1 - Full path to the open table file	- as String;
**                2 - Alias name                       - as String;
**                3 - Work area number	                - as Integer;
**                4 - Is open in Exclusive mode        - as Boolean;
**                5 - Is Updatable                     - as Boolean;
**                6 - Index tag                        - as String
**                7 - Record pointer's record #     - as Integer.
** Parameters   : Pointer on array (by ref.) - mandatory
** Returns      : The number of open table files found if success (includes 0 if none),
**                negative integer if errs.
** Side Effects : The array-parameter is re-dimensioned to accommodate the info on the
**                found open table files.
** Notes        : 1. Verbose on error, silent otherwise.
**                2. Can be used for programs written in VFP7 or newer versions.
** Author       : Ilya I. Rabyy
** Revisions    : 2010-10-12    - 1st draft.
**                2010-11-29    - by Ilya: added array element to store the current
**                record pointer's position.
*****************************************************************************************
LPARAMETERS taDBFs
** Parameter's verifcation
IF VARTYPE(taDBFs) = "U"
   = MESSAGEBOX([Invalid parameter passed: taDBFs s./b. a pointer on array.], 16, ;
PROGRAM() + [ - error: invalid parameter-array])
	RETURN -1
ENDIF ()

LOCAL I, lnRet, lnEmpty
LOCAL ARRAY laAreas[1]

lnRet = AUSED(laAreas, .NULL.)	&& Number of open tables found

IF lnRet == 0  && No open table files found - we're done here!
   RETURN lnRet
ENDIF ()

** Some open tables have been found - re-dimension and fill out the array-parameter
***********************************************************************************
** Next line was modified by Ilya on 2010-11-29, added array element to store
**  the current record pointer's position
DIMENSION taDBFs[lnRet, 7] &&  taDBFs[lnRet, 6]
** End of modification by Ilya on 2010-11-29, added array element to store the
** current record pointer's position **
***********************************************************************************
FOR I = 1 TO lnRet
   taDBFs[I, 1] = DBF(laAreas[I, 2]) && Full path to the DBF by work area # in laAreas
   taDBFs[I, 2] = laAreas[I, 1]	&& Alias name in laAreas
   taDBFs[I, 3] = laAreas[I, 2]	&& Work area # in laAreas
   taDBFs[I, 4] = ISEXCLUSIVE(laAreas[I, 2], 1) && Obviously...
   taDBFs[I, 5] = !ISREADONLY(laAreas[I, 2]) && Obviously...
   taDBFs[I, 6] = ORDER(laAreas[I, 2]) && Tag name of the index set, if exists
**                                        and set, empty string if ain't
   taDBFs[I, 7] = MIN(RECNO(laAreas[I, 1], ;
                      RECCOUNT(laAreas[I, 1])) && Record pointer position
NEXT I

RETURN lnRet
Using this little function, you can save the state of your open tables in an array (you can even make it an added property-array of your _SCREEN system mem. var.) before CLOSE DATABASES ALL command, and restore them after that your other EXE exits.
(BTW, how do you know that other EXE is done working? Do you use something like ExecAndWait type of function to run this 3rd-party EXE off your own application?)

HTH.


Regards,

Ilya
 
I'm sorry, colleagues, I posted the line with a typo (missed closing ")" after RECNO()). The correct line is following:
Code:
taDBFs[I, 7] = MIN(RECNO(laAreas[I, 1]), ;
                         RECCOUNT(laAreas[I, 1])) && Record pointer

Mea culpa! :-(

Regards,

Ilya
 
Ilya,

Your function is very interesting and as a matter of my interest I have placed it in my app code.

You show a parameter - how would I use this in my code?

I have already got a procedure

<code>
N = Aused(laOpenFiles)
Asort(laOpenFiles,2)
For I= 1 To N
Do Case
Case '_A'$laOpenFiles(I,1)
Case '_B'$laOpenFiles(I,1)
Case '_C'$laOpenFiles(I,1)
Case '_E'$laOpenFiles(I,1)
Case '_G'$laOpenFiles(I,1)
Case '_I'$laOpenFiles(I,1)
Case '_L'$laOpenFiles(I,1)
Case '_M'$laOpenFiles(I,1)
Case '_N'$laOpenFiles(I,1)
Case '_ND'$laOpenFiles(I,1)
Case '_NPT'$laOpenFiles(I,1)
Case '_NPV'$laOpenFiles(I,1)
Case '_O'$laOpenFiles(I,1)
Case '_P'$laOpenFiles(I,1)
Case '_PD'$laOpenFiles(I,1)
Case '_PPV'$laOpenFiles(I,1)
Case '_R'$laOpenFiles(I,1)
Case '_S'$laOpenFiles(I,1)
Case '_T'$laOpenFiles(I,1)
Case '_X'$laOpenFiles(I,1)
&& "3rd party Original Tables Ignored "

Otherwise
cMessageText = "File Open in Area "+Allt(Str(laOpenFiles(I,2)))+" is "+laOpenFiles(I,1)
Select laOpenFiles(I,2)
logging(cMessageText + ' ' +Str(Reccount(),6,0))
Endcase
Endfor

</code>

Regards

Coldan

 
Coldan,

I am pleased that my little function proved to be of a help to you, colleague!

Now, the way to cal this function - "It is quite simple, actually, double-O-7!" (Q, in a number of James Bond movies):
Code:
LOCAL ARRAY laOpenFiles[1] && It's better to declare this array
** I always told my students 1) to avoid one-letter memvars and make them meaningful,
** and 2) always declare them, preferably - as local ones. Therefore:
LOCAL lnOpenDBFs

** Now, the point you have missed - or so it seems to me:
** The array-parameter should be passed by reference:
lnOpenDBFs = AUsed(@laOpenFiles) ** Note the "At" sign
The rest is for you to program.
HTH.


Regards,

Ilya
 
Ilya,

Thank you.

To run my routine I use

do openings

adding your code to test it

<code>
PROCEDURE openings
LOCAL lnOpenDBFs

** The array-parameter should be passed by reference:
lnOpenDBFs = AUsed(@laOpenFiles)

Function ATables(taDBFs)
*****************************************************************************************
** Function Name: A[rray of open] Tables

</code>

I think I am still confused.

Coldan
 
You forgot to predefine laOpenFiles as an array in your openings procedure before you can pass it in into Ilya's function.

You can't pass in a thing that does not exist.

Actually I think you don't need all this. You should rather first test in the moment directly before you ShellExecute the other application, if your application is the one hindering further access to dbfs. You could inspect that by SET("EXCLUSIVE") and what's even better reflecting the status of some DBF test if ISEXCLUSIVE("somealias") returns .T. for one of the DBFs the other app can't work on.

Bye, Olaf.
 
Thanks Olaf,

I have added the ISEXCLUSIVE("somealias") returns .T. to my openings report and am investigation results.

Coldan
 
Actually, Olaf, my function populates the array's 4th column with the Is-Exclusive flag. There's no need to "test if ISEXCLUSIVE("somealias") returns .T. for one of the DBFs the other app can't work on": this way colleague Coldan may very well end up checking with ISEXCLUSIVE() the better part of his tables! ;-)

OTOH, checkinig the SET("EXCLUSIVE") for the entire program/session does have some merits. (Yours truly, however, never does it because he has EXCLUSIVE=OFF in the Config.FPW.)

Coldan,

the example of calling a subroutine (function or procedure) with a parameter having to be passed by reference can be as following:
Code:
LOCAL lSomeMemVar
= SomeSubroutine(@lSomeMemVar)
WAIT WINDOW NOWAIT lSomeMemVar
RETURN
PROCEDURE SomeSubroutine(tParam)
tParam = "Passed by reference parameter's been modified"
RETURN
Basically, you passing not the value of your memory vriable to that subroutine, but the address in the operating memory where your variable is to be stored. The called subroutine, then modifies the contents of that memory variable because it "knows" its address. It is somewhat similar to when you (subroutine) receive a form (say, application for a credit card) in a mail, with the postage-prepaid envelop and the return address already printed. You need just to fill out the boxes in this application, put it into envelop and give it back to postman. You do not need to write that return address yourself. (This is very crude similarity, I admit, but that what's come to my mind because of the word "address".)
HTH.


Regards,

Ilya
 
Ilya, you misunerstand me.

This was not to add to your function. This was just to see in a kind of debug run, if his application is hindering the secondara app to open data shared, as that turns out true, now he just needs to do as I say and SET EXCLUSIVE OFF in the BEFOREOPENTABLES method of each forms data environment and will himself not need to close the tables before starting the second exe. Therefor he won't need to store info about the state of all the tables and reopen them. He'll simply call the other exe via ShellExecute and do nothing more.

Coldan said he "thinks" the other exe is opnening tables exclusive, while indeed it turns out his app is doing so and that would most probably be all that needs to be fixed.

SET EXCLUSIVE is a per datasession setting and turning that OFF at the start of an application is not enough, even turning it off in the load of a form with private datasession is too late, it needs to be turend off in the dataenvironment before tables are opened there.

I already said so on 29 Nov 10 9:45, before you even suggested the ATABLESUSED() function and I am following this route of a solution, as it's much less work, much simpler than to store info about the current datasession state and restore it.

Bye, Olaf.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top