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!

Can i put code in Unload or Destroy? 4

Status
Not open for further replies.

Mandy_crw

Programmer
Jul 23, 2020
581
PH
Hi everyone... I have a code written in destroy as follows..

SELECT Log
SET ORDER TO IDNUM && IDNUM

SEEK(ALLTRIM(idnam))

IF FOUND()
IF FLOCK()
replace CntLog.log WITH .F.
ELSE
MESSAGEBOX("table Not locked",0+64,"Lock")
ENDIF
ENDIF

But everytime i close my form... it says "table Not locked", how come i cannot lock the table so that i cant replace CntLog.log with .F.? Thanks...
 
Hi,

why do you want to flock() , thats the whole file ?
It seems that you only want to update a record, so rlock() would be enough

Regard
tom
 
Mandy,

There is no reason to lock the file or the record here. A lock will be applied automatically by the REPLACE command, and released automatically after the command finishes.

Also, your question - whether this should be done in the Unload or Destroy - is irrelevant. In this case, your code would work exactly the same in each of those places. That said, it is generally preferable to use Destroy rather than Unload because by the time you reach the Unload all the controls in the form will no longer exist. That's not important in this case, but it is something to keep in mind.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
You got fine answers, but nobody asked what is your SEEK doing?

What's idnam? Is it a field of Log.dbf?
What are you doing there? If idnam is no variable but a field it will be preferred, even if a variable of same name exists. So you'd seek to the record you're already on.
Do you want to seek to idnam value of another table? Then you have to specify that, i.e. SEEK othertable.idnam.

And if you want to use the SEEK function (there is a seek command and a seek function, both exist and you can choose) then also take the SEEK() function return value instead of using FOUND. The SEEK function returns .T. if it found a record and .F. if not, so you could do
Code:
IF SEEK(value)
   REPLACE log with .f.
Endif

And then, you Select Log but want to REPLACE in Cnt.log, you don't select a field, you select a table/alisa/workarea, so it should be SELECT Cnt.
How can you do so many things wrong in so few lines?

Also, why do make 10 lines out of a single line of code?

Code:
Update Cnt Set Log = .f. Where idnam = something

That something has to come from somewhere else, I don't know from your code, but it surely isn't a field within Cnt itself. Because a clause like idnam=idnam is always true, no matter on which record you are, except when idnam would be NULL.

So, don't panic, focus, keep calm and really think about what you want to do and do that, don't guess what could be right, know what you want. We can't tell you what you want.. though.

Chriss
 
Thank you tom and Mike....
Yes Chriss... idname is a filed in log.dbf.. Oh I see... the update i think would be more that enough... you always give me an enlighten ment Chriss... another thing i wanted to know more is that why is it it can't be locked when i have put and open the table in shared? I got your point tom... im just wondering why... Thanks and God bless...
 
You can FLOCK a table even after you opened it shared. But opening it shared means until you lock it it's not, so others can open it to, if flock fails, that's the case, others also have the table open shared.

FLOCK does - as Tom said - lock the whole table, that's only possible if it is not in use by anyone else. And if your application is used by multiple users that's often the case, isn't it? Especially when it's a frequently used form or table. So in a multi user environment you will almost always fail on attempting to get exclusive file access to files that are used by two or more users.

As Mike saqid REPLACE and also UPDATE-SQL care for the write locks they need to write, and they are only very short term locks. An update will report the numnber of recordss it updated by _tally, so if _tally is 0 you can deduce that the update either didn't find the id value in the table, so there is no record to update, or it couldn't get the lock, though it would give it multiple tries, usually and if it fails would rather error, I think, especially if some other user actually managed to get an FLOCK.

Manually locking has it's purposes, but do you really need to lock in this case? Why? To ensure the replace works? Also REPLACE will set _tally, by the way. Learn about _TALLY as a way to verify and quantiufy your SQL success, if it's about that.

If you want to implement a manual locking mechanism that should actually restrict all other users but the one that has a lock, then you would rather lock when beginning a modifcation, not in form destroy or unload, rather at init, so I don't really assume it's about that, is it?

It would help to know what you want, as I already hinted at in my previous answer, your code does not tell the intention of it. I indirectly asked why you address cntlog if the table you have is log.dbf. Do you USE log.dbf alias cntlog or is cntlog a cursor from a SELECT SQL? cnt is usually the prefix of containers, not of cursors, so your code and naming is puzzling. You have log.dbf and that has a log field? log.log? Really? Be more verbose in your naming, you're not only confusing me - I assume - you even confuse yourself. You surely don't know what you coded a month ago. I don't even know what I had for lunch a week ago, do you? Be kind to yourself and use better speaking names. Not that a log.dbf is too short, but logs are logging many many different things, and a log table should perhaps have a message field, but a log field ? You log messages, not logs, do you? OR do you mean locks? You see how confusing that is?

You're not bound by rules and functioning vs non functio0ning code to use recommended prefixes. I'd be fine if you doin't give cursors a prefix, or would call a grid cursor listing custoers grdCusomter, just like the grid, even when you name the grid the same, the code that addresses the grid object has no contextual overlap with code adressing the workarea, usually. But if you named cntLog as it has to do with a container on your form you puzzle me while already being puzzled, which container has a control or recordsource? I doubt you mean container with cnt, maybe you shortened count, I don't know. But you see, how puzzling too short names are? Especially as you do what you always do, say too little about what this is about.

Chriss
 
Hi Chriss... sorry for im really having a hard time explaining what i wanted... but let me try my best... I have my main form that calls for the second form... which i wanted to record the idnumber in the cantlog, so that if the same number is used to log in another computer, it will tell that that idnum is already logged, evertime that idnumber is logged, it will see that the log is .T., another is that if i close the second form, it will look for the idnum that i have recorded earlier, and make the log .F.. but i cant edit the cantlog table which has an alias as cnt for it alwasy say, "Record not locked". This is my main concern why cant i update or replaced? I hope i have said it well Chris...
thanks

Yes Chriss the next time i will have it named i will not do it short... Thank you....
 
I don't get what you want to do, but as far as I get your concern:

If you're not able to FLOCK a DBF that does not say that you can't write to it. Not at all, you misinterpret that.
and, well, your code then starts that messagebox, you don't even try to update a table, then.
You're having a utterly wrong idea of what you need to be able to lock to be allowed to change a record.

Why are you even on the fixed idea of needing to do an FLOCK before a REPLACE. You do a lot of REPLACES in your code, don't you? Did you ever or even just sometimes use FLOCK beforehand?
You can easily see for youreself you're on a wrong fixed idea here.

Chriss
 
Mandy said:
I have my main form that calls for the second form... which i wanted to record the idnumber in the cantlog, so that if the same number is used to log in another computer, it will tell that that idnum is already logged

1. Are you confusing logging with locking? Those are two completely different things.
2. Your initially posted code contains a line REPLACE CntLog.log, which is wrong from the outset: REPLACE always acts on the currently selected workarea, if you specify a field of a table - here the CntLog table - then this won't work if CntLog is not currently selected. Your code just a few lines above selects Log.dbf (or a Log cursor), not CntLog. Now you talk of a cantlog table. What is it? Be precise.

Also, you've been teached on how REPLACE works exactly back in thread184-1812991
Look at the advice of Tamar Granor:
Tamar Granor said:
2) NEVER allow a single REPLACE command to replace fields in more than one table. If you take this advice, there is never a reason to put the alias on the "left-hand side" (that is, field named before WITH). It's my view that writing REPLACE alias.field WITH something is confusing and should thus be avoided.
Your REPLACE is against this advice, as you have selected LOG but try to replace CntLog.log, a field of another alias/table.

3. You're now talking of the start of a form you call from main form. Well, the situation we now talk about is destroying or unloading the form, which is clearly happening at the opposite end - the closing of the form. I get you want to act on the same record as you acted on at the form start? Is it that? What' so hard to tell this? If so, where do you store the information about which record your form dealt with, do you even store that information anywhere? Load and Unload , Init and Destroy - both pairs are at the opposite ends, they don't know of each other, though. Variables available at init are not anymore at Destroy. You have to store that information somehwere, an id or recno. In some form property or - if you have no other idea - in a public variable. But your destroy code doesn't get the idnum from there. The other choice is you navigate to some record and stay there, then you rely on having stayed on the same record all time during the form life and still when destroy and unload happen. But then there's no need at all to LOCATE or SEEK to some record.


Chriss
 
hi Chriss... Thank you again for enelightenment... i already found the culprit... im sorry i panic... my simple code wasnt working in such a way i intended it to work... so i ask here in the forum... i forgot to issue the unlock... so it keeps showing the error message "Record not locked." I was thinking that its not right to put code in the destroy and unload hence the error... So I run to you and everybody in this forum to ask... Thanks God you all have great ideas.... Thank you.... God bless....
 
Mandy_crw said:
i forgot to issue the unlock

I don't know at all what you now talk about, FLOCK is a locking function, not unlocking. And the message you saw came from your own code:
MESSAGEBOX("table Not locked",0+64,"Lock")

If you don't even see that this message comes from your own code,you need more than just tecnical help.

If you did an FLOCK in form init (we don't know, you don't post anything) then the help on FLOCK tells you:
Mandy said:
The table can be unlocked by issuing UNLOCK, closing the table, or quitting Visual FoxPro.
The usual way is the second, USE the table you're done with.

But it's the last step. After your final REPLACE, not before it, because:
help said:
When a table is locked, the table is available for both read and write access by the user who placed the lock.
So as long as you have the lock there's no problem at all to make any changes. You don't need a lock at all, reasons for locking a table can be manifold, I don't know your reasons. I'm fine as you seem to have solved your issue already, but what you're coding there is quite insane. It's pointing out you have no idea what you're doing.

An FLOCK is not something you should do at all. If you want exclusive access to one record for the current user, you do an RLOCK, not an FLOCK. I'm pretty sure you don't want to exclude all other users from editing other records, so you do the wrong lock types. You don't need locks at all, to be honest. A good multi user application will use all data shared, also allow parallel working on data and avoid conflicts with buffering data, in rare cases using record locks, but never table locks (FLOCK means file lock, so locking the whole table, meaning readonly access for all other users except the one that has the lock).

Chriss
 
Oh I see... Thanks Chriss... Now I know...
Chriss said:
I don't know at all what you now talk about, FLOCK is a locking function, not unlocking. And the message you saw came from your own code
I have put this code to test Chriss... because im suspecting its not writing the table... because everytime i check the table no changes is being made... My application is a multi user app Chriss ,thats why in my main form before calling the second form, ive locked the file, hence on the second form when i write to the table, it says "record not locked". So when i issued unlock before calling the second form solved the problem... Sorry Chriss I am having a hard time telling you in such a way that you can better understand me... Thank you always Chriss....
 
You might (most likely) not need to use the FLOCK command; but rather, the RLOCK command. As others have said, the FLOCK command locks the entire table and no one else can then access it and this would cause problems when they try to open a form when the table to be used is LOCKED. I think you only need to lock a specific record (hence RLOCK) to keep others from being able to access it for changes (they can access in read-only). Just remember to UNLOCK the record after use.
 
Mandy, I think at some point you came across what Mike said, a fail of VFP to do an automatically lock when you try to do a REPLACE. In part maybe also, when you do a totally wrong REPLACE as your code had - a replace in another workarea than is currently selected. You can program that, it's not in itself causing an error but it's still wrong, totally wrong if you don't even have a RELATION to antoher table that could justify it. If you want to replace fieldX in tableY then you SELECT tableY and REPLACE fieldY, not REPLACE tableY.fieldX and not at all REPLACE tableZ.fieldx.

Greg is right and I also recommended to RLOCK, not FLOCK. But I think you came across an error message about not being able to lock a record without doing locking yourself. That message is about automatic locks done by anything that writes into a DBF and is what Mike Lewis also mentioned happens. To enable that to have more success you don't need to switch to manual locking to already have the lock, instead you can SET REPROCESS to something giving automatic locking more time or chances.

Read the help on SET REPROCESS. It also contains the messages that VFP pops up in a WAIT WINDOW if automatic locks go wrong and especially, if you have no error handling. If you saw messsages like "attempting to lock" or "waiting for lock" and you think that suggests to program that FLOCK, then you're wrong. The logic is sound,, of course, that when you do locking by code yourself, automatic locks done by any write operation succeed as you already have a manual lock. But in my experience manual locks are buggy also depending on networks to work properly. Automatic locks failing point out that they don't. Locks, for example, may not propagate to all users and cause more and other problems. So just try to mitigate the problem by SET REPROCESS, not by starting to lock manually.

Manual locking with either FLOCK or RLOCK should be done only, if you really intend to lock tables or records for a user that will have exclusive write acccess for his edits and then requires any form that also could edit the same table or record to tell users to wait for a record to be unlocked, so you don't just need to add locking to one form, you also need to add lock checks and reacting properly to locks in all other places in your application. There are, of course, also automatic responses to RLOCKs, if you program nothing. But the automatic reactions to locks are errors and fails you will want to prevent. So you have to start veryfing whether a record is locked or not with ISRLOCKED. Problem only is, even if ISRLOCKD telly you .F. - the record isn't locked - the next moment before you write another user could already have done an RLOCK. And when you have bad luck with automatic locking, that is likely to continue even with manual locks. It could actually be a problematic network configuraton, a network package routing leading to packages lost by TTL (time to live) in network loops, which can only be solved with network configuration, not with any application code. Anyway, the first thing to try is to allow more reprocessing, not start locking just for sake of not getting such "attempt to lcok" / "waiting for lock" messages.
 
Mandy--

More on the use of RLOCK... Chriss's points are valid about having to check for locking by other users. I only use the RLOCK command to lock something for an extended period of time and not for a field update only (automatic locking takes care of this for me with the increased setting for REPROCESS. For instance, I use RLOCK in my accounting application to lock an invoice that is going to be edited. I use RLOCK to lock the header record of the invoice at the start of the edit process in the Init() event of the form. If the RLOCK is not successfully applied, then the form will inform the user that the invoice is being edited by another user and then close. In the Destroy() event of the form where the user is editing the invoice I then unlock the invoice record.
 
I totally agree to that procedure, Greg. Also the fact that you do RLOCKs for extended periods of modifying data like an invoice or order or a students timetable or a POS systems receipt in one header record of a data structure that usually has multiple records in further tables.

You also then never need to check ISRLOCKED, as trying the RLOCK on the unique header record tells you not only whether you suceed at locking - if RLOCK fails you know another user has the lock and you can inform the user to come back later for editing the then updated invoice, timetable, receipt, whatever...

There's only one unfortunate thing I had to deal with such a system I implemented, too: Users that go off (eating lunch, being in a conference) without first unlocking what they edit. Which is why the customer that wanted this rolled back from it and I rather worked allowing parallel editing with conflict checks. Which is difficult, too, indeed and also has it's problems.

But Mandy, I point out SET REPROCESS to point out that the messages you get without reprocessing set up right don't point out that you missed to do locking, locking is done automatically just when writing, too, and the help chapter that explains that is in the help ,too, Mandy: Locking data. It is not only about automatic locking, but it contrasts automatic vs manual locking and lists all VFP language (command, functions, SQL statements) which lock tables automatically and how. It also points to SET REPROCESS in the manual locking section of the topic, but only the SET REPROCESS topic would ring a bell about the messages you might have seen and which made you use locking. They don't intend to suggest that, you can easily program without locking at all, too. Especially when buffering.

And last not least, buffering and locking are closely related, too, when it comes to options you can choose for CURSORSETPROP of Buffering, because that does not purely set how data is buffered, the pessimistic buffering also includes locking, that could be describes as automatically done manual locks. i.e. those locks are not just shortly done when writing, require you to unlock them but they would put locks automatically at the moment a user starts editing a record. It sounds greater than manual locking as it's done just in time, but that also makes it less simple to check at the start of a form whether to let in a user on sme invoice, timetable, etc or allow parallel editing based on locks done just in time.

Overall I had much better experiences with buffering (optimistic, without locks) than with any manual locking mechanism including that meant by pessimistic buffering. You might have more luck in your companies network, but for a software to sell to many shps, schools etc. you don't usually have control, so take caution about using locking. As locks need to be seen on other clients, they can fail under bad conditions, buffers are always local and work reliable. It's harder to understand what a failed TABLEUPDATE measn, how to pinpoint what hinders saving changes in case of conflicts, etc. but the technical meachnism is more reliable. Not that is does the same job, but when you finally save buffered changes, you can either simply use the force mode, that means "last change wins" strategy and all in all it concentrates all changes to one save action, especially when done within a transaction you start when saving by cascades of TABLEUPDATES in the multiple tables of a data hierarchy so typical for anything like order and orderdetails, receipt and receiptitems, etc. etc. Which means you usually avoid having concurrency that way.
 
Last edited:
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top